From 32e2e4de5921ecb7b13722955298e886ec2b2d59 Mon Sep 17 00:00:00 2001 From: Lorenzo Stramaccia Date: Wed, 11 Oct 2017 12:34:46 +0200 Subject: [PATCH 001/333] Fix meta title property --- lib/internal/Magento/Framework/View/Page/Config.php | 1 + .../Magento/Framework/View/Page/Config/Renderer.php | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Page/Config.php b/lib/internal/Magento/Framework/View/Page/Config.php index a5bbefb33008e..f12e2b56a60ac 100644 --- a/lib/internal/Magento/Framework/View/Page/Config.php +++ b/lib/internal/Magento/Framework/View/Page/Config.php @@ -117,6 +117,7 @@ class Config 'description' => null, 'keywords' => null, 'robots' => null, + 'title' => null, ]; /** diff --git a/lib/internal/Magento/Framework/View/Page/Config/Renderer.php b/lib/internal/Magento/Framework/View/Page/Config/Renderer.php index 9563cbfbcc532..a26e341f16dd7 100644 --- a/lib/internal/Magento/Framework/View/Page/Config/Renderer.php +++ b/lib/internal/Magento/Framework/View/Page/Config/Renderer.php @@ -136,6 +136,12 @@ public function renderMetadata() protected function processMetadataContent($name, $content) { $method = 'get' . $this->string->upperCaseWords($name, '_', ''); + if($name === 'title') { + if (!$content) { + $content = $this->escaper->escapeHtml($this->pageConfig->$method()->get()); + } + return $content; + } if (method_exists($this->pageConfig, $method)) { $content = $this->pageConfig->$method(); } From b11c7e78b24450ec4d3e73534c40ab427e786cdd Mon Sep 17 00:00:00 2001 From: Lorenzo Stramaccia Date: Thu, 12 Oct 2017 10:17:25 +0200 Subject: [PATCH 002/333] Update unit test --- .../Magento/Framework/View/Test/Unit/Page/ConfigTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php index 400e9cf3d1ed7..23d391c7f4f8d 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/ConfigTest.php @@ -13,7 +13,7 @@ use Magento\Framework\View\Page\Config; /** - * @covers Magento\Framework\View\Page\Config + * @covers \Magento\Framework\View\Page\Config * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -139,6 +139,7 @@ public function testMetadata() 'description' => null, 'keywords' => null, 'robots' => null, + 'title' => null, 'name' => 'test_value', 'html_encoded' => '<title><span class="test">Test</span></title>', ]; From 474e43cfb97617355d6d17ec7eb7639789319a7d Mon Sep 17 00:00:00 2001 From: Lorenzo Stramaccia Date: Thu, 12 Oct 2017 10:33:09 +0200 Subject: [PATCH 003/333] Fix coding standards --- lib/internal/Magento/Framework/View/Page/Config/Renderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Page/Config/Renderer.php b/lib/internal/Magento/Framework/View/Page/Config/Renderer.php index a26e341f16dd7..93c8c5c338627 100644 --- a/lib/internal/Magento/Framework/View/Page/Config/Renderer.php +++ b/lib/internal/Magento/Framework/View/Page/Config/Renderer.php @@ -136,7 +136,7 @@ public function renderMetadata() protected function processMetadataContent($name, $content) { $method = 'get' . $this->string->upperCaseWords($name, '_', ''); - if($name === 'title') { + if ($name === 'title') { if (!$content) { $content = $this->escaper->escapeHtml($this->pageConfig->$method()->get()); } From f5f3c761a42579a4b0b6b2cafc45c17e4e17248f Mon Sep 17 00:00:00 2001 From: Ankur Raiyani Date: Tue, 27 Feb 2018 12:44:20 +0530 Subject: [PATCH 004/333] Corrected Invoice Grid Column Subtotal value to copy from. Changed from sales_order to sales_invoice --- app/code/Magento/Sales/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 9de3f238d6a39..4e3d3a03af8f5 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -676,7 +676,7 @@ BillingAddressAggregator ShippingAddressAggregator sales_order.shipping_description - sales_order.base_subtotal + sales_invoice.base_subtotal sales_order.base_shipping_amount sales_invoice.base_grand_total sales_invoice.grand_total From f5e429d52724795c804498a87884e84619149ea5 Mon Sep 17 00:00:00 2001 From: Bartek Igielski Date: Tue, 27 Mar 2018 16:20:57 +0200 Subject: [PATCH 005/333] Fixed extends and removed unnecessary variables --- .../web/css/source/module/checkout/_tooltip.less | 3 +-- .../Magento/blank/Magento_Sales/web/css/source/_module.less | 2 +- .../frontend/Magento/blank/web/css/source/_extends.less | 2 +- .../blank/web/css/source/components/_modals_extend.less | 5 ++--- .../frontend/Magento/luma/web/css/source/_extends.less | 2 +- .../luma/web/css/source/components/_modals_extend.less | 5 ++--- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less index cf84f34279086..273f626ec03d6 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less @@ -8,7 +8,6 @@ // _____________________________________________ @checkout-tooltip__hover__z-index: @tooltip__z-index; -@checkout-tooltip-breakpoint__screen-m: @modal-popup-breakpoint-screen__m; @checkout-tooltip-icon-arrow__font-size: 10px; @checkout-tooltip-icon-arrow__left: -( @checkout-tooltip-content__padding + @checkout-tooltip-icon-arrow__font-size - @checkout-tooltip-content__border-width); @@ -138,7 +137,7 @@ } } -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @checkout-tooltip-breakpoint__screen-m) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .field-tooltip { .field-tooltip-content { &:extend(.abs-checkout-tooltip-content-position-top-mobile all); diff --git a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less index 1ea1e2c483d0b..3847393a2f046 100644 --- a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less @@ -262,7 +262,7 @@ } .toolbar { - &:extend(.abs-add-clearfix-desktop all); + &:extend(.abs-add-clearfix-mobile all); .pages { float: right; diff --git a/app/design/frontend/Magento/blank/web/css/source/_extends.less b/app/design/frontend/Magento/blank/web/css/source/_extends.less index d4bede1279602..a36934111eb06 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_extends.less +++ b/app/design/frontend/Magento/blank/web/css/source/_extends.less @@ -1233,7 +1233,7 @@ } } -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = (@screen__m + 1)) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .abs-checkout-tooltip-content-position-top-mobile { @abs-checkout-tooltip-content-position-top(); } diff --git a/app/design/frontend/Magento/blank/web/css/source/components/_modals_extend.less b/app/design/frontend/Magento/blank/web/css/source/components/_modals_extend.less index d324bbeac598f..d76630b5cea47 100644 --- a/app/design/frontend/Magento/blank/web/css/source/components/_modals_extend.less +++ b/app/design/frontend/Magento/blank/web/css/source/components/_modals_extend.less @@ -16,7 +16,6 @@ @modal-popup-title__font-size: 26px; @modal-popup-title-mobile__font-size: @font-size__base; -@modal-popup-breakpoint-screen__m: @screen__m; @modal-slide__first__indent-left: 44px; @modal-slide-mobile__background-color: @color-gray-light01; @@ -149,7 +148,7 @@ } } -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @modal-popup-breakpoint-screen__m) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .modal-popup { &.modal-slide { .modal-inner-wrap[class] { @@ -180,7 +179,7 @@ // Desktop // _____________________________________________ -.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @modal-popup-breakpoint-screen__m) { +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .modal-popup { &.modal-slide { .modal-footer { diff --git a/app/design/frontend/Magento/luma/web/css/source/_extends.less b/app/design/frontend/Magento/luma/web/css/source/_extends.less index 602ed44d5a44d..760ec9ed861a9 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_extends.less +++ b/app/design/frontend/Magento/luma/web/css/source/_extends.less @@ -1681,7 +1681,7 @@ } } -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = (@screen__m + 1)) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .abs-checkout-tooltip-content-position-top-mobile { @abs-checkout-tooltip-content-position-top(); } diff --git a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less index b3165a41964e5..e90d312aa7801 100644 --- a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less +++ b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less @@ -16,7 +16,6 @@ @modal-popup-title__font-size: 26px; @modal-popup-title-mobile__font-size: @font-size__base; -@modal-popup-breakpoint-screen__m: @screen__m; @modal-slide__first__indent-left: 44px; @modal-slide-mobile__background-color: @color-gray-light01; @@ -148,7 +147,7 @@ } } -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @modal-popup-breakpoint-screen__m) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .modal-popup { &.modal-slide { .modal-inner-wrap[class] { @@ -179,7 +178,7 @@ // Desktop // _____________________________________________ -.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @modal-popup-breakpoint-screen__m) { +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .modal-popup { &.modal-slide { .modal-footer { From 92cbd4f24f28626312101d61300afd5f45d703af Mon Sep 17 00:00:00 2001 From: Todd Christensen Date: Fri, 13 Apr 2018 17:55:29 -0700 Subject: [PATCH 006/333] Optimize ID to SKU lookup of tier prices. Previously, with a large number of tier or group prices, each tier would separately make a database query to lookup the associated SKU. This instead load the ID to SKU mapping once, and uses it for all tiers. When lookup up a batch of 100 SKUs with hundreds of tier prices per SKU, this cuts lookup time from on the order of 4s to on the order of 0.1s or less. --- .../Model/Product/Price/TierPriceStorage.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php index 3bb6bba69bfb4..1bddd2d07cd81 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -172,8 +172,10 @@ private function getExistingPrices(array $skus, $groupBySku = false) $rawPrices = $this->tierPricePersistence->get($ids); $prices = []; + $linkField = $this->tierPricePersistence->getEntityLinkField(); + $skuByIdLookup = $this->buildSkuByIdLookup($skus); foreach ($rawPrices as $rawPrice) { - $sku = $this->retrieveSkuById($rawPrice[$this->tierPricePersistence->getEntityLinkField()], $skus); + $sku = $skuByIdLookup[$rawPrice[$linkField]]; $price = $this->tierPriceFactory->create($rawPrice, $sku); if ($groupBySku) { $prices[$sku][] = $price; @@ -300,21 +302,21 @@ private function isCorrectPriceValue(array $existingPrice, array $price) } /** - * Retrieve SKU by product ID. + * Generate lookup to retrieve SKU by product ID. * - * @param int $id * @param array $skus - * @return string|null + * @return array */ - private function retrieveSkuById($id, $skus) + private function buildSkuByIdLookup($skus) { + $lookup = []; foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) { - if (isset($ids[$id])) { - return $sku; + foreach (array_keys($ids) as $id) { + $lookup[$id] = $sku; } } - return null; + return $lookup; } /** From fd6aa643d41a8b9eae1e24ba5aea87bb449e3ff4 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta Date: Sun, 22 Apr 2018 04:46:16 +0200 Subject: [PATCH 007/333] Enable WebAPI interface to handle parameters through constructor DTO setter methods are no more required, parameters can be handled through constructor for immutable state approach A setter fallback is supported --- .../Webapi/ServiceInputProcessor.php | 45 ++++++++++++++++++- .../SimpleConstructor.php | 43 ++++++++++++++++++ .../ServiceInputProcessor/TestService.php | 23 +++++++--- .../Test/Unit/ServiceInputProcessorTest.php | 21 ++++++++- 4 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index f1df2b0ee53f0..0c32396897c12 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -65,6 +65,11 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface */ private $serviceTypeToEntityTypeMap; + /** + * @var \Magento\Framework\ObjectManager\ConfigInterface + */ + private $config; + /** * Initialize dependencies. * @@ -73,6 +78,7 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface * @param AttributeValueFactory $attributeValueFactory * @param CustomAttributeTypeLocatorInterface $customAttributeTypeLocator * @param MethodsMap $methodsMap + * @param \Magento\Framework\ObjectManager\ConfigInterface $config * @param ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap */ public function __construct( @@ -81,6 +87,7 @@ public function __construct( AttributeValueFactory $attributeValueFactory, CustomAttributeTypeLocatorInterface $customAttributeTypeLocator, MethodsMap $methodsMap, + \Magento\Framework\ObjectManager\ConfigInterface $config, ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap = null ) { $this->typeProcessor = $typeProcessor; @@ -90,6 +97,7 @@ public function __construct( $this->methodsMap = $methodsMap; $this->serviceTypeToEntityTypeMap = $serviceTypeToEntityTypeMap ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ServiceTypeToEntityTypeMap::class); + $this->config = $config; } /** @@ -153,6 +161,33 @@ public function process($serviceClassName, $serviceMethodName, array $inputArray return $inputData; } + /** + * @param string $className + * @param array $data + * @return array + * @throws \ReflectionException + */ + private function getConstructorData(string $className, array $data): array + { + $preferenceClass = $this->config->getPreference($className); + $class = new ClassReflection($preferenceClass ?: $className); + + $constructor = $class->getConstructor(); + if ($constructor === null) { + return []; + } + + $res = []; + $parameters = $constructor->getParameters(); + foreach ($parameters as $parameter) { + if (isset($data[$parameter->getName()])) { + $res[$parameter->getName()] = $data[$parameter->getName()]; + } + } + + return $res; + } + /** * Creates a new instance of the given class and populates it with the array of data. The data can * be in different forms depending on the adapter being used, REST vs. SOAP. For REST, the data is @@ -173,9 +208,17 @@ protected function _createFromArray($className, $data) if (is_subclass_of($className, self::EXTENSION_ATTRIBUTES_TYPE)) { $className = substr($className, 0, -strlen('Interface')); } - $object = $this->objectManager->create($className); + // Primary method: assign to constructor parameters + $constructorArgs = $this->getConstructorData($className, $data); + $object = $this->objectManager->create($className, $constructorArgs); + + // Secondary method: fallback to setter methods foreach ($data as $propertyName => $value) { + if (isset($constructorArgs[$propertyName])) { + continue; + } + // Converts snake_case to uppercase CamelCase to help form getter/setter method names // This use case is for REST only. SOAP request data is already camel cased $camelCaseProperty = SimpleDataObjectConverter::snakeCaseToUpperCamelCase($propertyName); diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php new file mode 100644 index 0000000000000..a62fb70c50116 --- /dev/null +++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php @@ -0,0 +1,43 @@ +entityId = $entityId; + $this->name = $name; + } + + /** + * @return int|null + */ + public function getEntityId() + { + return $this->entityId; + } + + /** + * @return string|null + */ + public function getName() + { + return $this->name; + } +} diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/TestService.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/TestService.php index a5f91c101aa56..34515e8460b57 100644 --- a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/TestService.php +++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/TestService.php @@ -5,11 +5,6 @@ */ namespace Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor; -use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\AssociativeArray; -use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\DataArray; -use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\Nested; -use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\SimpleArray; - class TestService { const DEFAULT_VALUE = 42; @@ -25,6 +20,15 @@ public function simple($entityId, $name) return [$entityId, $name]; } + /** + * @param \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\SimpleConstructor $simpleConstructor + * @return \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\SimpleConstructor + */ + public function simpleConstructor(SimpleConstructor $simpleConstructor) + { + return $simpleConstructor; + } + /** * @param int $entityId * @return string[] @@ -34,6 +38,15 @@ public function simpleDefaultValue($entityId = self::DEFAULT_VALUE) return [$entityId]; } + /** + * @param int $entityId + * @return string[] + */ + public function constructorArguments($entityId = self::DEFAULT_VALUE) + { + return [$entityId]; + } + /** * @param \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\Nested $nested * @return \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\Nested diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessorTest.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessorTest.php index a4c3f00e374a8..3393b325a8f16 100644 --- a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessorTest.php +++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessorTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Webapi\ServiceInputProcessor; use Magento\Framework\Webapi\ServiceTypeToEntityTypeMap; +use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\SimpleConstructor; use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\WebapiBuilderFactory; use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\AssociativeArray; use Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\DataArray; @@ -59,8 +60,8 @@ protected function setUp() $this->objectManagerMock->expects($this->any()) ->method('create') ->willReturnCallback( - function ($className) use ($objectManager) { - return $objectManager->getObject($className); + function ($className, $arguments = []) use ($objectManager) { + return $objectManager->getObject($className, $arguments); } ); @@ -202,6 +203,22 @@ public function testNestedDataProperties() $this->assertEquals('Test', $details->getName()); } + public function testSimpleConstructorProperties() + { + $data = ['simpleConstructor' => ['entityId' => 15, 'name' => 'Test']]; + $result = $this->serviceInputProcessor->process( + \Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor\TestService::class, + 'simpleConstructor', + $data + ); + $this->assertNotNull($result); + $arg = $result[0]; + + $this->assertTrue($arg instanceof SimpleConstructor); + $this->assertEquals(15, $arg->getEntityId()); + $this->assertEquals('Test', $arg->getName()); + } + public function testSimpleArrayProperties() { $data = ['ids' => [1, 2, 3, 4]]; From 04a53a8ac6b051172659c0b493eba6f508b860af Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta Date: Sun, 22 Apr 2018 12:49:46 -0700 Subject: [PATCH 008/333] FIX for static tests failure _createFromArray will not be fixed due to its eventual deprecation, since a new immutable approach is provided --- .../Magento/Framework/Webapi/ServiceInputProcessor.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index 0c32396897c12..f75044ec88c71 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -5,6 +5,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Webapi; use Magento\Framework\Webapi\ServiceTypeToEntityTypeMap; @@ -197,6 +199,7 @@ private function getConstructorData(string $className, array $data): array * @param array $data * @return object the newly created and populated object * @throws \Exception + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function _createFromArray($className, $data) { From 68e5dcd8c3bf9ad3b897d264579434de62940b62 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta Date: Sun, 22 Apr 2018 13:13:05 -0700 Subject: [PATCH 009/333] FIX static tests, missing declare(strict_types=1) --- .../Test/Unit/ServiceInputProcessor/SimpleConstructor.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php index a62fb70c50116..f457a96e22a37 100644 --- a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php +++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Webapi\Test\Unit\ServiceInputProcessor; class SimpleConstructor From 3a12905324c8e4975e554eeeec41552627e4369c Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Thu, 3 May 2018 20:26:06 +0200 Subject: [PATCH 010/333] Fix for displaying a negative price for a custom option. Currently a negative price is displayed as +- 5.00 for instance. By changing the template to check of the value is actually positive a negative value will be displayed as - 5.00 and a positive value will be displayed as + 5.00 --- app/code/Magento/Catalog/view/base/web/js/price-options.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/base/web/js/price-options.js b/app/code/Magento/Catalog/view/base/web/js/price-options.js index ceeea4c878622..e18abe3af38a6 100644 --- a/app/code/Magento/Catalog/view/base/web/js/price-options.js +++ b/app/code/Magento/Catalog/view/base/web/js/price-options.js @@ -20,8 +20,10 @@ define([ optionConfig: {}, optionHandlers: {}, optionTemplate: '<%= data.label %>' + - '<% if (data.finalPrice.value) { %>' + + '<% if (data.finalPrice.value > 0) { %>' + ' +<%- data.finalPrice.formatted %>' + + '<% } else if (data.finalPrice.value < 0) { %>' + + ' <%- data.finalPrice.formatted %>' + '<% } %>', controlContainer: 'dd' }; From 6dff3a53b1ac0df593163894fd57b8a36722450f Mon Sep 17 00:00:00 2001 From: gwharton Date: Mon, 7 May 2018 18:47:23 +0100 Subject: [PATCH 011/333] Correct missing commits for #10210 Add missing commits for \app\code\Magento\Sales\Model\Order\Email\Sender\OrderSender.php --- .../Sales/Model/Order/Email/Sender/OrderSender.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 2944b8ccef647..92d00d0436634 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php @@ -131,14 +131,17 @@ protected function prepareTemplate(Order $order) 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), ]; - $transport = new DataObject($transport); + $transportObject = new DataObject($transport); + /** + * Event argument `transport` is @deprecated. Use `transportObject` instead. + */ $this->eventManager->dispatch( 'email_order_set_template_vars_before', - ['sender' => $this, 'transport' => $transport] + ['sender' => $this, 'transport' => $transportObject->getData(), 'transportObject' => $transportObject] ); - $this->templateContainer->setTemplateVars($transport->getData()); + $this->templateContainer->setTemplateVars($transportObject->getData()); parent::prepareTemplate($order); } From b168a117710c824b2137c5227949253fe20ffd2e Mon Sep 17 00:00:00 2001 From: Joan He Date: Tue, 8 May 2018 11:49:26 -0500 Subject: [PATCH 012/333] MAGETWO-90652: PayPal BrainTree order fails to checkout with 500 Error --- app/code/Magento/Braintree/etc/di.xml | 2 +- app/code/Magento/Vault/Model/PaymentTokenFactory.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml index 5f4a345760f2d..2bb4cea6742d6 100644 --- a/app/code/Magento/Braintree/etc/di.xml +++ b/app/code/Magento/Braintree/etc/di.xml @@ -447,7 +447,7 @@ - Magento\Vault\Model\AccountPaymentTokenFactory + Magento\Vault\Api\Data\PaymentTokenFactoryInterface diff --git a/app/code/Magento/Vault/Model/PaymentTokenFactory.php b/app/code/Magento/Vault/Model/PaymentTokenFactory.php index e0b5d742fe8a4..6249fa4944a2c 100644 --- a/app/code/Magento/Vault/Model/PaymentTokenFactory.php +++ b/app/code/Magento/Vault/Model/PaymentTokenFactory.php @@ -22,6 +22,11 @@ class PaymentTokenFactory implements PaymentTokenFactoryInterface */ private $tokenTypes = []; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + /** * PaymentTokenFactory constructor. * @param ObjectManagerInterface $objectManager From 8e0ff74f67d9e6a75e6005171e059b4a4cbdced2 Mon Sep 17 00:00:00 2001 From: Daniel Renaud Date: Thu, 10 May 2018 14:47:59 -0500 Subject: [PATCH 013/333] MAGETWO-86709: Widget isn't updated on Storefront if related Products are updated --- app/code/Magento/Catalog/etc/widget.xml | 6 +++++- app/code/Magento/Catalog/i18n/en_US.csv | 8 +++++++- app/code/Magento/CatalogWidget/etc/widget.xml | 6 +++++- app/code/Magento/CatalogWidget/i18n/en_US.csv | 8 +++++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/etc/widget.xml b/app/code/Magento/Catalog/etc/widget.xml index ef9009549da24..1f194061fce6d 100644 --- a/app/code/Magento/Catalog/etc/widget.xml +++ b/app/code/Magento/Catalog/etc/widget.xml @@ -64,7 +64,11 @@ - 86400 by default, if not set. To refresh instantly, clear the Blocks HTML Output cache. + + If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. +

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product.]]> +
diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index b9012c030dace..0c80bd7461762 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -705,7 +705,13 @@ Template,Template "New Products Names Only Template","New Products Names Only Template" "New Products Images Only Template","New Products Images Only Template" "Cache Lifetime (Seconds)","Cache Lifetime (Seconds)" -"86400 by default, if not set. To refresh instantly, clear the Blocks HTML Output cache.","86400 by default, if not set. To refresh instantly, clear the Blocks HTML Output cache." +"Time in seconds between the widget updates. +
If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. +

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product." +, +"Time in seconds between the widget updates. +
If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. +

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product." "Catalog Product Link","Catalog Product Link" "Link to a Specified Product","Link to a Specified Product" "Select Product...","Select Product..." diff --git a/app/code/Magento/CatalogWidget/etc/widget.xml b/app/code/Magento/CatalogWidget/etc/widget.xml index 3d54c314c6622..a77c26de19c29 100644 --- a/app/code/Magento/CatalogWidget/etc/widget.xml +++ b/app/code/Magento/CatalogWidget/etc/widget.xml @@ -40,7 +40,11 @@ - 86400 by default, if not set. To refresh instantly, clear the Blocks HTML Output cache. + + If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. +

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product.]]> +
diff --git a/app/code/Magento/CatalogWidget/i18n/en_US.csv b/app/code/Magento/CatalogWidget/i18n/en_US.csv index 9ecde5cb1a062..d711da90785ce 100644 --- a/app/code/Magento/CatalogWidget/i18n/en_US.csv +++ b/app/code/Magento/CatalogWidget/i18n/en_US.csv @@ -16,5 +16,11 @@ Title,Title Template,Template "Products Grid Template","Products Grid Template" "Cache Lifetime (Seconds)","Cache Lifetime (Seconds)" -"86400 by default, if not set. To refresh instantly, clear the Blocks HTML Output cache.","86400 by default, if not set. To refresh instantly, clear the Blocks HTML Output cache." +"Time in seconds between the widget updates. +
If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. +

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product." +, +"Time in seconds between the widget updates. +
If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. +

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product." Conditions,Conditions From 2bff63f30a093da404f2edec105a5d674a568eca Mon Sep 17 00:00:00 2001 From: Joan He Date: Thu, 10 May 2018 16:12:38 -0500 Subject: [PATCH 014/333] MAGETWO-90663: Error/Exception is thrown when user tries to delete product from /checkout/cart page after already been removed from mini-shopping cart --- .../view/frontend/web/js/shopping-cart.js | 23 ++++++++++++++++++- .../Checkout/view/frontend/web/js/sidebar.js | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js index 399321bd2f67d..3ea49cd981d90 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js @@ -12,7 +12,7 @@ define([ $.widget('mage.shoppingCart', { /** @inheritdoc */ _create: function () { - var items, i; + var items, i, reload; $(this.options.emptyCartButton).on('click', $.proxy(function () { $(this.options.emptyCartButton).attr('name', 'update_cart_action_temp'); @@ -36,6 +36,27 @@ define([ $(this.options.continueShoppingButton).on('click', $.proxy(function () { location.href = this.options.continueShoppingUrl; }, this)); + + $(document).on('ajax:removeFromCart', $.proxy(function () { + reload = true; + $('div.block.block-minicart').on('dropdowndialogclose', $.proxy(function () { + if (reload === true) { + location.reload(); + reload = false; + } + $('div.block.block-minicart').off('dropdowndialogclose'); + })); + }, this)); + $(document).on('ajax:updateItemQty', $.proxy(function () { + reload = true; + $('div.block.block-minicart').on('dropdowndialogclose', $.proxy(function () { + if (reload === true) { + location.reload(); + reload = false; + } + $('div.block.block-minicart').off('dropdowndialogclose'); + })); + }, this)); } }); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index 13a2b524e5186..bf1124ba926b4 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -213,6 +213,7 @@ define([ */ _updateItemQtyAfter: function (elem) { this._hideItemButton(elem); + $(document).trigger('ajax:updateItemQty'); }, /** From 7e850495ad8f10785326a9f96013bcc2aed44eba Mon Sep 17 00:00:00 2001 From: Daniel Renaud Date: Thu, 10 May 2018 17:06:55 -0500 Subject: [PATCH 015/333] MAGETWO-89410: [2.3] Test testValidComposerJson fails on bundle extensions - Backport: MAGETWO-88812: Test testValidComposerJson fails on bundle extensions --- .../Magento/Test/Integrity/ComposerTest.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index c70cac369af6d..ba7df0c973605 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -171,7 +171,9 @@ private function assertMagentoConventions($dir, $packageType, \StdClass $json) switch ($packageType) { case 'magento2-module': $xml = simplexml_load_file("$dir/etc/module.xml"); - $this->assertConsistentModuleName($xml, $json->name); + if ($this->isVendorMagento($json->name)) { + $this->assertConsistentModuleName($xml, $json->name); + } $this->assertDependsOnPhp($json->require); $this->assertPhpVersionInSync($json->name, $json->require->php); $this->assertDependsOnFramework($json->require); @@ -210,6 +212,17 @@ private function assertMagentoConventions($dir, $packageType, \StdClass $json) } } + /** + * Returns true if package vendor is Magento. + * + * @param string $packageName + * @return bool + */ + private function isVendorMagento($packageName) + { + return strpos($packageName, 'magento') === 0; + } + /** * Assert that component registrar is autoloaded in composer json * From 4ed21aefe959ad860605ef7742d9ea7a1c8f5c80 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Fri, 11 May 2018 11:30:46 -0500 Subject: [PATCH 016/333] MAGETWO-91220: Disabling ui-input component isn't work properly - Fixed setting propagation - Added regression unit tests --- .../base/web/js/form/element/url-input.js | 2 + .../Ui/base/js/form/element/url-input.test.js | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/url-input.js b/app/code/Magento/Ui/view/base/web/js/form/element/url-input.js index 6c370919f11db..2bfce304b9adc 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/url-input.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/url-input.js @@ -55,6 +55,8 @@ define([ linkSettingsArray.name = baseLinkType.namePrefix + linkName; linkSettingsArray.dataScope = baseLinkType.dataScopePrefix + linkName; linkSettingsArray.type = linkName; + linkSettingsArray.disabled = config.disabled; + linkSettingsArray.visible = config.visible; processedLinkTypes[linkName] = {}; _.extend(processedLinkTypes[linkName], baseLinkType, linkSettingsArray); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js index c0f55168a5496..fed36b9f25490 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js @@ -72,5 +72,46 @@ define([ }); }); + + describe('Parent config properties are propagated', function () { + it('disabled property is set on child', function () { + var params = { + dataScope: 'urlInput', + disabled: true, + urlTypes: { + url: { + label: 'Test label', + component: 'Magento_Ui/js/form/element/abstract', + template: 'ui/form/element/input', + sortOrder: 40 + } + } + }; + + component = new UrlInput(params); + expect(component.disabled()).toBe(true); + expect(component.urlTypes.url.disabled).toBe(true); + }); + + it('visible property is set on child', function () { + var params = { + dataScope: 'urlInput', + visible: false, + urlTypes: { + url: { + label: 'Test label', + component: 'Magento_Ui/js/form/element/abstract', + template: 'ui/form/element/input', + sortOrder: 40 + } + } + }; + + component = new UrlInput(params); + expect(component.visible()).toBe(false); + expect(component.urlTypes.url.visible).toBe(false); + }); + }); + }); }); From 4f0454c30afc54ec26090091bb2a2855b39b8add Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Fri, 11 May 2018 11:54:30 -0500 Subject: [PATCH 017/333] MAGETWO-91220: Select Tab Alignment Visually - Fixed static error --- .../app/code/Magento/Ui/base/js/form/element/url-input.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js index fed36b9f25490..7fbac7a0c86e8 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js @@ -72,7 +72,6 @@ define([ }); }); - describe('Parent config properties are propagated', function () { it('disabled property is set on child', function () { var params = { From d05635998ba71fc8a8c65c5bcb8effd4c028c7ab Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Fri, 11 May 2018 11:57:58 -0500 Subject: [PATCH 018/333] MAGETWO-91220: Disabling ui-input component isn't work properly - Updated test names --- .../code/Magento/Ui/base/js/form/element/url-input.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js index 7fbac7a0c86e8..533faa7c2eef0 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/url-input.test.js @@ -73,7 +73,7 @@ define([ }); describe('Parent config properties are propagated', function () { - it('disabled property is set on child', function () { + it('sets the disabled property on the child element', function () { var params = { dataScope: 'urlInput', disabled: true, @@ -92,7 +92,7 @@ define([ expect(component.urlTypes.url.disabled).toBe(true); }); - it('visible property is set on child', function () { + it('sets the visible property on the child element', function () { var params = { dataScope: 'urlInput', visible: false, From a50478b794c760ab2c8da2e6922e01ec00206793 Mon Sep 17 00:00:00 2001 From: Joan He Date: Fri, 11 May 2018 12:35:44 -0500 Subject: [PATCH 019/333] MAGETWO-90652: PayPal BrainTree order fails to checkout with 500 Error --- .../Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml index bea169dc64ad3..7f15a6ba03f15 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml @@ -26,7 +26,7 @@ braintree braintree, braintree_use_vault Processing - test_type:3rd_party_test, severity:S1 + severity:S1 From 2518b24015dc67eb7c9763ac93ed5831f236b4f6 Mon Sep 17 00:00:00 2001 From: Daniel Renaud Date: Fri, 11 May 2018 13:17:16 -0500 Subject: [PATCH 020/333] MAGETWO-86709: Widget isn't updated on Storefront if related Products are updated --- app/code/Magento/Catalog/etc/widget.xml | 4 ++-- app/code/Magento/Catalog/i18n/en_US.csv | 8 ++++---- app/code/Magento/CatalogWidget/etc/widget.xml | 4 ++-- app/code/Magento/CatalogWidget/i18n/en_US.csv | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/etc/widget.xml b/app/code/Magento/Catalog/etc/widget.xml index 1f194061fce6d..a11d206e2ce42 100644 --- a/app/code/Magento/Catalog/etc/widget.xml +++ b/app/code/Magento/Catalog/etc/widget.xml @@ -66,8 +66,8 @@ If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. -

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product.]]> +
If not set, equals to 86400 seconds (24 hours). To update widget instantly, go to Cache Management and clear Blocks HTML Output cache. +
Widget will not show products that begin to match the specified conditions until cache is refreshed.]]>
diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index 0c80bd7461762..0727d03df1340 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -706,12 +706,12 @@ Template,Template "New Products Images Only Template","New Products Images Only Template" "Cache Lifetime (Seconds)","Cache Lifetime (Seconds)" "Time in seconds between the widget updates. -
If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. -

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product." +
If not set, equals to 86400 seconds (24 hours). To update widget instantly, go to Cache Management and clear Blocks HTML Output cache. +
Widget will not show products that begin to match the specified conditions until cache is refreshed." , "Time in seconds between the widget updates. -
If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. -

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product." +
If not set, equals to 86400 seconds (24 hours). To update widget instantly, go to Cache Management and clear Blocks HTML Output cache. +
Widget will not show products that begin to match the specified conditions until cache is refreshed." "Catalog Product Link","Catalog Product Link" "Link to a Specified Product","Link to a Specified Product" "Select Product...","Select Product..." diff --git a/app/code/Magento/CatalogWidget/etc/widget.xml b/app/code/Magento/CatalogWidget/etc/widget.xml index a77c26de19c29..bcc1b623da02e 100644 --- a/app/code/Magento/CatalogWidget/etc/widget.xml +++ b/app/code/Magento/CatalogWidget/etc/widget.xml @@ -42,8 +42,8 @@ If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. -

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product.]]> +
If not set, equals to 86400 seconds (24 hours). To update widget instantly, go to Cache Management and clear Blocks HTML Output cache. +
Widget will not show products that begin to match the specified conditions until cache is refreshed.]]>
If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. -

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product." +
If not set, equals to 86400 seconds (24 hours). To update widget instantly, go to Cache Management and clear Blocks HTML Output cache. +
Widget will not show products that begin to match the specified conditions until cache is refreshed." , "Time in seconds between the widget updates. -
If not set, equals to 86400 seconds (24 hours). To refresh instantly, go to Cache Management and clear Blocks HTML Output cache. -

Warning! Widget will show disabled or unavailable products if cache is not cleared after updating the product." +
If not set, equals to 86400 seconds (24 hours). To update widget instantly, go to Cache Management and clear Blocks HTML Output cache. +
Widget will not show products that begin to match the specified conditions until cache is refreshed." Conditions,Conditions From dd3aa3873c752394d5a3c37834005f916ab94e36 Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras Date: Sat, 12 May 2018 14:54:52 +0200 Subject: [PATCH 021/333] GraphQL - Added sort by options to Products GraphQL type --- .../Model/Resolver/Products.php | 35 +++++++++++++++++-- app/code/Magento/GraphQl/etc/schema.graphqls | 11 ++++++ .../GraphQl/Catalog/ProductSearchTest.php | 13 +++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php index cc791ce780c14..ad0cea010934f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php @@ -53,12 +53,23 @@ class Products implements ResolverInterface * @var Layer\DataProvider\Filters */ private $filtersDataProvider; + + /** + * @var \Magento\Catalog\Model\Config + */ + private $catalogConfig; + + /** + * @var \Magento\Store\Model\StoreManagerInterface + */ + private $storeManager; /** * @param Builder $searchCriteriaBuilder * @param Search $searchQuery * @param Filter $filterQuery * @param ValueFactory $valueFactory + * @param \Magento\Catalog\Model\Config $catalogConfig */ public function __construct( Builder $searchCriteriaBuilder, @@ -66,7 +77,9 @@ public function __construct( Filter $filterQuery, SearchFilter $searchFilter, ValueFactory $valueFactory, - \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider + \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider, + \Magento\Catalog\Model\Config $catalogConfig, + \Magento\Store\Model\StoreManagerInterface $storeManager ) { $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->searchQuery = $searchQuery; @@ -74,6 +87,8 @@ public function __construct( $this->searchFilter = $searchFilter; $this->valueFactory = $valueFactory; $this->filtersDataProvider = $filtersDataProvider; + $this->catalogConfig = $catalogConfig; + $this->storeManager = $storeManager; } /** @@ -118,14 +133,28 @@ public function resolve( ); } + $options = $this->catalogConfig->getAttributeUsedForSortByArray(); + + $sortFields = [ + 'default' => $this->catalogConfig->getProductListDefaultSortBy($this->storeManager->getStore()->getId()), + 'options' => [] + ]; + + $sortFields['options'][] = ['key' => 'position', 'label' => 'Position']; + foreach ($this->catalogConfig->getAttributesUsedForSortBy() as $attribute) { + $sortFields['options'][] = ['key' => $attribute->getAttributeCode(), 'label' => $attribute->getStoreLabel()]; + } + $data = [ 'total_count' => $searchResult->getTotalCount(), 'items' => $searchResult->getProductsSearchResult(), 'page_info' => [ 'page_size' => $searchCriteria->getPageSize(), - 'current_page' => $currentPage + 'current_page' => $currentPage, + 'sort_fields' => $sortFields, ], - 'filters' => $this->filtersDataProvider->getData($layerType) + 'filters' => $this->filtersDataProvider->getData($layerType), + ]; $result = function () use ($data) { diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index ffdf5511b7492..4e6bb1cc32efa 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -25,9 +25,20 @@ input FilterTypeInput @doc(description: "FilterTypeInput specifies which action type SearchResultPageInfo @doc(description: "SearchResultPageInfo provides navigation for the query response") { page_size: Int @doc(description: "Specifies the maximum number of items to return") current_page: Int @doc(description: "Specifies which page of results to return") + sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields") } enum SortEnum @doc(description: "This enumeration indicates whether to return results in ascending or descending order") { ASC DESC } + +type SortField { + key: String @doc(description: "Attribute code of sort field") + label: String @doc(description: "Label of sort field") +} + +type SortFields @doc(description: "SortFields contains a default value for sort fields and all available sort fields") { + default: String @doc(description: "Default value of sort fields") + options: [SortField] @doc(description: "Available sort fields") +} \ No newline at end of file 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 b95e0f933ea04..54380bf79937a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -511,6 +511,15 @@ public function testQueryProductsInCurrentPageSortedByPriceASC() { page_size current_page + sort_fields + { + default + options + { + key + label + } + } } } } @@ -530,6 +539,10 @@ public function testQueryProductsInCurrentPageSortedByPriceASC() $this->assertProductItems($filteredChildProducts, $response); $this->assertEquals(4, $response['products']['page_info']['page_size']); $this->assertEquals(1, $response['products']['page_info']['current_page']); + $this->assertArrayHasKey('sort_fields', $response['products']['page_info']); + $this->assertArrayHasKey('options', $response['products']['page_info']['sort_fields']); + $this->assertArrayHasKey('default', $response['products']['page_info']['sort_fields']); + $this->assertEquals('position', $response['products']['page_info']['sort_fields']['default']); } /** From 02d6a22a52f96e79e6b3baa1847bb224b824bc36 Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras Date: Sat, 12 May 2018 16:13:11 +0200 Subject: [PATCH 022/333] GraphQL - Added missing constructor doc block type for StoreManagerInterface --- app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php index ad0cea010934f..02a5284f74c8b 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php @@ -70,6 +70,7 @@ class Products implements ResolverInterface * @param Filter $filterQuery * @param ValueFactory $valueFactory * @param \Magento\Catalog\Model\Config $catalogConfig + * @param \Magento\Store\Model\StoreManagerInterface $storeManager */ public function __construct( Builder $searchCriteriaBuilder, From 3ec840ca5f72e543b3cd3672f03e3181f0984ef5 Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras Date: Sat, 12 May 2018 16:53:22 +0200 Subject: [PATCH 023/333] GraphQL - Fix urlResolver query to support relative path --- .../Model/Resolver/UrlRewrite.php | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 64bfa73f8aef1..1cef3770d78fa 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -14,6 +14,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\UrlFinderInterface; +use Magento\Store\Model\ScopeInterface; /** * UrlRewrite field resolver, used for GraphQL request processing. @@ -34,20 +35,29 @@ class UrlRewrite implements ResolverInterface * @var ValueFactory */ private $valueFactory; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + /** * @param UrlFinderInterface $urlFinder * @param StoreManagerInterface $storeManager * @param ValueFactory $valueFactory + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig */ public function __construct( UrlFinderInterface $urlFinder, StoreManagerInterface $storeManager, - ValueFactory $valueFactory + ValueFactory $valueFactory, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig ) { $this->urlFinder = $urlFinder; $this->storeManager = $storeManager; $this->valueFactory = $valueFactory; + $this->scopeConfig = $scopeConfig; } /** @@ -63,8 +73,19 @@ public function resolve( $result = function () { return null; }; + if (isset($args['url'])) { - $urlRewrite = $this->findCanonicalUrl($args['url']); + $url = $args['url']; + if (substr($url, 0, 1) === '/' && $url !== '/') { + $url = ltrim($url, '/'); + } else if ($url === '/') { + $homePageIdentifier = $this->scopeConfig->getValue( + \Magento\Cms\Helper\Page::XML_PATH_HOME_PAGE, + ScopeInterface::SCOPE_STORE + ); + $url = $homePageIdentifier; + } + $urlRewrite = $this->findCanonicalUrl($url); if ($urlRewrite) { $urlRewriteReturnArray = [ 'id' => $urlRewrite->getEntityId(), @@ -96,6 +117,7 @@ private function findCanonicalUrl(string $requestPath) : ?\Magento\UrlRewrite\Se if (!$urlRewrite) { $urlRewrite = $this->findUrlFromTargetPath($requestPath); } + return $urlRewrite; } From 92a6ccef16d257e37470bb77b03da5a55923fe0c Mon Sep 17 00:00:00 2001 From: Bartosz Kubicki Date: Sat, 12 May 2018 16:54:46 +0200 Subject: [PATCH 024/333] Adding support for variadic arguments fro method in generated proxy classes. --- .../Magento/Framework/ObjectManager/Code/Generator/Proxy.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php index 9d80e046518fd..96595bc7a073b 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php @@ -155,7 +155,8 @@ protected function _getMethodInfo(\ReflectionMethod $method) $parameterNames = []; $parameters = []; foreach ($method->getParameters() as $parameter) { - $parameterNames[] = '$' . $parameter->getName(); + $name = $parameter->isVariadic() ? '... $' . $parameter->getName() : '$' . $parameter->getName(); + $parameterNames[] = $name; $parameters[] = $this->_getMethodParameterInfo($parameter); } From 089e77a1efdbffd29c24fe7746e9e52c845ae56a Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta Date: Sun, 13 May 2018 19:39:58 +0200 Subject: [PATCH 025/333] FIX optional constructor parameter for backward compatiblity --- .../Framework/Webapi/ServiceInputProcessor.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index ed803260c77ef..7f40937e98ac9 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -9,12 +9,12 @@ namespace Magento\Framework\Webapi; -use Magento\Framework\Webapi\ServiceTypeToEntityTypeMap; use Magento\Framework\Api\AttributeValue; use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\Api\SimpleDataObjectConverter; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\SerializationException; +use Magento\Framework\ObjectManager\ConfigInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Phrase; use Magento\Framework\Reflection\MethodsMap; @@ -68,7 +68,7 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface private $serviceTypeToEntityTypeMap; /** - * @var \Magento\Framework\ObjectManager\ConfigInterface + * @var ConfigInterface */ private $config; @@ -80,7 +80,7 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface * @param AttributeValueFactory $attributeValueFactory * @param CustomAttributeTypeLocatorInterface $customAttributeTypeLocator * @param MethodsMap $methodsMap - * @param \Magento\Framework\ObjectManager\ConfigInterface $config + * @param ConfigInterface $config * @param ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap */ public function __construct( @@ -89,7 +89,7 @@ public function __construct( AttributeValueFactory $attributeValueFactory, CustomAttributeTypeLocatorInterface $customAttributeTypeLocator, MethodsMap $methodsMap, - \Magento\Framework\ObjectManager\ConfigInterface $config, + ConfigInterface $config = null, ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap = null ) { $this->typeProcessor = $typeProcessor; @@ -99,7 +99,8 @@ public function __construct( $this->methodsMap = $methodsMap; $this->serviceTypeToEntityTypeMap = $serviceTypeToEntityTypeMap ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ServiceTypeToEntityTypeMap::class); - $this->config = $config; + $this->config = $config + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ConfigInterface::class); } /** From daf9f9053b36b0f2010ba65e41066c6cade11054 Mon Sep 17 00:00:00 2001 From: Iryna Lagno Date: Mon, 14 May 2018 10:59:33 -0500 Subject: [PATCH 026/333] MAGETWO-91262: Fix static test to check files in the lib directory --- .../Test/Integrity/StaticFilesTest.php | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php index 46036faa99fca..9fb176de21458 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php @@ -6,6 +6,8 @@ namespace Magento\Test\Integrity; +use Magento\Framework\App\Filesystem\DirectoryList; + /** * An integrity test that searches for references to static files and asserts that they are resolved via fallback */ @@ -36,6 +38,24 @@ class StaticFilesTest extends \PHPUnit\Framework\TestCase */ private $baseTheme; + /** + * @var \Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Alternative + */ + private $alternativeResolver; + + /** + * Factory for simple rule + * + * @var \Magento\Framework\View\Design\Fallback\Rule\SimpleFactory + */ + private $simpleFactory; + + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + + protected function setUp() { $om = \Magento\TestFramework\Helper\Bootstrap::getObjectmanager(); @@ -46,6 +66,11 @@ protected function setUp() $this->themeRepo = $om->get(\Magento\Framework\View\Design\Theme\FlyweightFactory::class); $this->design = $om->get(\Magento\Framework\View\DesignInterface::class); $this->baseTheme = $om->get(\Magento\Framework\View\Design\ThemeInterface::class); + $this->alternativeResolver = $om->get( + \Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Alternative::class + ); + $this->simpleFactory = $om->get(\Magento\Framework\View\Design\Fallback\Rule\SimpleFactory::class); + $this->filesystem = $om->get(\Magento\Framework\Filesystem::class); } /** @@ -93,8 +118,17 @@ public function testReferencesFromStaticFiles($area, $themePath, $locale, $modul $relatedPath = \Magento\Framework\View\FileSystem::getRelatedPath($filePath, $relatedResource); } // the $relatedPath will be suitable for feeding to the fallback system + $staticFile = $this->getStaticFile($area, $themePath, $locale, $relatedPath, $fallbackModule); + if (empty($staticFile) && substr($relatedPath, 0, 2) === '..') { + //check if static file exists on lib level + $path = substr($relatedPath, 2); + $libDir = rtrim($this->filesystem->getDirectoryRead(DirectoryList::LIB_WEB)->getAbsolutePath(), '/'); + $rule = $this->simpleFactory->create(['pattern' => $libDir]); + $params = ['area' => $area, 'theme' => $themePath, 'locale' => $locale]; + $staticFile = $this->alternativeResolver->resolveFile($rule, $path, $params); + } $this->assertNotEmpty( - $this->getStaticFile($area, $themePath, $locale, $relatedPath, $fallbackModule), + $staticFile, "The related resource cannot be resolved through fallback: '{$relatedResource}'" ); } @@ -132,7 +166,7 @@ private function getDefaultThemePath($area) * @param bool $isExplicit * @return bool|string */ - private function getStaticFile($area, $theme, $locale, $filePath, $module, $isExplicit = false) + private function getStaticFile($area, $theme, $locale, $filePath, $module = null, $isExplicit = false) { if ($area == 'base') { $theme = $this->baseTheme; From 32a328435011568db6e8f5b2321d17e9cfec6605 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Mon, 14 May 2018 16:38:06 -0500 Subject: [PATCH 027/333] MC-57: Admin should be able to mass change products' status. --- .../AdminMassChangeProductsStatusTest.xml | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminMassChangeProductsStatusTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminMassChangeProductsStatusTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminMassChangeProductsStatusTest.xml new file mode 100644 index 0000000000000..cf7c6af2fbd85 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminMassChangeProductsStatusTest.xml @@ -0,0 +1,75 @@ + + + + + + + + + + <description value="Admin should be able to mass change products status"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-57"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProductOne"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProductTwo"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <argument name="keyword" value="api-simple-product"/> + </actionGroup> + <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + <!-- Mass change status --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickChangeStatus"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisable"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 2 record(s) have been updated." stepKey="seeAttributeUpateSuccessMsg"/> + + <!-- Assert product one is not visible on storefront --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStorefrontAdvancedCatalogSearchProductOne"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="searchByNameProductOne"> + <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="StorefrontCheckAdvancedSearchResultForProductOne"/> + <see userInput="We can't find any items matching these search criteria" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeCantFindProductOneMessage"/> + + <!-- Assert product two is not visible on storefront --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStorefrontAdvancedCatalogSearchProductTwo"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="searchByNameProductTwo"> + <argument name="name" value="$$createProductTwo.name$$"/> + <argument name="priceFrom" value="$$createProductTwo.price$$0"/> + <argument name="priceTo" value="$$createProductTwo.price$$0"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultForProductTwo"/> + <see userInput="We can't find any items matching these search criteria" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeCantFindProductTwoMessage"/> + </test> +</tests> From ccda08dd0ca5773f5b01be5d3eab9327c87357b4 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Mon, 14 May 2018 12:30:40 -0500 Subject: [PATCH 028/333] MAGETWO-91249: TinyMCE v3.5.8 WYSIWYG as a deprecated module --- .../Model/Wysiwyg/DefaultConfigProvider.php | 4 +- .../Wysiwyg/Gallery/DefaultConfigProvider.php | 4 +- .../Cms/Model/WysiwygDefaultConfig.php | 4 +- .../Model/Config/Source/Wysiwyg/Editor.php | 6 +++ .../Tinymce3/Model/Config/Variable/Config.php | 5 +- .../Tinymce3/Model/Config/Widget/Config.php | 7 ++- .../Config/Widget/PlaceholderImagesPool.php | 5 +- .../Tinymce3/Model/Config/Wysiwyg/Config.php | 5 +- .../Magento/Tinymce3/Model/Plugin/Widget.php | 12 +++-- app/code/Magento/Tinymce3/README.md | 4 +- app/code/Magento/Tinymce3/etc/di.xml | 16 +++++++ .../plugins/magentovariable/editor_plugin.js | 3 ++ .../Tinymce3/view/base/web/tinymce3Adapter.js | 3 ++ .../Magento/Ui/Block/Wysiwyg/ActiveEditor.php | 25 +++++++++- .../Model/Variable/ConfigProvider.php | 3 +- .../Magento/Widget/Model/Widget/Config.php | 2 +- .../Section/AdminProductFormSection.xml | 1 - .../Cms/Section/TinyMCESection.xml | 1 - .../Section/NewsletterTemplateSection.xml | 1 - .../FunctionalTest/Tinymce3/LICENSE.txt | 48 +++++++++++++++++++ .../FunctionalTest/Tinymce3/LICENSE_AFL.txt | 48 +++++++++++++++++++ .../Magento/FunctionalTest/Tinymce3/README.md | 3 ++ .../Section/AdminTinymce3FileldsSection.xml | 20 ++++++++ .../Test/AdminSwitchWYSIWYGOptionsTest.xml | 0 .../FunctionalTest/Tinymce3/composer.json | 35 ++++++++++++++ .../TestModuleWysiwygConfig/Model/Config.php | 11 +++-- .../TestModuleWysiwygConfig/etc/di.xml | 16 +++++++ .../Data/Wysiwyg/ConfigProviderInterface.php | 5 +- 28 files changed, 268 insertions(+), 29 deletions(-) create mode 100644 app/code/Magento/Tinymce3/etc/di.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/LICENSE.txt create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/LICENSE_AFL.txt create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/README.md create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/Section/AdminTinymce3FileldsSection.xml rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/{Cms => Tinymce3}/Test/AdminSwitchWYSIWYGOptionsTest.xml (100%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/composer.json create mode 100644 dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml diff --git a/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php b/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php index dee37c8e901ec..2ff2aa3f82ba8 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Model\Wysiwyg; /** @@ -27,7 +29,7 @@ public function __construct(\Magento\Framework\View\Asset\Repository $assetRepo) /** * {@inheritdoc} */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { $config->addData([ 'tinymce4' => [ diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Gallery/DefaultConfigProvider.php b/app/code/Magento/Cms/Model/Wysiwyg/Gallery/DefaultConfigProvider.php index 2301cf9950ecc..822f9ce2b1cb5 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Gallery/DefaultConfigProvider.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Gallery/DefaultConfigProvider.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Model\Wysiwyg\Gallery; class DefaultConfigProvider implements \Magento\Framework\Data\Wysiwyg\ConfigProviderInterface @@ -49,7 +51,7 @@ public function __construct( /** * {@inheritdoc} */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { $pluginData = (array) $config->getData('plugins'); $imageData = [ diff --git a/app/code/Magento/Cms/Model/WysiwygDefaultConfig.php b/app/code/Magento/Cms/Model/WysiwygDefaultConfig.php index c03629188798b..b0f7260d209ea 100644 --- a/app/code/Magento/Cms/Model/WysiwygDefaultConfig.php +++ b/app/code/Magento/Cms/Model/WysiwygDefaultConfig.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Model; class WysiwygDefaultConfig implements \Magento\Framework\Data\Wysiwyg\ConfigProviderInterface @@ -10,7 +12,7 @@ class WysiwygDefaultConfig implements \Magento\Framework\Data\Wysiwyg\ConfigProv /** * {@inheritdoc} */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { return $config; } diff --git a/app/code/Magento/Tinymce3/Model/Config/Source/Wysiwyg/Editor.php b/app/code/Magento/Tinymce3/Model/Config/Source/Wysiwyg/Editor.php index c01b036225a1a..00f1a82698381 100644 --- a/app/code/Magento/Tinymce3/Model/Config/Source/Wysiwyg/Editor.php +++ b/app/code/Magento/Tinymce3/Model/Config/Source/Wysiwyg/Editor.php @@ -3,8 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Tinymce3\Model\Config\Source\Wysiwyg; +/** + * Class Editor provides configuration value for TinyMCE3 editor + * @deprecated use as configuration value tinymce4 path: mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter + */ class Editor { /** diff --git a/app/code/Magento/Tinymce3/Model/Config/Variable/Config.php b/app/code/Magento/Tinymce3/Model/Config/Variable/Config.php index 59c3b529fdc37..2d016a5101abe 100644 --- a/app/code/Magento/Tinymce3/Model/Config/Variable/Config.php +++ b/app/code/Magento/Tinymce3/Model/Config/Variable/Config.php @@ -3,10 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); namespace Magento\Tinymce3\Model\Config\Variable; /** * Class Config adds variable plugin information required for tinymce3 editor + * @deprecated use \Magento\Variable\Model\Variable\ConfigProvider instead */ class Config implements \Magento\Framework\Data\Wysiwyg\ConfigProviderInterface { @@ -38,7 +41,7 @@ public function __construct( * @param \Magento\Framework\DataObject $config * @return \Magento\Framework\DataObject */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { $settings = $this->defaultVariableConfig->getWysiwygPluginSettings($config); $pluginConfig = isset($settings['plugins']) ? $settings['plugins'] : []; diff --git a/app/code/Magento/Tinymce3/Model/Config/Widget/Config.php b/app/code/Magento/Tinymce3/Model/Config/Widget/Config.php index 81c814681ea8c..de548df4bc9f3 100644 --- a/app/code/Magento/Tinymce3/Model/Config/Widget/Config.php +++ b/app/code/Magento/Tinymce3/Model/Config/Widget/Config.php @@ -3,10 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Tinymce3\Model\Config\Widget; /** * Class Config adds widget plugin information required for tinymce3 editor + * @deprecated use \Magento\Widget\Model\Widget\Config instead */ class Config implements \Magento\Framework\Data\Wysiwyg\ConfigProviderInterface { @@ -35,7 +38,7 @@ public function __construct( /** * {@inheritdoc} */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { $settings = [ 'widget_plugin_src' => $this->getWysiwygJsPluginSrc(), @@ -52,7 +55,7 @@ public function getConfig($config) * * @return string */ - private function getWysiwygJsPluginSrc() + private function getWysiwygJsPluginSrc() : string { $editorPluginJs = 'Magento_Tinymce3::tiny_mce/plugins/magentowidget/editor_plugin.js'; $result = $this->assetRepo->getUrl($editorPluginJs); diff --git a/app/code/Magento/Tinymce3/Model/Config/Widget/PlaceholderImagesPool.php b/app/code/Magento/Tinymce3/Model/Config/Widget/PlaceholderImagesPool.php index ba570ab12494f..1ab3de708dd26 100644 --- a/app/code/Magento/Tinymce3/Model/Config/Widget/PlaceholderImagesPool.php +++ b/app/code/Magento/Tinymce3/Model/Config/Widget/PlaceholderImagesPool.php @@ -3,11 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +declare(strict_types=1); namespace Magento\Tinymce3\Model\Config\Widget; /** * Class PlaceholderImages provide ability to override placeholder images for Widgets + * @deprecated */ class PlaceholderImagesPool { @@ -29,7 +30,7 @@ public function __construct( /** * @return array */ - public function getWidgetPlaceholders() + public function getWidgetPlaceholders() : array { return $this->widgetPlaceholders; } diff --git a/app/code/Magento/Tinymce3/Model/Config/Wysiwyg/Config.php b/app/code/Magento/Tinymce3/Model/Config/Wysiwyg/Config.php index 1e8b57996954b..f3dc4c8591cbd 100644 --- a/app/code/Magento/Tinymce3/Model/Config/Wysiwyg/Config.php +++ b/app/code/Magento/Tinymce3/Model/Config/Wysiwyg/Config.php @@ -3,10 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Tinymce3\Model\Config\Wysiwyg; /** * Class Config adds information about required css files for tinymce3 editor + * @deprecated use \Magento\Cms\Model\Wysiwyg\DefaultConfigProvider instead */ class Config implements \Magento\Framework\Data\Wysiwyg\ConfigProviderInterface { @@ -27,7 +30,7 @@ public function __construct( /** * {@inheritdoc} */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { $config->addData([ 'popup_css' => $this->assetRepo->getUrl( diff --git a/app/code/Magento/Tinymce3/Model/Plugin/Widget.php b/app/code/Magento/Tinymce3/Model/Plugin/Widget.php index 408dee97b4ed8..1cf8a67751b76 100644 --- a/app/code/Magento/Tinymce3/Model/Plugin/Widget.php +++ b/app/code/Magento/Tinymce3/Model/Plugin/Widget.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); namespace Magento\Tinymce3\Model\Plugin; use Magento\Tinymce3\Model\Config\Source\Wysiwyg\Editor; @@ -44,16 +46,16 @@ public function __construct( /** * @param \Magento\Widget\Model\Widget $subject - * @param $proceed - * @param $type + * @param \Closure $proceed + * @param string $type * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function aroundGetPlaceholderImageUrl( \Magento\Widget\Model\Widget $subject, - $proceed, - $type - ) { + \Closure $proceed, + string $type + ) : string { if ($this->activeEditor->getWysiwygAdapterPath() !== Editor::WYSIWYG_EDITOR_CONFIG_VALUE) { return $proceed($type); } diff --git a/app/code/Magento/Tinymce3/README.md b/app/code/Magento/Tinymce3/README.md index 9fb0473fadedd..8e24c218171b6 100644 --- a/app/code/Magento/Tinymce3/README.md +++ b/app/code/Magento/Tinymce3/README.md @@ -1,3 +1 @@ -This moodule provides backwards compatibility for TinyMCE3 for clients -that have modified the TinyMCE3 editor in Magento and don't want to -lose functionality. \ No newline at end of file +We have updated the TinyMCE module to the latest available version, 4.6.4. TinyMCE v4.6.4 provides backwards-compatibility for modified editor modules to prevent the loss of functionality. The TinyMCE3 module is now deprecated and will be removed in a future release. \ No newline at end of file diff --git a/app/code/Magento/Tinymce3/etc/di.xml b/app/code/Magento/Tinymce3/etc/di.xml new file mode 100644 index 0000000000000..e03d865ce4e01 --- /dev/null +++ b/app/code/Magento/Tinymce3/etc/di.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Ui\Block\Wysiwyg\ActiveEditor"> + <arguments> + <argument name="availableAdapterPaths" xsi:type="array"> + <item name="Magento_Tinymce3/tinymce3Adapter" xsi:type="string"/> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Tinymce3/view/adminhtml/web/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js b/app/code/Magento/Tinymce3/view/adminhtml/web/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js index 560a60e98bc83..f332d4c88c615 100644 --- a/app/code/Magento/Tinymce3/view/adminhtml/web/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js +++ b/app/code/Magento/Tinymce3/view/adminhtml/web/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js @@ -3,6 +3,9 @@ * See COPYING.txt for license details. */ +/** + * @deprecated use lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js instead + */ /* global tinyMCE, tinymce, MagentovariablePlugin */ /* eslint-disable strict */ tinyMCE.addI18n({ diff --git a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js index 474861a523878..88e2d4cc78f96 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js +++ b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js @@ -3,6 +3,9 @@ * See COPYING.txt for license details. */ +/** + * @deprecated use lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js instead + */ /* global varienGlobalEvents, tinyMceEditors, MediabrowserUtility, closeEditorPopup, Base64 */ /* eslint-disable strict */ define([ diff --git a/app/code/Magento/Ui/Block/Wysiwyg/ActiveEditor.php b/app/code/Magento/Ui/Block/Wysiwyg/ActiveEditor.php index ac0af00f9682d..fbb2cb4979a00 100644 --- a/app/code/Magento/Ui/Block/Wysiwyg/ActiveEditor.php +++ b/app/code/Magento/Ui/Block/Wysiwyg/ActiveEditor.php @@ -16,20 +16,38 @@ */ class ActiveEditor extends \Magento\Framework\View\Element\Template { + const DEFAULT_EDITOR_PATH = 'mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter'; /** * @var ScopeConfigInterface */ private $scopeConfig; + /** + * @var array + */ + private $availableAdapterPaths; + /** * @param Context $context * @param ScopeConfigInterface $scopeConfig * @param array $data */ - public function __construct(Context $context, ScopeConfigInterface $scopeConfig, array $data = []) - { + /** + * ActiveEditor constructor. + * @param Context $context + * @param ScopeConfigInterface $scopeConfig + * @param array $availableAdapterPaths + * @param array $data + */ + public function __construct( + Context $context, + ScopeConfigInterface $scopeConfig, + $availableAdapterPaths = [], + array $data = [] + ) { parent::__construct($context, $data); $this->scopeConfig = $scopeConfig; + $this->availableAdapterPaths = $availableAdapterPaths; } /** @@ -40,6 +58,9 @@ public function __construct(Context $context, ScopeConfigInterface $scopeConfig, public function getWysiwygAdapterPath() { $adapterPath = $this->scopeConfig->getValue(Model\Config::WYSIWYG_EDITOR_CONFIG_PATH); + if ($adapterPath !== self::DEFAULT_EDITOR_PATH && !isset($this->availableAdapterPaths[$adapterPath])) { + $adapterPath = self::DEFAULT_EDITOR_PATH; + } return $this->escapeHtml($adapterPath); } } diff --git a/app/code/Magento/Variable/Model/Variable/ConfigProvider.php b/app/code/Magento/Variable/Model/Variable/ConfigProvider.php index 00eb4531b27b7..f6fe9dd880381 100644 --- a/app/code/Magento/Variable/Model/Variable/ConfigProvider.php +++ b/app/code/Magento/Variable/Model/Variable/ConfigProvider.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Variable\Model\Variable; @@ -30,7 +31,7 @@ public function __construct(Config $variableConfig) * {@inheritdoc} * */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { $settings = $this->variableConfig->getWysiwygPluginSettings($config); return $config->addData($settings); diff --git a/app/code/Magento/Widget/Model/Widget/Config.php b/app/code/Magento/Widget/Model/Widget/Config.php index 0519f0b9d732a..4f81ef33f47f7 100644 --- a/app/code/Magento/Widget/Model/Widget/Config.php +++ b/app/code/Magento/Widget/Model/Widget/Config.php @@ -75,7 +75,7 @@ public function __construct( * @param \Magento\Framework\DataObject $config * @return \Magento\Framework\DataObject */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { $settings = $this->getPluginSettings($config); return $config->addData($settings); diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml index 08c3d42c02457..480970bd77d41 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml @@ -46,7 +46,6 @@ <element name="v4" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 4.3.6']" /> <element name="v3" type ="button" selector="//select[@id='dropdown-switcher']/option[text()='TinyMCE 3.6(Deprecated)']" /> <element name="TinymceDescription3" type ="button" selector="//span[text()='Description']" /> - <element name="Tinymce3MSG" type="button" selector=".admin__field-error"/> <element name="SaveConfig" type ="button" selector="#save" /> <element name="v4" type="button" selector="#category_form_description_v4"/> <element name="WYSIWYGBtn" type="button" selector=".//button[@class='action-default scalable action-wysiwyg']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/TinyMCESection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/TinyMCESection.xml index ed6b1dd0284f3..a158d1ed8580b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/TinyMCESection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/TinyMCESection.xml @@ -12,7 +12,6 @@ <element name="checkIfContentTabOpen" type="button" selector="//span[text()='Content']/parent::strong/parent::*[@data-state-collapsible='closed']"/> <element name="CheckIfTabExpand" type="button" selector="//div[@data-state-collapsible='closed']//span[text()='Content']"/> <element name="TinyMCE4" type="text" selector=".mce-branding-powered-by" /> - <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> <element name="InsertVariableBtn" type="button" selector=".scalable.add-variable.plugin"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Newsletter/Section/NewsletterTemplateSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Newsletter/Section/NewsletterTemplateSection.xml index 9f272d34a100a..e3be7570f87d4 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Newsletter/Section/NewsletterTemplateSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Newsletter/Section/NewsletterTemplateSection.xml @@ -19,7 +19,6 @@ <section name="NewsletterWYSIWYGSection"> <element name="TextArea" type="text" selector="#text" /> <element name="TinyMCE4" type="text" selector=".mce-branding-powered-by" /> - <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> <element name="ShowHideBtn" type="button" selector="#toggletext"/> <element name="InsertWidgetBtn" type="button" selector=".action-add-widget"/> <element name="InsertWidgetIcon" type="button" selector="div[aria-label='Insert Widget']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/LICENSE.txt b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/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/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/LICENSE_AFL.txt b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/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/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/README.md b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/README.md new file mode 100644 index 0000000000000..a9412ea9d26b8 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/README.md @@ -0,0 +1,3 @@ +# Magento 2 Functional Tests + +The Functional Tests Module for **Magento_Tinemce3** Module. diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/Section/AdminTinymce3FileldsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/Section/AdminTinymce3FileldsSection.xml new file mode 100644 index 0000000000000..1c666a9755420 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/Section/AdminTinymce3FileldsSection.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="ProductWYSIWYGSection"> + <element name="Tinymce3MSG" type="button" selector=".admin__field-error"/> + </section> + <section name="TinyMCESection"> + <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> + </section> + <section name="NewsletterWYSIWYGSection"> + <element name="TinyMCE3" type="text" selector="#cms_page_form_content_tbl"/> + </section> +</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Test/AdminSwitchWYSIWYGOptionsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/Test/AdminSwitchWYSIWYGOptionsTest.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Test/AdminSwitchWYSIWYGOptionsTest.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/Test/AdminSwitchWYSIWYGOptionsTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/composer.json b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/composer.json new file mode 100644 index 0000000000000..7bccbdd2b1b3d --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Tinymce3/composer.json @@ -0,0 +1,35 @@ +{ + "name": "magento/magento2-functional-test-module-tinymce-3", + "description": "Magento 2 Functional Test Module Theme", + "type": "magento2-test-module", + "version": "100.0.0-dev", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "config": { + "sort-packages": true + }, + "require": { + "magento/magento2-functional-testing-framework": "~2.0.0", + "php": "~7.1.3||~7.2.0" + }, + "suggest": { + "magento/magento2-functional-test-module-ui": "100.0.0-dev", + "magento/magento2-functional-test-module-variable": "100.0.0-dev", + "magento/magento2-functional-test-module-widget": "100.0.0-dev" + }, + "autoload": { + "psr-4": { + "Magento\\FunctionalTest\\Theme\\": "" + } + }, + "extra": { + "map": [ + [ + "*", + "tests/functional/Magento/FunctionalTest/Theme" + ] + ] + } +} diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php index b74c30c6eef65..7726782c49f08 100644 --- a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/Model/Config.php @@ -22,11 +22,14 @@ class Config implements \Magento\Framework\Data\Wysiwyg\ConfigProviderInterface /** * @inheritdoc */ - public function getConfig($config) + public function getConfig(\Magento\Framework\DataObject $config): \Magento\Framework\DataObject { - $config['height'] = self::CONFIG_HEIGHT; - $config['content_css'] = self::CONFIG_CONTENT_CSS; - + $config->addData( + [ + 'height' => self::CONFIG_HEIGHT, + 'content_css' => self::CONFIG_CONTENT_CSS + ] + ); return $config; } } diff --git a/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml new file mode 100644 index 0000000000000..cd9710db45b45 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleWysiwygConfig/etc/di.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Ui\Block\Wysiwyg\ActiveEditor"> + <arguments> + <argument name="availableAdapterPaths" xsi:type="array"> + <item name="testAdapter" xsi:type="string"/> + </argument> + </arguments> + </type> +</config> diff --git a/lib/internal/Magento/Framework/Data/Wysiwyg/ConfigProviderInterface.php b/lib/internal/Magento/Framework/Data/Wysiwyg/ConfigProviderInterface.php index dca806678dcb2..dc1dabd42d9e8 100644 --- a/lib/internal/Magento/Framework/Data/Wysiwyg/ConfigProviderInterface.php +++ b/lib/internal/Magento/Framework/Data/Wysiwyg/ConfigProviderInterface.php @@ -3,10 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Data\Wysiwyg; /** * Interface ConfigProviderInterface + * @api */ interface ConfigProviderInterface { @@ -14,5 +17,5 @@ interface ConfigProviderInterface * @param \Magento\Framework\DataObject $config * @return \Magento\Framework\DataObject */ - public function getConfig($config); + public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject; } From 3852a34051a3ed05b8014322043d9bf6dddde5cc Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Mon, 14 May 2018 17:28:25 -0500 Subject: [PATCH 029/333] MAGETWO-45950: Product ResourceCollection wrong Join Query --- app/code/Magento/Catalog/Model/Product.php | 13 ----- app/code/Magento/Catalog/etc/di.xml | 2 +- .../Catalog/Model/ProductRepositoryTest.php | 51 +++++++++++++++++++ 3 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index e58c9aab77665..dd4883a981deb 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -513,19 +513,6 @@ public function getStoreId() return $this->_storeManager->getStore()->getId(); } - /** - * Get collection instance - * - * @return object - * @deprecated 101.1.0 because collections should be used directly via factory - */ - public function getResourceCollection() - { - $collection = parent::getResourceCollection(); - $collection->setStoreId($this->getStoreId()); - return $collection; - } - /** * Get product url model * diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 875c3fecf37c6..df66d885e9a9d 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -864,7 +864,7 @@ <arguments> <argument name="customFilters" xsi:type="array"> <item name="category_id" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductCategoryFilter</item> - <item name="store" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductStoreFilter</item> + <item name="store_id" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductStoreFilter</item> <item name="website_id" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductWebsiteFilter</item> </argument> </arguments> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php new file mode 100644 index 0000000000000..71b15dd22b2b7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model; + +class ProductRepositoryTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Catalog\Api\ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * Sets up common objects + */ + protected function setUp() + { + $this->productRepository = \Magento\Framework\App\ObjectManager::getInstance()->create( + \Magento\Catalog\Api\ProductRepositoryInterface::class + ); + + $this->searchCriteriaBuilder = \Magento\Framework\App\ObjectManager::getInstance()->create( + \Magento\Framework\Api\SearchCriteriaBuilder::class + ); + } + + /** + * Checks filtering by store_id + * + * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/product_simple.php + */ + public function testFilterByStoreId() + { + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter('store_id', '1', 'eq') + ->create(); + $list = $this->productRepository->getList($searchCriteria); + $count = $list->getTotalCount(); + + $this->assertGreaterThanOrEqual(1, $count); + } +} From 30b4ecccb71284401b8d3255c2c56b37ad6256ce Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Tue, 15 May 2018 10:38:35 -0500 Subject: [PATCH 030/333] MAGETWO-86867: [Tech Debt] Revise LESS/CSS classes and icons --- .../testsuite/Magento/Test/Integrity/StaticFilesTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php index 9fb176de21458..2afb6e90667dc 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php @@ -10,6 +10,7 @@ /** * An integrity test that searches for references to static files and asserts that they are resolved via fallback + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class StaticFilesTest extends \PHPUnit\Framework\TestCase { @@ -55,7 +56,6 @@ class StaticFilesTest extends \PHPUnit\Framework\TestCase */ private $filesystem; - protected function setUp() { $om = \Magento\TestFramework\Helper\Bootstrap::getObjectmanager(); From 5ba8ebb54d6dfd7c1263944ee62565d6a82d06a3 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 15 May 2018 10:38:58 -0500 Subject: [PATCH 031/333] MAGETWO-45950: Product ResourceCollection wrong Join Query --- app/code/Magento/Catalog/etc/di.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index df66d885e9a9d..9f1fb020ef95a 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -864,6 +864,7 @@ <arguments> <argument name="customFilters" xsi:type="array"> <item name="category_id" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductCategoryFilter</item> + <item name="store" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductStoreFilter</item> <item name="store_id" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductStoreFilter</item> <item name="website_id" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductWebsiteFilter</item> </argument> From a92215a65b1a59b728b28ddb8e86d4ae34c35ee3 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Tue, 15 May 2018 15:30:09 -0500 Subject: [PATCH 032/333] MC-77: Customer should see basic Configurable Product details. - Added mftf test. --- .../StorefrontProductInfoMainSection.xml | 1 + ...orefrontConfigurableProductDetailsTest.xml | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductInfoMainSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductInfoMainSection.xml index bdcbb509738a4..5abc388a7d65d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductInfoMainSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductInfoMainSection.xml @@ -12,6 +12,7 @@ <element name="stock" type="input" selector=".stock.available"/> <element name="productName" type="text" selector=".base"/> <element name="productSku" type="text" selector=".product.attribute.sku>.value"/> + <element name="productPriceLabel" type="text" selector=".price-label"/> <element name="productPrice" type="text" selector="div.price-box.price-final_price"/> <element name="specialPrice" type="text" selector=".special-price"/> <element name="oldPrice" type="text" selector=".old-price"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml new file mode 100644 index 0000000000000..77ad65f8f7e12 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductDetailsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Storefront configurable product details"/> + <title value="Guest customer should see the basic configurable product details in product view"/> + <description value="Guest customer should see the basic configurable product details in product view"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-77"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Verify configurable product details in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <see userInput="{{_defaultProduct.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="seeProductSku"/> + <see userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="seeProductPriceLabel"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="seeProductStockStatus"/> + <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice"/> + <see userInput="{{colorProductAttribute.default_label}}" selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" stepKey="seeProductAttributeTitle"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="{{colorProductAttribute.default_label}}" stepKey="seeColorAttributeName1"/> + <see userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown1"/> + <see userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown2"/> + <see userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown3"/> + </test> +</tests> From d9fddb896b45c35c5545d2303fbc056668500005 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Tue, 15 May 2018 15:47:38 -0500 Subject: [PATCH 033/333] MAGETWO-90663: Error/Exception is thrown when user tries to delete product from /checkout/cart page after already been removed from mini-shopping cart - added test for the failed workflow --- .../Section/CheckoutCartProductSection.xml | 2 + .../StoreFrontRemoveItemModalSection.xml | 8 +++ .../Section/StorefrontMiniCartSection.xml | 1 + ...koutForProductsDeletedFromMiniCartTest.xml | 54 +++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartProductSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartProductSection.xml index 6e4f52f689eb4..92c10ca83a76d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartProductSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartProductSection.xml @@ -24,5 +24,7 @@ <element name="ProductOptionByNameAndAttribute" type="input" selector="//main//table[@id='shopping-cart-table']//tbody//tr[.//strong[contains(@class, 'product-item-name')]//a[contains(text(), '{{var1}}')]]//dl[@class='item-options']//dt[.='{{var2}}']/following-sibling::dd[1]" parameterized="true"/> + <element name="RemoveItem" type="button" + selector="//table[@id='shopping-cart-table']//tbody//tr[contains(@class,'item-actions')]//a[contains(@class,'action-delete')]"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml new file mode 100644 index 0000000000000..f7aecdbdd6572 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml @@ -0,0 +1,8 @@ +<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"> + <section name="StoreFrontRemoveItemModalSection"> + <element name="message" type="text" selector="aside.confirm div.modal-content"/> + <element name="ok" type="button" selector="aside.confirm .modal-footer .action-primary"/> + <element name="cancel" type="button" selector="aside.confirm .modal-footer .action-secondary"/> + </section> +</sections> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml index 8f21770491f58..bdd97130a9715 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml @@ -22,5 +22,6 @@ <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="deleteMiniCartItem" type="button" selector=".action.delete" timeout="30"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml new file mode 100644 index 0000000000000..2ec05b1914ac2 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="NoErrorCartCheckoutForProductsDeletedFromMiniCartTest"> + <annotations> + <features value="Checkout"/> + <stories value="Delete product from Storefront checkout"/> + <title value="Delete product from Checkout"/> + <description value="No error from cart should be thrown for product deleted from minicart"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-72094"/> + <group value="thampy"/> + </annotations> + <!-- Preconditions --> + <before> + <!-- Simple product is created with price = 100 --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <field key="price">100.00</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addProductToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + <!-- open the minicart --> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart1"/> + <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="editProductFromMiniCart"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart2"/> + <click selector="{{StorefrontMinicartSection.deleteMiniCartItem}}" stepKey="deleteMiniCartItem"/> + <waitForElementVisible selector="{{StoreFrontRemoveItemModalSection.message}}" stepKey="waitFortheConfirmationModal"/> + <see selector="{{StoreFrontRemoveItemModalSection.message}}" userInput="Are you sure you would like to remove this item from the shopping cart?" stepKey="seeDeleteConfirmationMessage"/> + <click selector="{{StoreFrontRemoveItemModalSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForDeleteToFinish"/> + <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteProductFromCheckoutCart"/> + <waitForPageLoad stepKey="WaitForPageLoad3"/> + <see userInput="You have no items in your shopping cart." stepKey="seeNoItemsInShoppingCart"/> + </test> +</tests> From 3c825ca7bd7c8df3b47988da8c5832ec5aaca367 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Tue, 15 May 2018 16:07:53 -0500 Subject: [PATCH 034/333] MC-92: Customer should be able to see product configuration options. - Added mftf test. --- ...orefrontConfigurableProductDetailsTest.xml | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml index 77ad65f8f7e12..1e2f65f6fef16 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="StorefrontConfigurableProductDetailsTest"> + <test name="StorefrontConfigurableProductBasicInfoTest"> <annotations> <features value="ConfigurableProduct"/> <stories value="Storefront configurable product details"/> @@ -48,4 +48,45 @@ <see userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown2"/> <see userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeInDropDown3"/> </test> + + <test name="StorefrontConfigurableProductOptionsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Storefront configurable product details"/> + <title value="Guest customer should see configurable product options in product view"/> + <description value="Guest customer should see configurable product options in product view"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-92"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Verify configurable product details in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel1"/> + <see userInput="1.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice1"/> + <selectOption userInput="{{colorProductAttribute2.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption2"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel2"/> + <see userInput="2.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice2"/> + <selectOption userInput="{{colorProductAttribute3.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption3"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel3"/> + <see userInput="3.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice3"/> + </test> </tests> From 1698e2bf7516da63580194fbe8bca9de3dcb2e14 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Tue, 15 May 2018 16:28:40 -0500 Subject: [PATCH 035/333] MC-97: Customer should be able to successfully add the product to the cart. - Added mftf test. --- ...orefrontConfigurableProductDetailsTest.xml | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml index 1e2f65f6fef16..8ce4d298fc861 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml @@ -14,7 +14,7 @@ <stories value="Storefront configurable product details"/> <title value="Guest customer should see the basic configurable product details in product view"/> <description value="Guest customer should see the basic configurable product details in product view"/> - <severity value="CRITICAL"/> + <severity value="MAJOR"/> <testCaseId value="MC-77"/> <group value="ConfigurableProduct"/> </annotations> @@ -55,7 +55,7 @@ <stories value="Storefront configurable product details"/> <title value="Guest customer should see configurable product options in product view"/> <description value="Guest customer should see configurable product options in product view"/> - <severity value="CRITICAL"/> + <severity value="MAJOR"/> <testCaseId value="MC-92"/> <group value="ConfigurableProduct"/> </annotations> @@ -89,4 +89,42 @@ <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel3"/> <see userInput="3.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice3"/> </test> + + <test name="StorefrontConfigurableProductCanAddToCartTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Storefront configurable product details"/> + <title value="Guest customer should be able to add configurable product to cart after an option is selected in product view"/> + <description value="Guest customer should be able to add configurable product to cart after an option is selected in product view"/> + <severity value="MAJOR"/> + <testCaseId value="MC-97"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Verify configurable product details in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="_defaultProduct"/> + <argument name="productCount" value="1"/> + </actionGroup> + </test> </tests> From 89558e709420f865eaab9473fd3e4326d699cbb1 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Tue, 15 May 2018 16:37:08 -0500 Subject: [PATCH 036/333] MAGETWO-90663: Error/Exception is thrown when user tries to delete product from /checkout/cart page after already been removed from mini-shopping cart - updated annotation --- .../NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml index 2ec05b1914ac2..ac29f8aa0bcc9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml @@ -15,8 +15,8 @@ <title value="Delete product from Checkout"/> <description value="No error from cart should be thrown for product deleted from minicart"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-72094"/> - <group value="thampy"/> + <testCaseId value="MAGETWO-91451"/> + <group value="checkout"/> </annotations> <!-- Preconditions --> <before> From 375132a81b95fafa4a03a17b72dbacdc90afa745 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Tue, 15 May 2018 17:01:19 -0500 Subject: [PATCH 037/333] MAGETWO-90358: Auto-generate ExtensionAttributes object --- app/code/Magento/Catalog/Model/Product.php | 8 +-- .../Api/Data/PaymentDetailsInterface.php | 2 +- .../Api/Data/FakeAddressInterface.php | 13 ++-- .../Data/FakeAttributeMetadataInterface.php | 3 +- .../Api/Data/FakeCustomerInterface.php | 10 ++-- .../Api/Data/FakeExtensibleOneInterface.php | 3 +- .../Api/Data/FakeExtensibleTwoInterface.php | 3 +- .../Api/Data/FakeRegionInterface.php | 10 ++-- .../Model/Data/FakeAddress.php | 16 ++--- .../Model/Data/FakeAttributeMetadata.php | 5 +- .../Model/Data/FakeCustomer.php | 11 ++-- .../Model/Data/FakeExtensibleOne.php | 6 +- .../Model/Data/FakeExtensibleTwo.php | 6 +- .../Model/Data/FakeRegion.php | 13 ++-- .../Model/FakeAddress.php | 16 ++--- .../Model/FakeAttributeMetadata.php | 5 +- .../Model/FakeCustomer.php | 11 ++-- .../Model/FakeRegion.php | 12 ++-- .../etc/extension_attributes.xml | 4 +- .../etc/module.xml | 10 ++++ .../registration.php | 13 ++++ .../Customer/Api/AddressRepositoryTest.php | 5 ++ .../Edit/Tab/View/PersonalInfoTest.php | 12 ++-- .../ResourceModel/AddressRepositoryTest.php | 5 ++ .../Api/AbstractExtensibleObjectTest.php | 29 ++++----- .../ExtensionAttributesGenerationTest.php | 59 +++++++++++++++++++ .../Api/ExtensionAttributesFactoryTest.php | 10 ++-- .../Magento/Framework/DataObject/CopyTest.php | 22 ++++--- .../Integrity/_files/blacklist/reference.txt | 26 ++++---- .../Api/AbstractExtensibleObject.php | 21 ++++++- .../Api/ExtensionAttributesFactory.php | 2 +- .../Model/AbstractExtensibleModel.php | 18 ++++++ 32 files changed, 268 insertions(+), 121 deletions(-) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Api/Data/FakeAddressInterface.php (85%) rename dev/tests/integration/{testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Api/Data/FakeAttributeMetadataInterface.php (94%) rename dev/tests/integration/{testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Api/Data/FakeCustomerInterface.php (83%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Api/Data/FakeExtensibleOneInterface.php (80%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Api/Data/FakeExtensibleTwoInterface.php (85%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Api/Data/FakeRegionInterface.php (72%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/Data/FakeAddress.php (85%) rename dev/tests/integration/{testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/Data/FakeAttributeMetadata.php (91%) rename dev/tests/integration/{testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/Data/FakeCustomer.php (83%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/Data/FakeExtensibleOne.php (51%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/Data/FakeExtensibleTwo.php (71%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/Data/FakeRegion.php (68%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/FakeAddress.php (85%) rename dev/tests/integration/{testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/FakeAttributeMetadata.php (91%) rename dev/tests/integration/{testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/FakeCustomer.php (83%) rename dev/tests/integration/{testsuite/Magento/Framework/Api/_files/Magento/Wonderland => _files/Magento/TestModuleExtensionAttributes}/Model/FakeRegion.php (69%) rename dev/tests/integration/{testsuite/Magento/Framework/Api => _files/Magento/TestModuleExtensionAttributes}/etc/extension_attributes.xml (97%) create mode 100644 dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/etc/module.xml create mode 100644 dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/registration.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttribute/ExtensionAttributesGenerationTest.php diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index e58c9aab77665..19289b4188fea 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -2533,13 +2533,7 @@ public function setTypeId($typeId) */ public function getExtensionAttributes() { - $extensionAttributes = $this->_getExtensionAttributes(); - if (null === $extensionAttributes) { - /** @var \Magento\Catalog\Api\Data\ProductExtensionInterface $extensionAttributes */ - $extensionAttributes = $this->extensionAttributesFactory->create(ProductInterface::class); - $this->setExtensionAttributes($extensionAttributes); - } - return $extensionAttributes; + return $this->_getExtensionAttributes(); } /** diff --git a/app/code/Magento/Checkout/Api/Data/PaymentDetailsInterface.php b/app/code/Magento/Checkout/Api/Data/PaymentDetailsInterface.php index 45e885d0dbd46..cad1c100c7e5b 100644 --- a/app/code/Magento/Checkout/Api/Data/PaymentDetailsInterface.php +++ b/app/code/Magento/Checkout/Api/Data/PaymentDetailsInterface.php @@ -9,7 +9,7 @@ * Interface PaymentDetailsInterface * @api */ -interface PaymentDetailsInterface +interface PaymentDetailsInterface extends \Magento\Framework\Api\ExtensibleDataInterface { /**#@+ * Constants defined for keys of array, makes typos less likely diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeAddressInterface.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeAddressInterface.php similarity index 85% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeAddressInterface.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeAddressInterface.php index ed085b13bc720..d792adae9b786 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeAddressInterface.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeAddressInterface.php @@ -4,8 +4,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Wonderland\Api\Data; +namespace Magento\TestModuleExtensionAttributes\Api\Data; use Magento\Framework\Api\ExtensibleDataInterface; @@ -55,14 +56,14 @@ public function getCustomerId(); /** * Get region * - * @return \Magento\Wonderland\Api\Data\FakeRegionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionInterface|null */ public function getRegion(); /** * Get region * - * @return \Magento\Wonderland\Api\Data\FakeRegionInterface[]|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionInterface[]|null */ public function getRegions(); @@ -174,17 +175,17 @@ public function isDefaultBilling(); /** * Retrieve existing extension attributes object or create a new one. * - * @return \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface|null */ public function getExtensionAttributes(); /** * Set an extension attributes object. * - * @param \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface $extensionAttributes ); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Api/Data/FakeAttributeMetadataInterface.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeAttributeMetadataInterface.php similarity index 94% rename from dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Api/Data/FakeAttributeMetadataInterface.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeAttributeMetadataInterface.php index 9f183be7e5d36..6cd7d2e212adf 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Api/Data/FakeAttributeMetadataInterface.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeAttributeMetadataInterface.php @@ -3,8 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Wonderland\Api\Data; +namespace Magento\TestModuleExtensionAttributes\Api\Data; /** * Customer attribute metadata interface. diff --git a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Api/Data/FakeCustomerInterface.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeCustomerInterface.php similarity index 83% rename from dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Api/Data/FakeCustomerInterface.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeCustomerInterface.php index 1cb6336866474..63c32869941e2 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Api/Data/FakeCustomerInterface.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeCustomerInterface.php @@ -3,7 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Wonderland\Api\Data; +declare(strict_types=1); + +namespace Magento\TestModuleExtensionAttributes\Api\Data; /** * Customer interface. @@ -98,17 +100,17 @@ public function setPrefix($prefix); /** * Retrieve existing extension attributes object or create a new one. * - * @return \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface|null */ public function getExtensionAttributes(); /** * Set an extension attributes object. * - * @param \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface $extensionAttributes ); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeExtensibleOneInterface.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeExtensibleOneInterface.php similarity index 80% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeExtensibleOneInterface.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeExtensibleOneInterface.php index 086c28b1e52af..c1178d2867e8f 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeExtensibleOneInterface.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeExtensibleOneInterface.php @@ -4,8 +4,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Wonderland\Api\Data; +namespace Magento\TestModuleExtensionAttributes\Api\Data; use Magento\Framework\Api\ExtensibleDataInterface; diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeExtensibleTwoInterface.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeExtensibleTwoInterface.php similarity index 85% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeExtensibleTwoInterface.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeExtensibleTwoInterface.php index 79adec87b9f1d..c740e3ec07bed 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeExtensibleTwoInterface.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeExtensibleTwoInterface.php @@ -4,8 +4,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Wonderland\Api\Data; +namespace Magento\TestModuleExtensionAttributes\Api\Data; use Magento\Framework\Api\ExtensibleDataInterface; diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeRegionInterface.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeRegionInterface.php similarity index 72% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeRegionInterface.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeRegionInterface.php index 20784a289360a..57a08f1fbd216 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Api/Data/FakeRegionInterface.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Api/Data/FakeRegionInterface.php @@ -4,7 +4,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Wonderland\Api\Data; +declare(strict_types=1); + +namespace Magento\TestModuleExtensionAttributes\Api\Data; use Magento\Framework\Api\ExtensibleDataInterface; @@ -45,17 +47,17 @@ public function getRegionId(); /** * Retrieve existing extension attributes object or create a new one. * - * @return \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface|null */ public function getExtensionAttributes(); /** * Set an extension attributes object. * - * @param \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface $extensionAttributes ); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeAddress.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeAddress.php similarity index 85% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeAddress.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeAddress.php index bc5413964c234..a2a177bc5b9d0 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeAddress.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeAddress.php @@ -4,10 +4,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Wonderland\Model\Data; +declare(strict_types=1); + +namespace Magento\TestModuleExtensionAttributes\Model\Data; use Magento\Framework\Api\AbstractExtensibleObject; -use Magento\Wonderland\Api\Data\FakeAddressInterface; +use Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressInterface; class FakeAddress extends AbstractExtensibleObject implements FakeAddressInterface { @@ -34,7 +36,7 @@ public function getCustomerId() /** * Get region * - * @return \Magento\Wonderland\Model\Data\FakeRegion|null + * @return \Magento\TestModuleExtensionAttributes\Model\Data\FakeRegion|null */ public function getRegion() { @@ -44,7 +46,7 @@ public function getRegion() /** * Get region * - * @return \Magento\Wonderland\Model\Data\FakeRegion[]|null + * @return \Magento\TestModuleExtensionAttributes\Model\Data\FakeRegion[]|null */ public function getRegions() { @@ -194,7 +196,7 @@ public function isDefaultShipping() /** * {@inheritdoc} * - * @return \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface|null */ public function getExtensionAttributes() { @@ -214,11 +216,11 @@ public function isDefaultBilling() /** * {@inheritdoc} * - * @param \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface $extensionAttributes ) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/Data/FakeAttributeMetadata.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeAttributeMetadata.php similarity index 91% rename from dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/Data/FakeAttributeMetadata.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeAttributeMetadata.php index 2efe9f353c4df..ea50342a01d7f 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/Data/FakeAttributeMetadata.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeAttributeMetadata.php @@ -3,14 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Wonderland\Model\Data; +namespace Magento\TestModuleExtensionAttributes\Model\Data; /** * Customer attribute metadata class. */ class FakeAttributeMetadata extends \Magento\Framework\Api\AbstractSimpleObject implements - \Magento\Wonderland\Api\Data\FakeAttributeMetadataInterface + \Magento\TestModuleExtensionAttributes\Api\Data\FakeAttributeMetadataInterface { /** * {@inheritdoc} diff --git a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/Data/FakeCustomer.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeCustomer.php similarity index 83% rename from dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/Data/FakeCustomer.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeCustomer.php index 38022ef213875..c2396374ba073 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/Data/FakeCustomer.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeCustomer.php @@ -3,15 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Wonderland\Model\Data; +namespace Magento\TestModuleExtensionAttributes\Model\Data; /** * Class Customer * */ class FakeCustomer extends \Magento\Framework\Api\AbstractExtensibleObject implements - \Magento\Wonderland\Api\Data\FakeCustomerInterface + \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerInterface { /** * Get email address @@ -121,7 +122,7 @@ public function setPrefix($prefix) /** * {@inheritdoc} * - * @return \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface|null */ public function getExtensionAttributes() { @@ -131,11 +132,11 @@ public function getExtensionAttributes() /** * {@inheritdoc} * - * @param \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface $extensionAttributes ) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeExtensibleOne.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeExtensibleOne.php similarity index 51% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeExtensibleOne.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeExtensibleOne.php index a4d5152990409..1bbd48020bcfc 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeExtensibleOne.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeExtensibleOne.php @@ -4,9 +4,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Wonderland\Model\Data; +declare(strict_types=1); -use Magento\Wonderland\Api\Data\FakeExtensibleOneInterface; +namespace Magento\TestModuleExtensionAttributes\Model\Data; + +use Magento\TestModuleExtensionAttributes\Api\Data\FakeExtensibleOneInterface; class FakeExtensibleOne implements FakeExtensibleOneInterface { diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeExtensibleTwo.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeExtensibleTwo.php similarity index 71% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeExtensibleTwo.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeExtensibleTwo.php index 70ea680162a41..3064b884616ed 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeExtensibleTwo.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeExtensibleTwo.php @@ -4,10 +4,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Wonderland\Model\Data; +declare(strict_types=1); + +namespace Magento\TestModuleExtensionAttributes\Model\Data; use Magento\Framework\Api\AbstractExtensibleObject; -use Magento\Wonderland\Api\Data\FakeExtensibleTwoInterface; +use Magento\TestModuleExtensionAttributes\Api\Data\FakeExtensibleTwoInterface; class FakeExtensibleTwo extends AbstractExtensibleObject implements FakeExtensibleTwoInterface { diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeRegion.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeRegion.php similarity index 68% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeRegion.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeRegion.php index 7ee12ce5e3a3b..ff3dbecbb8a7c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/Data/FakeRegion.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/Data/FakeRegion.php @@ -4,11 +4,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Wonderland\Model\Data; +declare(strict_types=1); +namespace Magento\TestModuleExtensionAttributes\Model\Data; + +use Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionInterface; use Magento\Framework\Api\AbstractExtensibleObject; -class FakeRegion extends AbstractExtensibleObject implements \Magento\Wonderland\Api\Data\FakeRegionInterface +class FakeRegion extends AbstractExtensibleObject implements FakeRegionInterface { /** * Get region @@ -43,7 +46,7 @@ public function getRegionId() /** * {@inheritdoc} * - * @return \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface|null */ public function getExtensionAttributes() { @@ -53,11 +56,11 @@ public function getExtensionAttributes() /** * {@inheritdoc} * - * @param \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface $extensionAttributes ) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/FakeAddress.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeAddress.php similarity index 85% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/FakeAddress.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeAddress.php index 2b657ffc25839..36c2aabd72037 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/FakeAddress.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeAddress.php @@ -4,10 +4,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Wonderland\Model; +declare(strict_types=1); + +namespace Magento\TestModuleExtensionAttributes\Model; use Magento\Framework\Model\AbstractExtensibleModel; -use Magento\Wonderland\Api\Data\FakeAddressInterface; +use Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressInterface; class FakeAddress extends AbstractExtensibleModel implements FakeAddressInterface { @@ -34,7 +36,7 @@ public function getCustomerId() /** * Get region * - * @return \Magento\Wonderland\Api\Data\FakeRegionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionInterface|null */ public function getRegion() { @@ -44,7 +46,7 @@ public function getRegion() /** * Get region * - * @return \Magento\Wonderland\Api\Data\FakeRegionInterface[]|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionInterface[]|null */ public function getRegions() { @@ -204,7 +206,7 @@ public function isDefaultBilling() /** * {@inheritdoc} * - * @return \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface|null */ public function getExtensionAttributes() { @@ -214,11 +216,11 @@ public function getExtensionAttributes() /** * {@inheritdoc} * - * @param \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeAddressExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressExtensionInterface $extensionAttributes ) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/FakeAttributeMetadata.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeAttributeMetadata.php similarity index 91% rename from dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/FakeAttributeMetadata.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeAttributeMetadata.php index 293f238b24adf..ae791de4e0943 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/FakeAttributeMetadata.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeAttributeMetadata.php @@ -3,14 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Wonderland\Api\Model; +namespace Magento\TestModuleExtensionAttributes\Api\Model; /** * Customer attribute metadata class. */ class FakeAttributeMetadata extends \Magento\Framework\Api\AbstractSimpleObject implements - \Magento\Wonderland\Api\Data\FakeAttributeMetadataInterface + \Magento\TestModuleExtensionAttributes\Api\Data\FakeAttributeMetadataInterface { /** * {@inheritdoc} diff --git a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/FakeCustomer.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeCustomer.php similarity index 83% rename from dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/FakeCustomer.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeCustomer.php index 0f533ebf83bcc..64ff041179b71 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DataObject/_files/Magento/Wonderland/Model/FakeCustomer.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeCustomer.php @@ -3,15 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\Wonderland\Api\Model; +namespace Magento\TestModuleExtensionAttributes\Api\Model; /** * Class Customer * */ class FakeCustomer extends \Magento\Framework\Api\AbstractExtensibleObject implements - \Magento\Wonderland\Api\Data\FakeCustomerInterface + \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerInterface { /** * Get customer id @@ -121,7 +122,7 @@ public function setPrefix($prefix) /** * {@inheritdoc} * - * @return \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface|null */ public function getExtensionAttributes() { @@ -131,11 +132,11 @@ public function getExtensionAttributes() /** * {@inheritdoc} * - * @param \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeCustomerExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerExtensionInterface $extensionAttributes ) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/FakeRegion.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeRegion.php similarity index 69% rename from dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/FakeRegion.php rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeRegion.php index fcadfc36deea7..aee30887fc341 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/_files/Magento/Wonderland/Model/FakeRegion.php +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/Model/FakeRegion.php @@ -4,10 +4,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Wonderland\Model; +declare(strict_types=1); + +namespace Magento\TestModuleExtensionAttributes\Model; use Magento\Framework\Model\AbstractExtensibleModel; -use Magento\Wonderland\Api\Data\FakeRegionInterface; +use Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionInterface; class FakeRegion extends AbstractExtensibleModel implements FakeRegionInterface { @@ -44,7 +46,7 @@ public function getRegionId() /** * {@inheritdoc} * - * @return \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface|null + * @return \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface|null */ public function getExtensionAttributes() { @@ -54,11 +56,11 @@ public function getExtensionAttributes() /** * {@inheritdoc} * - * @param \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface $extensionAttributes + * @param \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\Wonderland\Api\Data\FakeRegionExtensionInterface $extensionAttributes + \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtensionInterface $extensionAttributes ) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/etc/extension_attributes.xml b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/etc/extension_attributes.xml similarity index 97% rename from dev/tests/integration/testsuite/Magento/Framework/Api/etc/extension_attributes.xml rename to dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/etc/extension_attributes.xml index f8162d54c1ad1..afe722cfac11e 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/etc/extension_attributes.xml +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/etc/extension_attributes.xml @@ -4,7 +4,7 @@ * 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:Api/etc/extension_attributes.xsd"> <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> @@ -33,7 +33,7 @@ </attribute> </extension_attributes> - <extension_attributes for="Magento\Wonderland\Api\Data\FakeCustomerInterface"> + <extension_attributes for="Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerInterface"> <attribute code="test_group_code" type="string"> <join reference_table="customer_group" join_on_field="group_id" diff --git a/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/etc/module.xml new file mode 100644 index 0000000000000..8f885b1d9a2a8 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/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_TestModuleExtensionAttributes" /> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/registration.php b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/registration.php new file mode 100644 index 0000000000000..2077708c02415 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleExtensionAttributes/registration.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleExtensionAttributes') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleExtensionAttributes', __DIR__); +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Api/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Api/AddressRepositoryTest.php index be2f07ce5de62..308824db23b57 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Api/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Api/AddressRepositoryTest.php @@ -160,6 +160,11 @@ public function testSaveNewAddress() $expectedNewAddress = $this->_expectedAddresses[1]; $expectedNewAddress->setId($savedAddress->getId()); $expectedNewAddress->setRegion($this->_expectedAddresses[1]->getRegion()); + + // Call getters on extension attributes to initialize them + $savedAddress->getExtensionAttributes(); + $savedAddress->getRegion()->getExtensionAttributes(); + $this->assertEquals($expectedNewAddress, $savedAddress); } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php index 4ba1b44010c34..c0c2dbb8554b3 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php @@ -119,7 +119,11 @@ public function testGetCustomer() public function testGetCustomerEmpty() { - $this->assertEquals($this->_createCustomer(), $this->_block->getCustomer()); + $expectedCustomer = $this->createCustomerAndAddToBackendSession(); + $actualCustomer = $this->_block->getCustomer(); + // Trigger extension attributes generation + $actualCustomer->getExtensionAttributes(); + $this->assertEquals($expectedCustomer, $actualCustomer); } /** @@ -133,7 +137,7 @@ public function testGetGroupName() public function testGetGroupNameNull() { - $this->_createCustomer(); + $this->createCustomerAndAddToBackendSession(); $this->assertNull($this->_block->getGroupName()); } @@ -243,7 +247,7 @@ public function testGetBillingAddressHtml() public function testGetBillingAddressHtmlNoDefaultAddress() { - $this->_createCustomer(); + $this->createCustomerAndAddToBackendSession(); $this->assertEquals( __('The customer does not have default billing address.'), $this->_block->getBillingAddressHtml() @@ -253,7 +257,7 @@ public function testGetBillingAddressHtmlNoDefaultAddress() /** * @return \Magento\Customer\Api\Data\CustomerInterface */ - private function _createCustomer() + private function createCustomerAndAddToBackendSession() { /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ $customer = $this->_customerFactory->create()->setFirstname( diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php index b06ddf48d9e64..67c43c19c5ba4 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php @@ -166,6 +166,11 @@ public function testSaveNewAddress() $expectedNewAddress = $this->_expectedAddresses[1]; $expectedNewAddress->setId($savedAddress->getId()); $expectedNewAddress->setRegion($this->_expectedAddresses[1]->getRegion()); + + // Trigger extension attributes object generation + $savedAddress->getExtensionAttributes(); + $savedAddress->getRegion()->getExtensionAttributes(); + $this->assertEquals($expectedNewAddress, $savedAddress); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/AbstractExtensibleObjectTest.php b/dev/tests/integration/testsuite/Magento/Framework/Api/AbstractExtensibleObjectTest.php index 2aaaae9dd38f2..51e66eec676ea 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/AbstractExtensibleObjectTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Api/AbstractExtensibleObjectTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Api; +use Magento\TestModuleExtensionAttributes\Model\Data\FakeRegionFactory; +use Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtension; + /** * Test for \Magento\Framework\Api\AbstractExtensibleObject */ @@ -15,16 +18,14 @@ class AbstractExtensibleObjectTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $autoloadWrapper = \Magento\Framework\Autoload\AutoloaderRegistry::getAutoloader(); - $autoloadWrapper->addPsr4('Magento\\Wonderland\\', realpath(__DIR__ . '/_files/Magento/Wonderland')); $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->_objectManager->configure( [ 'preferences' => [ - \Magento\Wonderland\Api\Data\FakeAddressInterface::class => - \Magento\Wonderland\Model\FakeAddress::class, - \Magento\Wonderland\Api\Data\FakeRegionInterface::class => - \Magento\Wonderland\Model\FakeRegion::class, + \Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressInterface::class => + \Magento\TestModuleExtensionAttributes\Model\FakeAddress::class, + \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionInterface::class => + \Magento\TestModuleExtensionAttributes\Model\FakeRegion::class, ], ] ); @@ -41,30 +42,30 @@ public function testExtensionAttributes($expectedDataBefore, $expectedDataAfter) { /** @var \Magento\Framework\Api\ExtensionAttributesFactory $regionExtensionFactory */ $regionExtensionFactory = $this->_objectManager->get(\Magento\Framework\Api\ExtensionAttributesFactory::class); - /** @var \Magento\Wonderland\Model\Data\FakeRegionFactory $regionFactory */ - $regionFactory = $this->_objectManager->get(\Magento\Wonderland\Model\Data\FakeRegionFactory::class); + /** @var FakeRegionFactory $regionFactory */ + $regionFactory = $this->_objectManager->get(FakeRegionFactory::class); - /** @var \Magento\Wonderland\Model\Data\FakeRegion $region */ + /** @var \Magento\TestModuleExtensionAttributes\Model\Data\FakeRegion $region */ $region = $regionFactory->create(); $regionCode = 'test_code'; - /** @var \Magento\Wonderland\Model\Data\FakeRegionExtensionInterface $regionExtension */ + /** @var \Magento\TestModuleExtensionAttributes\Model\Data\FakeRegionExtensionInterface $regionExtension */ $regionExtension = $regionExtensionFactory->create( - \Magento\Wonderland\Model\Data\FakeRegion::class, + \Magento\TestModuleExtensionAttributes\Model\Data\FakeRegion::class, ['data' => $expectedDataBefore] ); $region->setRegionCode($regionCode)->setExtensionAttributes($regionExtension); - $this->assertInstanceOf(\Magento\Wonderland\Model\Data\FakeRegion::class, $region); + $this->assertInstanceOf(\Magento\TestModuleExtensionAttributes\Model\Data\FakeRegion::class, $region); $extensionAttributes = $region->getExtensionAttributes(); - $this->assertInstanceOf(\Magento\Wonderland\Api\Data\FakeRegionExtension::class, $extensionAttributes); + $this->assertInstanceOf(FakeRegionExtension::class, $extensionAttributes); $this->assertEquals($expectedDataBefore, $extensionAttributes->__toArray()); $this->assertEquals($regionCode, $region->getRegionCode()); $regionCode = 'changed_test_code'; $region->setExtensionAttributes( $regionExtensionFactory->create( - \Magento\Wonderland\Model\Data\FakeRegion::class, + \Magento\TestModuleExtensionAttributes\Model\Data\FakeRegion::class, ['data' => $expectedDataAfter] ) )->setRegionCode($regionCode); // change $regionCode to test AbstractExtensibleObject::setData diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttribute/ExtensionAttributesGenerationTest.php b/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttribute/ExtensionAttributesGenerationTest.php new file mode 100644 index 0000000000000..991998cf59167 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttribute/ExtensionAttributesGenerationTest.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\Api\ExtensionAttribute; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductExtensionInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerExtensionInterface; + +/** + * Class to test the automatic generation of extension attributes object. + */ +class ExtensionAttributesGenerationTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test extension attributes generation for extensible models. + * + * Make sure that extension attributes object is not empty after instantiation + * of objects inherited from @see \Magento\Framework\Model\AbstractExtensibleModel. + * + * In addition, verify that empty objects are not generated for complex extension attributes. + */ + public function testAttributeObjectGenerationForExtensibleModel() + { + /** @var \Magento\Framework\ObjectManagerInterface */ + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var ProductInterface $product */ + $product = $objectManager->get(ProductInterface::class); + + $extensionAttributes = $product->getExtensionAttributes(); + $this->assertInstanceOf(ProductExtensionInterface::class, $extensionAttributes); + + $stockItemExtensionAttribute = $extensionAttributes->getStockItem(); + $this->assertNull($stockItemExtensionAttribute); + } + + /** + * Test extension attributes generation for extensible objects. + * + * Make sure that extension attributes object is not empty after instantiation + * of objects inherited from @see \Magento\Framework\Api\AbstractExtensibleObject + */ + public function testAttributeObjectGenerationForExtensibleObject() + { + /** @var \Magento\Framework\ObjectManagerInterface */ + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var CustomerInterface $customer */ + $customer = $objectManager->get(CustomerInterface::class); + + $extensionAttributes = $customer->getExtensionAttributes(); + $this->assertInstanceOf(CustomerExtensionInterface::class, $extensionAttributes); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttributesFactoryTest.php b/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttributesFactoryTest.php index c0cb63e30f69d..1c86bd594c6a0 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttributesFactoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttributesFactoryTest.php @@ -12,8 +12,6 @@ class ExtensionAttributesFactoryTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $autoloadWrapper = \Magento\Framework\Autoload\AutoloaderRegistry::getAutoloader(); - $autoloadWrapper->addPsr4('Magento\\Wonderland\\', realpath(__DIR__ . '/_files/Magento/Wonderland')); /** @var \Magento\Framework\ObjectManagerInterface */ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -36,7 +34,7 @@ public function testCreateThrowExceptionIfInterfaceNotImplemented() */ public function testCreateThrowExceptionIfInterfaceNotOverridden() { - $this->factory->create(\Magento\Wonderland\Model\Data\FakeExtensibleOne::class); + $this->factory->create(\Magento\TestModuleExtensionAttributes\Model\Data\FakeExtensibleOne::class); } /** @@ -44,14 +42,14 @@ public function testCreateThrowExceptionIfInterfaceNotOverridden() */ public function testCreateThrowExceptionIfReturnIsIncorrect() { - $this->factory->create(\Magento\Wonderland\Model\Data\FakeExtensibleTwo::class); + $this->factory->create(\Magento\TestModuleExtensionAttributes\Model\Data\FakeExtensibleTwo::class); } public function testCreate() { $this->assertInstanceOf( - \Magento\Wonderland\Api\Data\FakeRegionExtension::class, - $this->factory->create(\Magento\Wonderland\Model\Data\FakeRegion::class) + \Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionExtension::class, + $this->factory->create(\Magento\TestModuleExtensionAttributes\Model\Data\FakeRegion::class) ); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/DataObject/CopyTest.php b/dev/tests/integration/testsuite/Magento/Framework/DataObject/CopyTest.php index dce6880a1dbab..e333fb4bc42cf 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DataObject/CopyTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/DataObject/CopyTest.php @@ -39,8 +39,6 @@ public function testCopyFieldset() public function testCopyFieldsetWithExtensionAttributes() { - $autoloadWrapper = \Magento\Framework\Autoload\AutoloaderRegistry::getAutoloader(); - $autoloadWrapper->addPsr4('Magento\\Wonderland\\', realpath(__DIR__ . '/_files/Magento/Wonderland')); $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $fieldsetConfigMock = $this->getMockBuilder(\Magento\Framework\DataObject\Copy\Config::class) @@ -74,20 +72,22 @@ public function testCopyFieldsetWithExtensionAttributes() /** @var \Magento\Framework\Api\DataObjectHelper $dataObjectHelper */ $dataObjectHelper = $objectManager->get(\Magento\Framework\Api\DataObjectHelper::class); - /** @var \Magento\Wonderland\Model\Data\FakeCustomerFactory $customerFactory */ - $customerFactory = $objectManager->get(\Magento\Wonderland\Model\Data\FakeCustomerFactory::class); - /** @var \Magento\Wonderland\Api\Data\CustomerInterface $source */ + /** @var \Magento\TestModuleExtensionAttributes\Model\Data\FakeCustomerFactory $customerFactory */ + $customerFactory = $objectManager->get( + \Magento\TestModuleExtensionAttributes\Model\Data\FakeCustomerFactory::class + ); + /** @var \Magento\TestModuleExtensionAttributes\Api\Data\CustomerInterface $source */ $source = $customerFactory->create(); $dataObjectHelper->populateWithArray( $source, $dataWithExtraField, - \Magento\Wonderland\Api\Data\FakeCustomerInterface::class + \Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerInterface::class ); - /** @var \Magento\Wonderland\Api\Data\CustomerInterface $target */ + /** @var \Magento\TestModuleExtensionAttributes\Api\Data\CustomerInterface $target */ $target = $customerFactory->create(); $target = $service->copyFieldsetToTarget($fieldset, $aspect, $source, $target); - $this->assertInstanceOf(\Magento\Wonderland\Api\Data\FakeCustomerInterface::class, $target); + $this->assertInstanceOf(\Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerInterface::class, $target); $this->assertNull( $target->getEmail(), "Email should not be set because it is not defined in the fieldset." @@ -106,8 +106,6 @@ public function testCopyFieldsetWithExtensionAttributes() public function testCopyFieldsetWithAbstractSimpleObject() { - $autoloadWrapper = \Magento\Framework\Autoload\AutoloaderRegistry::getAutoloader(); - $autoloadWrapper->addPsr4('Magento\\Wonderland\\', realpath(__DIR__ . '/_files/Magento/Wonderland')); $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $fieldset = 'sales_copy_order'; @@ -130,13 +128,13 @@ public function testCopyFieldsetWithAbstractSimpleObject() ->method('getFieldSet') ->willReturn($data); - $source = $objectManager->get(\Magento\Wonderland\Model\Data\FakeAttributeMetadata::class); + $source = $objectManager->get(\Magento\TestModuleExtensionAttributes\Model\Data\FakeAttributeMetadata::class); $source->setStoreLabel('storeLabel'); $source->setFrontendLabel('frontendLabel'); $source->setAttributeCode('attributeCode'); $source->setNote('note'); - $target = $objectManager->get(\Magento\Wonderland\Model\Data\FakeAttributeMetadata::class); + $target = $objectManager->get(\Magento\TestModuleExtensionAttributes\Model\Data\FakeAttributeMetadata::class); $expectedTarget = $source; $this->assertEquals( diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/reference.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/reference.txt index 2348a3b974b20..7f454d246d1bf 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/reference.txt +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/reference.txt @@ -30,18 +30,18 @@ TestSuite DbTest Model1 Model3 -\Magento\Wonderland\Api\Data\FakeRegionInterface -\Magento\Wonderland\Api\Data\FakeAddressInterface -\Magento\Wonderland\Api\Data\FakeExtensibleOneInterface -\Magento\Wonderland\Api\Data\FakeExtensibleTwoInterface -\Magento\Wonderland\Api\Data\FakeAttributeMetadataInterface -\Magento\Wonderland\Api\Data\FakeCustomerInterface -\Magento\Wonderland\Model\Data\FakeRegion -\Magento\Wonderland\Model\Data\FakeAddress -\Magento\Wonderland\Model\Data\FakeAttributeMetadata -\Magento\Wonderland\Model\Data\FakeCustomer -\Magento\Wonderland\Model\Data\FakeExtensibleOne -\Magento\Wonderland\Model\Data\FakeExtensibleTwo +\Magento\TestModuleExtensionAttributes\Api\Data\FakeRegionInterface +\Magento\TestModuleExtensionAttributes\Api\Data\FakeAddressInterface +\Magento\TestModuleExtensionAttributes\Api\Data\FakeExtensibleOneInterface +\Magento\TestModuleExtensionAttributes\Api\Data\FakeExtensibleTwoInterface +\Magento\TestModuleExtensionAttributes\Api\Data\FakeAttributeMetadataInterface +\Magento\TestModuleExtensionAttributes\Api\Data\FakeCustomerInterface +\Magento\TestModuleExtensionAttributes\Model\Data\FakeRegion +\Magento\TestModuleExtensionAttributes\Model\Data\FakeAddress +\Magento\TestModuleExtensionAttributes\Model\Data\FakeAttributeMetadata +\Magento\TestModuleExtensionAttributes\Model\Data\FakeCustomer +\Magento\TestModuleExtensionAttributes\Model\Data\FakeExtensibleOne +\Magento\TestModuleExtensionAttributes\Model\Data\FakeExtensibleTwo \Magento\Framework\Error\Processor \Magento\TestModule3\Service\V1\Entity\Parameter \Magento\TestModule3\Service\V1\Entity\ParameterBuilder @@ -120,4 +120,4 @@ DoubleColon \Magento\Mtf\Client\ElementInterface BarFactory PartialNamespace\BarFactory -Product\OptionFactory \ No newline at end of file +Product\OptionFactory diff --git a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php index 3ea43cbccd24f..7b2b94cbd0973 100644 --- a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php @@ -9,9 +9,8 @@ /** * Base Class for extensible data Objects - * @SuppressWarnings(PHPMD.NumberOfChildren) - * TODO: This class can be split into Custom attribute and Extension attribute implementation classes * + * @SuppressWarnings(PHPMD.NumberOfChildren) * @api */ abstract class AbstractExtensibleObject extends AbstractSimpleObject implements CustomAttributesDataInterface @@ -51,6 +50,9 @@ public function __construct( $this->extensionFactory = $extensionFactory; $this->attributeValueFactory = $attributeValueFactory; parent::__construct($data); + if (isset($data[self::EXTENSION_ATTRIBUTES_KEY]) && is_array($data[self::EXTENSION_ATTRIBUTES_KEY])) { + $this->populateExtensionAttributes($data[self::EXTENSION_ATTRIBUTES_KEY]); + } } /** @@ -160,9 +162,24 @@ protected function getEavAttributesCodes(\Magento\Framework\Api\MetadataServiceI */ protected function _getExtensionAttributes() { + if (!$this->_get(self::EXTENSION_ATTRIBUTES_KEY)) { + $this->populateExtensionAttributes([]); + } return $this->_get(self::EXTENSION_ATTRIBUTES_KEY); } + /** + * Instantiate extension attributes object and populate it with the provided data. + * + * @param array $extensionAttributesData + * @return void + */ + private function populateExtensionAttributes(array $extensionAttributesData = []) + { + $extensionAttributes = $this->extensionFactory->create(get_class($this), $extensionAttributesData); + $this->_setExtensionAttributes($extensionAttributes); + } + /** * Set an extension attributes object. * diff --git a/lib/internal/Magento/Framework/Api/ExtensionAttributesFactory.php b/lib/internal/Magento/Framework/Api/ExtensionAttributesFactory.php index 01c09a160aece..2d5d4f751f5a7 100644 --- a/lib/internal/Magento/Framework/Api/ExtensionAttributesFactory.php +++ b/lib/internal/Magento/Framework/Api/ExtensionAttributesFactory.php @@ -42,7 +42,7 @@ public function __construct(\Magento\Framework\ObjectManagerInterface $objectMan * * @param string $extensibleClassName * @param array $data - * @return object + * @return \Magento\Framework\Api\ExtensionAttributesInterface */ public function create($extensibleClassName, $data = []) { diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php index db919c80e251c..e7ffcde03ff64 100644 --- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php +++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php @@ -69,6 +69,9 @@ public function __construct( if (isset($data['id'])) { $this->setId($data['id']); } + if (isset($data[self::EXTENSION_ATTRIBUTES_KEY]) && is_array($data[self::EXTENSION_ATTRIBUTES_KEY])) { + $this->populateExtensionAttributes($data[self::EXTENSION_ATTRIBUTES_KEY]); + } } /** @@ -338,9 +341,24 @@ protected function _setExtensionAttributes(\Magento\Framework\Api\ExtensionAttri */ protected function _getExtensionAttributes() { + if (!$this->getData(self::EXTENSION_ATTRIBUTES_KEY)) { + $this->populateExtensionAttributes([]); + } return $this->getData(self::EXTENSION_ATTRIBUTES_KEY); } + /** + * Instantiate extension attributes object and populate it with the provided data. + * + * @param array $extensionAttributesData + * @return void + */ + private function populateExtensionAttributes(array $extensionAttributesData = []) + { + $extensionAttributes = $this->extensionAttributesFactory->create(get_class($this), $extensionAttributesData); + $this->_setExtensionAttributes($extensionAttributes); + } + /** * @inheritdoc */ From d6c15d6210203483ff2f9ae35edf184803a82288 Mon Sep 17 00:00:00 2001 From: austris argalis <austris.argalis@gmail.com> Date: Sun, 6 May 2018 20:13:45 +0300 Subject: [PATCH 038/333] Return full canonical product URL within product object --- .../Model/Resolver/Product/CanonicalUrl.php | 67 ++++++++++++++ .../Test/Integration/Model/ProductsTest.php | 73 +++++++++++++++ .../Resolver/Product/CanonicalUrlTest.php | 92 +++++++++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 1 + 4 files changed, 233 insertions(+) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php create mode 100644 app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php create mode 100644 app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Product/CanonicalUrlTest.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php new file mode 100644 index 0000000000000..071a5ec2fab89 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php @@ -0,0 +1,67 @@ +<?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\Product; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +class CanonicalUrl implements ResolverInterface +{ + /** + * @var ValueFactory + */ + private $valueFactory; + + /** + * @param ValueFactory $valueFactory + */ + public function __construct( + ValueFactory $valueFactory + ) { + $this->valueFactory = $valueFactory; + } + + /** + * Fetches the data from persistence models and format it according to the GraphQL schema. + * + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return Value + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ): Value { + if (!isset($value['model'])) { + $result = function () { + return null; + }; + return $this->valueFactory->create($result); + } + + /* @var $product Product */ + $product = $value['model']; + $url = $product->getUrlModel()->getUrl($product, ['_ignore_category' => true]); + $result = function () use ($url) { + return $url; + }; + + return $this->valueFactory->create($result); + } +} diff --git a/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php b/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php new file mode 100644 index 0000000000000..29c9f33d74718 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Test\Integration\Model; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\GraphQl\Controller\GraphQl as GraphQlController; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +class ProductsTest extends TestCase +{ + /** + * @var SerializerInterface + */ + private $jsonSerializer; + /** + * @var ObjectManager + */ + private $objectManager; + /** + * @var GraphQlController + */ + private $graphql; + + protected function setUp() : void + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); + $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); + } + + /** + * @magentoAppArea graphql + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php + * @magentoDbIsolation disabled + * @magentoConfigFixture default_store catalog/seo/product_url_suffix .html + */ + public function testResponseContainsCanonicalURLs(): void + { + $fixtureSku = 'p002'; + $query = <<<QUERY + { + products(filter: {sku: {eq: "$fixtureSku"}}) + { + items { + sku + canonical_url + } + } +} +QUERY; + $postData = ['query' => $query]; + $request = $this->objectManager->get(HttpRequest::class); + $request->setPathInfo('/graphql'); + $request->setContent(json_encode($postData)); + $headers = $this->objectManager->create(\Zend\Http\Headers::class) + ->addHeaders(['Content-Type' => 'application/json']); + $request->setHeaders($headers); + + $response = $this->graphql->dispatch($request); + $output = $this->jsonSerializer->unserialize($response->getContent()); + $canonical_url = $output['data']['products']['items'][0]['canonical_url']; + + $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); + $this->assertEquals('http://localhost/index.php/' . $fixtureSku . '.html', $canonical_url); + } +} diff --git a/app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Product/CanonicalUrlTest.php b/app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Product/CanonicalUrlTest.php new file mode 100644 index 0000000000000..ae01c67eb5224 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Product/CanonicalUrlTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Test\Unit\Model\Resolver\Product; + +use Magento\CatalogGraphQl\Model\Resolver\Product\CanonicalUrl; +use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; + +class CanonicalUrlTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CanonicalUrl + */ + private $subject; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $mockValueFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $mockStoreManager; + + public function testReturnsNullWhenNoProductAvailable() + { + $mockField = $this->getMockBuilder(\Magento\Framework\GraphQl\Config\Element\Field::class) + ->disableOriginalConstructor() + ->getMock(); + $mockInfo = $this->getMockBuilder(\Magento\Framework\GraphQl\Schema\Type\ResolveInfo::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->mockValueFactory->method('create')->with( + $this->callback( + function ($param) { + return $param() === null; + } + ) + ); + + $this->subject->resolve($mockField, '', $mockInfo, [], []); + } + + protected function setUp() + { + parent::setUp(); + $this->objectManager = new ObjectManager($this); + $this->mockStoreManager = $this->getMockBuilder(StoreManagerInterface::class)->getMock(); + $this->mockValueFactory = $this->getMockBuilder(ValueFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->mockValueFactory->method('create')->willReturn( + $this->objectManager->getObject( + Value::class, + ['callback' => function () { + return ''; + }] + ) + ); + + $mockProductUrlPathGenerator = $this->getMockBuilder(ProductUrlPathGenerator::class) + ->disableOriginalConstructor() + ->getMock(); + $mockProductUrlPathGenerator->method('getUrlPathWithSuffix')->willReturn('product_url.html'); + + $this->subject = $this->objectManager->getObject( + CanonicalUrl::class, + [ + 'valueFactory' => $this->mockValueFactory, + 'storeManager' => $this->mockStoreManager, + 'productUrlPathGenerator' => $mockProductUrlPathGenerator + ] + ); + } +} diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index ca1ff78654319..e56f1dc3ff1ad 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -279,6 +279,7 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ 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\\Category") + canonical_url: String @doc(description: "Canonical URL") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\CanonicalUrl") } interface PhysicalProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "PhysicalProductInterface contains attributes specific to tangible products") { From 94fb609769e406c0a8b22a21431cecb7b076b5fe Mon Sep 17 00:00:00 2001 From: austris argalis <austris.argalis@gmail.com> Date: Mon, 14 May 2018 19:47:11 +0300 Subject: [PATCH 039/333] Correct docblocks, add api-functional test --- .../Model/Resolver/Product/CanonicalUrl.php | 13 +--- .../Test/Integration/Model/ProductsTest.php | 73 ------------------- .../GraphQl/Catalog/ProductViewTest.php | 5 ++ 3 files changed, 9 insertions(+), 82 deletions(-) delete mode 100644 app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php index 071a5ec2fab89..d2675848c2d2a 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php @@ -14,6 +14,9 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +/** + * Resolve data for product canonical URL + */ class CanonicalUrl implements ResolverInterface { /** @@ -31,15 +34,7 @@ public function __construct( } /** - * Fetches the data from persistence models and format it according to the GraphQL schema. - * - * @param \Magento\Framework\GraphQl\Config\Element\Field $field - * @param $context - * @param ResolveInfo $info - * @param array|null $value - * @param array|null $args - * @throws \Exception - * @return Value + * {@inheritdoc} */ public function resolve( Field $field, diff --git a/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php b/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php deleted file mode 100644 index 29c9f33d74718..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogGraphQl\Test\Integration\Model; - -use Magento\Framework\App\Request\Http as HttpRequest; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\GraphQl\Controller\GraphQl as GraphQlController; -use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\TestCase; - -class ProductsTest extends TestCase -{ - /** - * @var SerializerInterface - */ - private $jsonSerializer; - /** - * @var ObjectManager - */ - private $objectManager; - /** - * @var GraphQlController - */ - private $graphql; - - protected function setUp() : void - { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - } - - /** - * @magentoAppArea graphql - * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php - * @magentoDbIsolation disabled - * @magentoConfigFixture default_store catalog/seo/product_url_suffix .html - */ - public function testResponseContainsCanonicalURLs(): void - { - $fixtureSku = 'p002'; - $query = <<<QUERY - { - products(filter: {sku: {eq: "$fixtureSku"}}) - { - items { - sku - canonical_url - } - } -} -QUERY; - $postData = ['query' => $query]; - $request = $this->objectManager->get(HttpRequest::class); - $request->setPathInfo('/graphql'); - $request->setContent(json_encode($postData)); - $headers = $this->objectManager->create(\Zend\Http\Headers::class) - ->addHeaders(['Content-Type' => 'application/json']); - $request->setHeaders($headers); - - $response = $this->graphql->dispatch($request); - $output = $this->jsonSerializer->unserialize($response->getContent()); - $canonical_url = $output['data']['products']['items'][0]['canonical_url']; - - $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); - $this->assertEquals('http://localhost/index.php/' . $fixtureSku . '.html', $canonical_url); - } -} 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 a4c7f41df0e99..54d363ea6c1e6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -227,6 +227,7 @@ public function testQueryAllFieldsSimpleProduct() updated_at url_key url_path + canonical_url websites { id name code sort_order default_group_id is_default } ... on PhysicalProductInterface { weight @@ -272,6 +273,10 @@ public function testQueryAllFieldsSimpleProduct() 'Filter category', $responseObject->getData('products/items/0/categories/2/name') ); + self::assertEquals( + $product->getUrlModel()->getUrl($product, ['_ignore_category' => true, '_nosid' => true]), + $responseObject->getData('products/items/0/canonical_url') + ); } /** From a73491cf830af90fdf0ef9805c1af462c20a4754 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 16 May 2018 12:15:16 +0300 Subject: [PATCH 040/333] MAGETWO-90350: Admin Section is not visible in backend on production mode - GitHub issue #14104 --- app/code/Magento/Backend/etc/adminhtml/di.xml | 3 -- .../ConcealInProductionConfigList.php | 40 ++------------- .../ConcealInProductionConfigListTest.php | 10 +--- .../Deploy/App/Mode/ConfigProvider.php | 4 +- app/code/Magento/Deploy/Model/Mode.php | 10 ++-- .../Deploy/Test/Unit/Model/ModeTest.php | 4 +- app/code/Magento/Deploy/etc/di.xml | 28 +---------- .../Developer/etc/adminhtml/system.xml | 1 + .../Backend/Test/Block/System/Config/Form.php | 50 ++++--------------- .../AssertDeveloperSectionVisibility.php | 44 +++------------- .../Model/Logger/Handler/DebugTest.php | 1 + 11 files changed, 36 insertions(+), 159 deletions(-) diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index 1276033b33056..e886e21ef4313 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -145,9 +145,6 @@ <item name="dev" xsi:type="const">Magento\Config\Model\Config\Structure\ElementVisibilityInterface::HIDDEN</item> <item name="general/locale/code" xsi:type="const">Magento\Config\Model\Config\Structure\ElementVisibilityInterface::DISABLED</item> </argument> - <argument name="exemptions" xsi:type="array"> - <item name="dev/debug/debug_logging" xsi:type="string"/> - </argument> </arguments> </type> <type name="Magento\Backend\Model\Search\Config\Result\Builder"> diff --git a/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php b/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php index 92bc61b3d65e5..a5c1708cd6c49 100644 --- a/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php +++ b/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php @@ -42,36 +42,14 @@ class ConcealInProductionConfigList implements ElementVisibilityInterface */ private $state; - /** - * - * The list of form element paths which ignore visibility status. - * - * E.g. - * - * ```php - * [ - * 'general/country/default' => '', - * ]; - * ``` - * - * It means that: - * - field 'default' in group Country Options (in section General) will be showed, even if all group(section) - * will be hidden. - * - * @var array - */ - private $exemptions = []; - /** * @param State $state The object that has information about the state of the system * @param array $configs The list of form element paths with concrete visibility status. - * @param array $exemptions The list of form element paths which ignore visibility status. */ - public function __construct(State $state, array $configs = [], array $exemptions = []) + public function __construct(State $state, array $configs = []) { $this->state = $state; $this->configs = $configs; - $this->exemptions = $exemptions; } /** @@ -80,21 +58,11 @@ public function __construct(State $state, array $configs = [], array $exemptions */ public function isHidden($path) { - $result = false; $path = $this->normalizePath($path); - if ($this->state->getMode() === State::MODE_PRODUCTION - && preg_match('/(?<group>(?<section>.*?)\/.*?)\/.*?/', $path, $match)) { - $group = $match['group']; - $section = $match['section']; - $exemptions = array_keys($this->exemptions); - foreach ($this->configs as $configPath => $value) { - if ($value === static::HIDDEN && strpos($path, $configPath) !==false) { - $result = empty(array_intersect([$section, $group, $path], $exemptions)); - } - } - } - return $result; + return $this->state->getMode() === State::MODE_PRODUCTION + && !empty($this->configs[$path]) + && $this->configs[$path] === static::HIDDEN; } /** diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php index fa78d5dde652c..5cad923264e00 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php @@ -33,13 +33,9 @@ protected function setUp() 'third/path' => 'no', 'third/path/field' => ConcealInProductionConfigList::DISABLED, 'first/path/field' => 'no', - 'fourth' => ConcealInProductionConfigList::HIDDEN, - ]; - $exemptions = [ - 'fourth/path/value' => '', ]; - $this->model = new ConcealInProductionConfigList($this->stateMock, $configs, $exemptions); + $this->model = new ConcealInProductionConfigList($this->stateMock, $configs); } /** @@ -100,10 +96,8 @@ public function hiddenDataProvider() ['first/path', State::MODE_PRODUCTION, false], ['first/path', State::MODE_DEFAULT, false], ['some/path', State::MODE_PRODUCTION, false], - ['second/path/field', State::MODE_PRODUCTION, true], + ['second/path', State::MODE_PRODUCTION, true], ['second/path', State::MODE_DEVELOPER, false], - ['fourth/path/value', State::MODE_PRODUCTION, false], - ['fourth/path/test', State::MODE_PRODUCTION, true], ]; } } diff --git a/app/code/Magento/Deploy/App/Mode/ConfigProvider.php b/app/code/Magento/Deploy/App/Mode/ConfigProvider.php index 900908a1f158f..142e3fe819438 100644 --- a/app/code/Magento/Deploy/App/Mode/ConfigProvider.php +++ b/app/code/Magento/Deploy/App/Mode/ConfigProvider.php @@ -16,7 +16,7 @@ class ConfigProvider * [ * 'developer' => [ * 'production' => [ - * {{setting_path}} => ['value' => {{setting_value}}, 'lock' => {{lock_value}}] + * {{setting_path}} => {{setting_value}} * ] * ] * ] @@ -41,7 +41,7 @@ public function __construct(array $config = []) * need to turn off 'dev/debug/debug_logging' setting in this case method * will return array * [ - * {{setting_path}} => ['value' => {{setting_value}}, 'lock' => {{lock_value}}] + * {{setting_path}} => {{setting_value}} * ] * * @param string $currentMode diff --git a/app/code/Magento/Deploy/Model/Mode.php b/app/code/Magento/Deploy/Model/Mode.php index d4ae72d63bce7..fe24fb297e978 100644 --- a/app/code/Magento/Deploy/Model/Mode.php +++ b/app/code/Magento/Deploy/Model/Mode.php @@ -234,17 +234,17 @@ protected function setStoreMode($mode) private function saveAppConfigs($mode) { $configs = $this->configProvider->getConfigs($this->getMode(), $mode); - foreach ($configs as $path => $item) { - $this->emulatedAreaProcessor->process(function () use ($path, $item) { + foreach ($configs as $path => $value) { + $this->emulatedAreaProcessor->process(function () use ($path, $value) { $this->processorFacadeFactory->create()->processWithLockTarget( $path, - $item['value'], + $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, - $item['lock'] + true ); }); - $this->output->writeln('Config "' . $path . ' = ' . $item['value'] . '" has been saved.'); + $this->output->writeln('Config "' . $path . ' = ' . $value . '" has been saved.'); } } } diff --git a/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php b/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php index 50725a3382073..5cb8a8e47bc97 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/ModeTest.php @@ -228,7 +228,7 @@ public function testEnableProductionModeMinimal() ->method('getConfigs') ->with('developer', 'production') ->willReturn([ - 'dev/debug/debug_logging' => ['value' => 0, 'lock' => false] + 'dev/debug/debug_logging' => 0, ]); $this->emulatedAreaProcessor->expects($this->once()) ->method('process') @@ -247,7 +247,7 @@ public function testEnableProductionModeMinimal() 0, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null, - false + true ); $this->outputMock->expects($this->once()) ->method('writeln') diff --git a/app/code/Magento/Deploy/etc/di.xml b/app/code/Magento/Deploy/etc/di.xml index 9e936f70c9986..e47fca3a6b946 100644 --- a/app/code/Magento/Deploy/etc/di.xml +++ b/app/code/Magento/Deploy/etc/di.xml @@ -31,7 +31,6 @@ <item name="dumpApplicationCommand" xsi:type="object">\Magento\Deploy\Console\Command\App\ApplicationDumpCommand</item> <item name="sensitiveConfigSetCommand" xsi:type="object">\Magento\Deploy\Console\Command\App\SensitiveConfigSetCommand</item> <item name="configImportCommand" xsi:type="object">Magento\Deploy\Console\Command\App\ConfigImportCommand</item> - <item name="configStatusCommand" xsi:type="object">Magento\Deploy\Console\Command\App\ConfigStatusCommand</item> </argument> </arguments> </type> @@ -76,32 +75,7 @@ <argument name="config" xsi:type="array"> <item name="developer" xsi:type="array"> <item name="production" xsi:type="array"> - <item name="dev/debug/debug_logging" xsi:type="array"> - <item name="value" xsi:type="string">0</item> - <item name="lock" xsi:type="boolean">false</item> - </item> - </item> - </item> - <item name="production" xsi:type="array"> - <item name="developer" xsi:type="array"> - <item name="dev/debug/debug_logging" xsi:type="array"> - <item name="value" xsi:type="string">1</item> - <item name="lock" xsi:type="boolean">false</item> - </item> - </item> - </item> - <item name="default" xsi:type="array"> - <item name="production" xsi:type="array"> - <item name="dev/debug/debug_logging" xsi:type="array"> - <item name="value" xsi:type="string">0</item> - <item name="lock" xsi:type="boolean">false</item> - </item> - </item> - <item name="developer" xsi:type="array"> - <item name="dev/debug/debug_logging" xsi:type="array"> - <item name="value" xsi:type="string">1</item> - <item name="lock" xsi:type="boolean">false</item> - </item> + <item name="dev/debug/debug_logging" xsi:type="string">0</item> </item> </item> </argument> diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index 0166814d889c2..9663cff72bc9d 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -28,6 +28,7 @@ <group id="debug" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <field id="debug_logging" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Log to File</label> + <comment>Not available in production mode.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php index 31fadc2cf4f85..61a39a441c973 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Config/Form.php @@ -60,7 +60,17 @@ class Form extends Block */ public function getGroup($tabName, $groupName) { - $this->openTab($tabName); + $this->baseUrl = $this->getBrowserUrl(); + if (substr($this->baseUrl, -1) !== '/') { + $this->baseUrl = $this->baseUrl . '/'; + } + + $tabUrl = $this->getTabUrl($tabName); + + if ($this->getBrowserUrl() !== $tabUrl) { + $this->browser->open($tabUrl); + } + $this->waitForElementNotVisible($this->tabReadiness); $groupElement = $this->_rootElement->find( sprintf($this->groupBlock, $tabName, $groupName), @@ -85,24 +95,6 @@ public function getGroup($tabName, $groupName) return $blockFactory->getMagentoBackendSystemConfigFormGroup($groupElement); } - /** - * Check whether specified group presented on page. - * - * @param string $tabName - * @param string $groupName - * - * @return bool - */ - public function isGroupVisible(string $tabName, string $groupName) - { - $this->openTab($tabName); - - return $this->_rootElement->find( - sprintf($this->groupBlockLink, $tabName, $groupName), - Locator::SELECTOR_CSS - )->isVisible(); - } - /** * Retrieve url associated with the form. */ @@ -145,24 +137,4 @@ private function getTabUrl($tabName) return $tabUrl; } - - /** - * Open specified tab. - * - * @param string $tabName - * @return void - */ - private function openTab(string $tabName) - { - $this->baseUrl = $this->getBrowserUrl(); - if (substr($this->baseUrl, -1) !== '/') { - $this->baseUrl = $this->baseUrl . '/'; - } - $tabUrl = $this->getTabUrl($tabName); - - if ($this->getBrowserUrl() !== $tabUrl) { - $this->browser->open($tabUrl); - } - $this->waitForElementNotVisible($this->tabReadiness); - } } diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php index 010b3a58134aa..4cff4b2a90c17 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertDeveloperSectionVisibility.php @@ -9,57 +9,27 @@ use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit; /** - * Assert that all groups in Developer section is not present in production mode except debug group "Log to File" field. + * Assert that Developer section is not present in production mode. */ class AssertDeveloperSectionVisibility extends AbstractConstraint { /** - * List of groups not visible in production mode. - * - * @var array - */ - private $groups = [ - 'front_end_development_workflow', - 'restrict', - 'template', - 'translate_inline', - 'js', - 'css', - 'image', - 'static', - 'grid', - ]; - - /** - * Assert all groups in Developer section is not present in production mode except debug group "Log to File" field. + * Assert Developer section is not present in production mode. * * @param SystemConfigEdit $configEdit * @return void */ public function processAssert(SystemConfigEdit $configEdit) { - $configEdit->open(); if ($_ENV['mage_mode'] === 'production') { - foreach ($this->groups as $group) { - \PHPUnit\Framework\Assert::assertFalse( - $configEdit->getForm()->isGroupVisible('dev', $group), - sprintf('%s group should be hidden in production mode.', $group) - ); - } - \PHPUnit\Framework\Assert::assertTrue( - $configEdit->getForm()->getGroup('dev', 'debug')->isFieldVisible('dev', 'debug_debug', 'logging'), - '"Log to File" should be presented in production mode.' + \PHPUnit\Framework\Assert::assertFalse( + in_array('Developer', $configEdit->getTabs()->getSubTabsNames('Advanced')), + 'Developer section should be hidden in production mode.' ); } else { - foreach ($this->groups as $group) { - \PHPUnit\Framework\Assert::assertTrue( - $configEdit->getForm()->isGroupVisible('dev', $group), - sprintf('%s group should be visible in developer mode.', $group) - ); - } \PHPUnit\Framework\Assert::assertTrue( - $configEdit->getForm()->isGroupVisible('dev', 'debug'), - 'Debug group should be visible in developer mode.' + in_array('Developer', $configEdit->getTabs()->getSubTabsNames('Advanced')), + 'Developer section should be not hidden in developer or default mode.' ); } } diff --git a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php index 3b006b5044337..3bef48d8801f7 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php @@ -95,6 +95,7 @@ public function setUp() // Preconditions $this->mode->enableDeveloperMode(); + $this->enableDebugging(); if (file_exists($this->getDebuggerLogPath())) { unlink($this->getDebuggerLogPath()); } From ac921c7a803aa5ee6e439f1fd76022b9f0de4b3f Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Wed, 16 May 2018 13:05:49 +0300 Subject: [PATCH 041/333] Update ServiceInputProcessor.php Enable WebAPI interface to handle parameters through constructor #14801 - New optional param should be last in the params list. --- .../Magento/Framework/Webapi/ServiceInputProcessor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index 7f40937e98ac9..5293e7ee1306b 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -89,8 +89,8 @@ public function __construct( AttributeValueFactory $attributeValueFactory, CustomAttributeTypeLocatorInterface $customAttributeTypeLocator, MethodsMap $methodsMap, - ConfigInterface $config = null, - ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap = null + ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap = null, + ConfigInterface $config = null ) { $this->typeProcessor = $typeProcessor; $this->objectManager = $objectManager; From 4426cdb5bf0ef1a3460520f2e0e6e911906c6125 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Wed, 16 May 2018 13:07:04 +0300 Subject: [PATCH 042/333] Enable WebAPI interface --- lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index 5293e7ee1306b..26102f008c7c3 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -80,8 +80,8 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface * @param AttributeValueFactory $attributeValueFactory * @param CustomAttributeTypeLocatorInterface $customAttributeTypeLocator * @param MethodsMap $methodsMap - * @param ConfigInterface $config * @param ServiceTypeToEntityTypeMap $serviceTypeToEntityTypeMap + * @param ConfigInterface $config */ public function __construct( TypeProcessor $typeProcessor, From 770d8bae159c7fdfc9a35a17b1b66e988acc9e2f Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Wed, 16 May 2018 12:46:16 +0200 Subject: [PATCH 043/333] [fix] Property shippingAddress has unknown class Magento\InstantPurchase\Model\AddressIn as its type. --- .../Magento/InstantPurchase/Model/InstantPurchaseOption.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/InstantPurchase/Model/InstantPurchaseOption.php b/app/code/Magento/InstantPurchase/Model/InstantPurchaseOption.php index 214b93560669f..29ec874c41a1a 100644 --- a/app/code/Magento/InstantPurchase/Model/InstantPurchaseOption.php +++ b/app/code/Magento/InstantPurchase/Model/InstantPurchaseOption.php @@ -25,7 +25,7 @@ class InstantPurchaseOption private $paymentToken; /** - * @var AddressIn + * @var Address */ private $shippingAddress; From e03c9a3c5cc63b299eb0a93c80550534e6a5d1f6 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Wed, 16 May 2018 12:50:35 +0200 Subject: [PATCH 044/333] [fix] accept null for properties --- .../InstantPurchase/Model/InstantPurchaseOption.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/InstantPurchase/Model/InstantPurchaseOption.php b/app/code/Magento/InstantPurchase/Model/InstantPurchaseOption.php index 29ec874c41a1a..0748c5818c857 100644 --- a/app/code/Magento/InstantPurchase/Model/InstantPurchaseOption.php +++ b/app/code/Magento/InstantPurchase/Model/InstantPurchaseOption.php @@ -20,22 +20,22 @@ class InstantPurchaseOption { /** - * @var PaymentTokenInterface + * @var PaymentTokenInterface|null */ private $paymentToken; /** - * @var Address + * @var Address|null */ private $shippingAddress; /** - * @var Address + * @var Address|null */ private $billingAddress; /** - * @var ShippingMethodInterface + * @var ShippingMethodInterface|null */ private $shippingMethod; From 31a098ba81be3bd3413cf845d47316e42ac51daa Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Wed, 16 May 2018 12:54:00 +0200 Subject: [PATCH 045/333] [fix] PHPDoc tag param $addressId had none type hint --- .../Model/InstantPurchaseOptionLoadingFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/InstantPurchase/Model/InstantPurchaseOptionLoadingFactory.php b/app/code/Magento/InstantPurchase/Model/InstantPurchaseOptionLoadingFactory.php index d1dc71b80d5d1..b203cfdad2221 100644 --- a/app/code/Magento/InstantPurchase/Model/InstantPurchaseOptionLoadingFactory.php +++ b/app/code/Magento/InstantPurchase/Model/InstantPurchaseOptionLoadingFactory.php @@ -100,7 +100,7 @@ public function create( /** * Loads customer address model by identifier. * - * @param $addressId + * @param int $addressId * @return Address */ private function getAddress($addressId): Address From 57e3180950fa26128edbdfe763d2469add1ec5d2 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Wed, 16 May 2018 13:37:10 +0200 Subject: [PATCH 046/333] [fix] return type in InvalidArgumentException instead of fixed value chooser --- .../ShippingMethodChoose/DeferredShippingMethodChooserPool.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/InstantPurchase/Model/ShippingMethodChoose/DeferredShippingMethodChooserPool.php b/app/code/Magento/InstantPurchase/Model/ShippingMethodChoose/DeferredShippingMethodChooserPool.php index 96c01cdbb6663..ca0e9351967ad 100644 --- a/app/code/Magento/InstantPurchase/Model/ShippingMethodChoose/DeferredShippingMethodChooserPool.php +++ b/app/code/Magento/InstantPurchase/Model/ShippingMethodChoose/DeferredShippingMethodChooserPool.php @@ -39,7 +39,7 @@ public function get($type) : DeferredShippingMethodChooserInterface { if (!isset($this->choosers[$type])) { throw new \InvalidArgumentException(sprintf( - 'Deferred shipping method chooser is not registered.', + 'Deferred shipping method %s is not registered.', $type )); } From 08508fae913796e516618ba2c9043495b16ef7a8 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Wed, 16 May 2018 14:24:50 +0200 Subject: [PATCH 047/333] [fix] missing PHPDoc tag param $storeId --- .../PaymentMethodIntegration/IntegrationsManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationsManager.php b/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationsManager.php index 9c93febe0db36..3ad2e000e97d3 100644 --- a/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationsManager.php +++ b/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationsManager.php @@ -146,7 +146,7 @@ private function findIntegrations(int $storeId): array * </instant_purchase> * * @param VaultPaymentInterface $paymentMethod - * @param $storeId + * @param int|string|null|\Magento\Store\Model\Store $storeId * @return bool */ private function isIntegrationAvailable(VaultPaymentInterface $paymentMethod, $storeId): bool From 028e67d32bc3dfc36d7cccd8c84ffbcfdc6bc4cb Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 16 May 2018 16:32:20 +0300 Subject: [PATCH 048/333] MAGETWO-89411: [2.3] Failed Magento\Test\Integrity\ExceptionHierarchyTest::testIsInheritedLocalizedException --- .../Test/Integrity/ExceptionHierarchyTest.php | 113 ------------------ 1 file changed, 113 deletions(-) delete mode 100644 dev/tests/static/testsuite/Magento/Test/Integrity/ExceptionHierarchyTest.php diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ExceptionHierarchyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ExceptionHierarchyTest.php deleted file mode 100644 index 7556ab2c1b47c..0000000000000 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ExceptionHierarchyTest.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Test\Integrity; - -use Magento\Framework\App\Utility\Files as UtilityFiles; - -/** - * Checks that all Exceptions inherit LocalizedException - */ -class ExceptionHierarchyTest extends \PHPUnit\Framework\TestCase -{ - /** - * @param \ReflectionClass $reflectionException - * @dataProvider isInheritedLocalizedExceptionDataProvider - */ - public function testIsInheritedLocalizedException(\ReflectionClass $reflectionException) - { - $this->assertTrue( - $reflectionException->isSubclassOf(\Magento\Framework\Exception\LocalizedException::class), - "{$reflectionException->name} is not inherited LocalizedException" - ); - } - - /** - * @return array - */ - public function isInheritedLocalizedExceptionDataProvider() - { - $files = UtilityFiles::init()->getPhpFiles(UtilityFiles::INCLUDE_APP_CODE | UtilityFiles::INCLUDE_LIBS); - $blacklistExceptions = $this->getBlacklistExceptions(); - - $data = []; - foreach ($files as $file) { - $className = $this->convertPathToClassName($file); - try { - $reflectionClass = new \ReflectionClass($className); - if ($reflectionClass->isSubclassOf('Exception') && !in_array($className, $blacklistExceptions)) { - $data[$className] = [$reflectionClass]; - } - } catch (\Exception $e) { - $this->fail("File name and class name '{$className}' is not appropriate"); - } - } - return $data; - } - - /** - * @param string $filePath - * @return string - */ - protected function convertPathToClassName($filePath) - { - $componentRegistrar = new \Magento\Framework\Component\ComponentRegistrar(); - $foundItems = null; - $moduleNamespace = null; - $foundItems = array_filter( - $componentRegistrar->getPaths(\Magento\Framework\Component\ComponentRegistrar::MODULE), - function ($item) use ($filePath) { - if (strpos($filePath, $item . '/') !== false) { - return true; - } else { - return false; - } - } - ); - if ($foundItems) { - $moduleNamespace = str_replace('_', '\\', array_keys($foundItems)[0]); - $classPath = str_replace('/', '\\', str_replace(array_shift($foundItems), '', $filePath)); - } else { - $foundItems = array_filter( - $componentRegistrar->getPaths(\Magento\Framework\Component\ComponentRegistrar::LIBRARY), - function ($item) use ($filePath) { - if (strpos($filePath, $item . '/') !== false) { - return true; - } else { - return false; - } - } - ); - $libName = array_keys($foundItems)[0]; - $libName = str_replace('framework-', 'framework/', $libName); - $namespaceParts = explode('/', $libName); - $namespaceParts = array_map( - function ($item) { - return str_replace(' ', '', ucwords(str_replace('-', ' ', $item))); - }, - $namespaceParts - ); - $moduleNamespace = implode('\\', $namespaceParts); - $classPath = str_replace('/', '\\', str_replace(array_shift($foundItems), '', $filePath)); - } - - $className = '\\' . $moduleNamespace . $classPath; - $className = str_replace('.php', '', $className); - return $className; - } - - /** - * @return array - */ - protected function getBlacklistExceptions() - { - $blacklistFiles = str_replace('\\', '/', realpath(__DIR__)) . '/_files/blacklist/exception_hierarchy*.txt'; - $exceptions = []; - foreach (glob($blacklistFiles) as $fileName) { - $exceptions = array_merge($exceptions, file($fileName, FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)); - } - return $exceptions; - } -} From ed368b0f061a84d20a83e102d7e0cfe71dc10d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 16 May 2018 15:47:35 +0200 Subject: [PATCH 049/333] Remove duplicated phpdoc comment Fix class variable phpdoc comment Remove deprecated private method getSerializer, injected in constructor instead --- .../Model/Customer/NotificationStorage.php | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Customer/Model/Customer/NotificationStorage.php b/app/code/Magento/Customer/Model/Customer/NotificationStorage.php index 7054324851f34..11e0b9b916559 100644 --- a/app/code/Magento/Customer/Model/Customer/NotificationStorage.php +++ b/app/code/Magento/Customer/Model/Customer/NotificationStorage.php @@ -5,6 +5,7 @@ */ namespace Magento\Customer\Model\Customer; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Cache\FrontendInterface; use Magento\Framework\Serialize\SerializerInterface; @@ -18,21 +19,21 @@ class NotificationStorage private $cache; /** - * @param FrontendInterface $cache - */ - - /** - * @param FrontendInterface $cache + * @var SerializerInterface */ private $serializer; /** * NotificationStorage constructor. * @param FrontendInterface $cache + * @param SerializerInterface $serializer */ - public function __construct(FrontendInterface $cache) - { + public function __construct( + FrontendInterface $cache, + SerializerInterface $serializer = null + ) { $this->cache = $cache; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); } /** @@ -45,7 +46,7 @@ public function __construct(FrontendInterface $cache) public function add($notificationType, $customerId) { $this->cache->save( - $this->getSerializer()->serialize([ + $this->serializer->serialize([ 'customer_id' => $customerId, 'notification_type' => $notificationType ]), @@ -88,19 +89,4 @@ private function getCacheKey($notificationType, $customerId) { return 'notification_' . $notificationType . '_' . $customerId; } - - /** - * Get serializer - * - * @return SerializerInterface - * @deprecated 100.2.0 - */ - private function getSerializer() - { - if ($this->serializer === null) { - $this->serializer = \Magento\Framework\App\ObjectManager::getInstance() - ->get(SerializerInterface::class); - } - return $this->serializer; - } } From 69ed26a0cecf93d47d53b90feb6df80379177b32 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Wed, 16 May 2018 09:50:02 -0500 Subject: [PATCH 050/333] MC-81: Customer should not be able to add the product to the cart if options are not selected. - Added mftf test. --- .../StorefrontProductInfoMainSection.xml | 1 + ...orefrontConfigurableProductDetailsTest.xml | 39 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/StorefrontProductInfoMainSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/StorefrontProductInfoMainSection.xml index c5da83d9ac6ed..cebb76e68a6cc 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/StorefrontProductInfoMainSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/StorefrontProductInfoMainSection.xml @@ -13,5 +13,6 @@ <element name="productAttributeTitle1" type="text" selector="#product-options-wrapper div[tabindex='0'] label"/> <element name="productAttributeOptions1" type="select" selector="#product-options-wrapper div[tabindex='0'] option"/> <element name="productAttributeOptionsSelectButton" type="select" selector="#product-options-wrapper .super-attribute-select"/> + <element name="productAttributeOptionsError" type="text" selector="//div[@class='mage-error']"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml index 8ce4d298fc861..790606fbf59eb 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml @@ -75,7 +75,7 @@ <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> - <!-- Verify configurable product details in storefront product view --> + <!-- Verify configurable product options in storefront product view --> <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> <waitForPageLoad stepKey="wait"/> <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> @@ -116,7 +116,7 @@ <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> - <!-- Verify configurable product details in storefront product view --> + <!-- Verify adding configurable product to cart after an option is selected in storefront product view --> <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> <waitForPageLoad stepKey="wait"/> <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> @@ -127,4 +127,39 @@ <argument name="productCount" value="1"/> </actionGroup> </test> + + <test name="StorefrontConfigurableProductCantAddToCartTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Storefront configurable product details"/> + <title value="Guest customer should not be able to add configurable product to cart if no option is selected in product view"/> + <description value="Guest customer should not be able to add configurable product to cart if no option is selected in product view"/> + <severity value="MAJOR"/> + <testCaseId value="MC-81"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Verify not able to add configurable product to cart when no option is selected in storefront product view --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> + <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart" /> + <see userInput="This is a required field" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsError}}" stepKey="seeError"/> + </test> </tests> From 05af6255fd8cd8677e3083f937d5c89aff81e8d4 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Wed, 16 May 2018 11:55:59 -0500 Subject: [PATCH 051/333] MC-113: Admin should be able to add and remove images for simple products. - Updated story annotation. --- .../Bundle/Test/AdminAddRemoveProductImageBundleProductTest.xml | 2 +- .../Test/AdminAddRemoveProductImageSimpleProductTest.xml | 2 +- .../Test/AdminAddRemoveProductImageVirtualProductTest.xml | 2 +- .../Test/AdminAddRemoveProductImageConfigurableTest.xml | 2 +- .../Test/AdminAddRemoveProductImageDownloadableProductTest.xml | 2 +- .../Test/AdminAddRemoveProductImageGroupedProductTest.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddRemoveProductImageBundleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddRemoveProductImageBundleProductTest.xml index d39b633bedcba..39c2ec33b9b3a 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddRemoveProductImageBundleProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddRemoveProductImageBundleProductTest.xml @@ -11,7 +11,7 @@ <test name="AdminAddRemoveProductImageBundleProductTest"> <annotations> <features value="Bundle"/> - <stories value="Bundle Product Add/Remove Images"/> + <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add/remove images for a Bundle Product"/> <description value="Admin should be able to add/remove images for a Bundle Product"/> <severity value="MAJOR"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageSimpleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageSimpleProductTest.xml index f3ff2d1b3f635..23911e88002d1 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageSimpleProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageSimpleProductTest.xml @@ -11,7 +11,7 @@ <test name="AdminAddRemoveProductImageSimpleProductTest"> <annotations> <features value="Catalog"/> - <stories value="Simple Product Add/Remove Images"/> + <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add/remove images for a Simple Product"/> <description value="Admin should be able to add/remove images for a Simple Product"/> <severity value="MAJOR"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageVirtualProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageVirtualProductTest.xml index 360b1b4d95819..d6f51108c3d66 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageVirtualProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageVirtualProductTest.xml @@ -11,7 +11,7 @@ <test name="AdminAddRemoveProductImageVirtualProductTest"> <annotations> <features value="Catalog"/> - <stories value="Virtual Product Add/Remove Images"/> + <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add/remove images for a Virtual Product"/> <description value="Admin should be able to add/remove images for a Virtual Product"/> <severity value="MAJOR"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddRemoveProductImageConfigurableTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddRemoveProductImageConfigurableTest.xml index 52dc1bf0b5390..efaecc34df7a2 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddRemoveProductImageConfigurableTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddRemoveProductImageConfigurableTest.xml @@ -11,7 +11,7 @@ <test name="AdminAddRemoveProductImageConfigurableTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Configurable Product Add/Remove Images"/> + <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add/remove images for a Configurable Product"/> <description value="Admin should be able to add/remove images for a Configurable Product"/> <severity value="MAJOR"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddRemoveProductImageDownloadableProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddRemoveProductImageDownloadableProductTest.xml index 6b086a3d4050b..7417786046f8d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddRemoveProductImageDownloadableProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddRemoveProductImageDownloadableProductTest.xml @@ -11,7 +11,7 @@ <test name="AdminAddRemoveProductImageDownloadableProductTest"> <annotations> <features value="Downloadable"/> - <stories value="Downloadable Product Add/Remove Images"/> + <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add/remove images for a Downloadable Product"/> <description value="Admin should be able to add/remove images for a Downloadable Product"/> <severity value="MAJOR"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddRemoveProductImageGroupedProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddRemoveProductImageGroupedProductTest.xml index 84ecec485db6d..f76f0cb7e3798 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddRemoveProductImageGroupedProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddRemoveProductImageGroupedProductTest.xml @@ -11,7 +11,7 @@ <test name="AdminAddRemoveProductImageGroupedProductTest"> <annotations> <features value="GroupedProduct"/> - <stories value="Grouped Product Add/Remove Images"/> + <stories value="Add/remove images and videos for all product types and category"/> <title value="Admin should be able to add/remove images for a Grouped Product"/> <description value="Admin should be able to add/remove images for a Grouped Product"/> <severity value="MAJOR"/> From 37b1835277124dc718192825396f992e6393535f Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Mon, 7 May 2018 10:59:07 -0500 Subject: [PATCH 052/333] MAGETWO-71425: Product list configuration with Magento Condition Tree - Added supporting file - Added JS unit tests --- .../web/conditions-data-normalizer.js | 111 ++++++++++++++++++ .../web/conditions-data-normalizer.test.js | 65 ++++++++++ 2 files changed, 176 insertions(+) create mode 100644 app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js diff --git a/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js b/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js new file mode 100644 index 0000000000000..5a9ee9b4f68d8 --- /dev/null +++ b/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js @@ -0,0 +1,111 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'underscore' +], function ($, _) { + var ConditionsDataNormalizer = new Class.create(); + + ConditionsDataNormalizer.prototype = { + initialize: function () { + this.patterns = { + validate: /^[a-z0-9_-][a-z0-9_-]*(?:\[(?:\d*|[a-z0-9_-]+)\])*$/i, + key: /[a-z0-9_-]+|(?=\[\])/gi, + push: /^$/, + fixed: /^\d+$/, + named: /^[a-z0-9_-]+$/i + }; + }, + + /** + * Will convert an object: + * { + * "foo[bar][1][baz]": 123, + * "foo[bar][1][blah]": 321 + * "foo[bar][1--1][ah]": 456 + * } + * + * to + * { + * "foo": { + * "bar": { + * "1": { + * "baz": 123, + * "blah": 321 + * }, + * "1--1": { + * "ah": 456 + * } + * } + * } + * } + * + * + */ + normalize: function normalize(value) { + var _this = this; + + this.pushes = {}; + this.data = {}; + + _.each(value, function (e, i) { + var el = {}; + el[i] = e; + + _this._addPair({ + name: i, + value: e + }); + }); + + return this.data; + }, + + _build: function build(base, key, value) { + base[key] = value; + + return base; + }, + + _makeObject: function makeObject(root, value) { + var keys = root.match(this.patterns.key), + k; // nest, nest, ..., nest + + while ((k = keys.pop()) !== undefined) { + // foo[] + if (this.patterns.push.test(k)) { + var idx = this._incrementPush(root.replace(/\[\]$/, '')); + value = this._build([], idx, value); + } // foo[n] + else if (this.patterns.fixed.test(k)) { + value = this._build({}, k, value); + } // foo; foo[bar] + else if (this.patterns.named.test(k)) { + value = this._build({}, k, value); + } + } + + return value; + }, + + _incrementPush: function incrementPush(key) { + if (this.pushes[key] === undefined) { + this.pushes[key] = 0; + } + + return this.pushes[key]++; + }, + + _addPair: function addPair(pair) { + if (!this.patterns.validate.test(pair.name)) return this; + var obj = this._makeObject(pair.name, pair.value); + this.data = $.extend(true, this.data, obj); + return this; + } + }; + + return ConditionsDataNormalizer; +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js new file mode 100644 index 0000000000000..e2307e05e8101 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js @@ -0,0 +1,65 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_Rule/conditions-data-normalizer' +], function ($, Normalizer) { + 'use strict'; + + describe('Magento_Rule/conditions-data-normalizer', function () { + var normalizer; + + beforeEach(function () { + normalizer = new Normalizer(); + }); + + it('Check for empty object when input is a falsey value', function () { + expect(normalizer.normalize('')).toEqual({}); + expect(normalizer.normalize()).toEqual({}); + expect(normalizer.normalize(false)).toEqual({}); + expect(normalizer.normalize(null)).toEqual({}); + expect(normalizer.normalize(0)).toEqual({}); + }); + + it('Check single level normalization.', function () { + var normal = normalizer.normalize({ + foo: 'bar', + bar: 123 + }); + expect(normal.foo).toEqual('bar'); + expect(normal.bar).toEqual(123); + }); + + it('Check one sub-level of normalization.', function () { + var normal = normalizer.normalize({ + 'foo[value]': 'bar', + 'foo[name]': 123 + }); + expect(normal.foo.value).toEqual('bar'); + expect(normal.foo.name).toEqual(123); + }); + + it('Check two sub-levels of normalization.', function () { + var normal = normalizer.normalize({ + 'foo[prefix][value]': 'bar', + 'foo[prefix][name]': 123 + }); + expect(normal.foo.prefix.value).toEqual('bar'); + expect(normal.foo.prefix.name).toEqual(123); + }); + + it('Check that numeric types don\'t get converted to array form.', function () { + var normal = normalizer.normalize({ + 'foo[1][name]': 'bar', + 'foo[1][value]': 123, + 'foo[1--1]': 321 + }); + expect(normal.foo['1'].name).toEqual('bar'); + expect(normal.foo['1'].value).toEqual(123); + expect(normal.foo['1--1']).toEqual(321); + }); + }); +}); From 9035d2951561dad0760f2bd3d6732b6da7422db0 Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras <zbigniew.kuras@gate-software.com> Date: Wed, 16 May 2018 20:06:37 +0200 Subject: [PATCH 053/333] GraphQL - Created separate resolver for sort_fields --- .../Resolver/Category/SortFieldDefault.php | 64 +++++++++++++++++++ .../Resolver/Category/SortFieldsOptions.php | 61 ++++++++++++++++++ .../Model/Resolver/Products.php | 33 +--------- app/code/Magento/GraphQl/etc/schema.graphqls | 4 +- 4 files changed, 130 insertions(+), 32 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldDefault.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldsOptions.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldDefault.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldDefault.php new file mode 100644 index 0000000000000..37b9d5f9c5e0b --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldDefault.php @@ -0,0 +1,64 @@ +<?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\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Query\ResolverInterface; + +/** + * Retrieves the sort field default value + */ +class SortFieldDefault implements ResolverInterface +{ + /** + * @var ValueFactory + */ + private $valueFactory; + + /** + * @var \Magento\Catalog\Model\Config + */ + private $catalogConfig; + + /** + * @var \Magento\Store\Model\StoreManagerInterface + */ + private $storeManager; + + /** + * @param ValueFactory $valueFactory + * @param \Magento\Catalog\Model\Config $catalogConfig + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + */ + public function __construct( + ValueFactory $valueFactory, + \Magento\Catalog\Model\Config $catalogConfig, + \Magento\Store\Model\StoreManagerInterface $storeManager + ) { + $this->valueFactory = $valueFactory; + $this->catalogConfig = $catalogConfig; + $this->storeManager = $storeManager; + } + + /** + * {@inheritDoc} + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) : Value + { + $sortFieldDefault = $this->catalogConfig->getProductListDefaultSortBy($this->storeManager->getStore()->getId()); + + $result = function () use ($sortFieldDefault) { + return $sortFieldDefault; + }; + + return $this->valueFactory->create($result); + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldsOptions.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldsOptions.php new file mode 100644 index 0000000000000..b644fa10a21c8 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldsOptions.php @@ -0,0 +1,61 @@ +<?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\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Query\ResolverInterface; + +/** + * Retrieves the sort fields options information object + */ +class SortFieldsOptions implements ResolverInterface +{ + /** + * @var ValueFactory + */ + private $valueFactory; + + /** + * @var \Magento\Catalog\Model\Config + */ + private $catalogConfig; + + /** + * @param ValueFactory $valueFactory + * @param \Magento\Catalog\Model\Config $catalogConfig + */ + public function __construct( + ValueFactory $valueFactory, + \Magento\Catalog\Model\Config $catalogConfig + ) { + $this->valueFactory = $valueFactory; + $this->catalogConfig = $catalogConfig; + } + + /** + * {@inheritDoc} + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) : Value + { + $sortFieldsOptions = [ + ['key' => 'position', 'label' => 'Position'] + ]; + foreach ($this->catalogConfig->getAttributesUsedForSortBy() as $attribute) { + $sortFieldsOptions[] = ['key' => $attribute->getAttributeCode(), 'label' => $attribute->getStoreLabel()]; + } + + $result = function () use ($sortFieldsOptions) { + return $sortFieldsOptions; + }; + + return $this->valueFactory->create($result); + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php index 02a5284f74c8b..f696c52cf6848 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php @@ -53,24 +53,12 @@ class Products implements ResolverInterface * @var Layer\DataProvider\Filters */ private $filtersDataProvider; - - /** - * @var \Magento\Catalog\Model\Config - */ - private $catalogConfig; - - /** - * @var \Magento\Store\Model\StoreManagerInterface - */ - private $storeManager; /** * @param Builder $searchCriteriaBuilder * @param Search $searchQuery * @param Filter $filterQuery * @param ValueFactory $valueFactory - * @param \Magento\Catalog\Model\Config $catalogConfig - * @param \Magento\Store\Model\StoreManagerInterface $storeManager */ public function __construct( Builder $searchCriteriaBuilder, @@ -78,9 +66,7 @@ public function __construct( Filter $filterQuery, SearchFilter $searchFilter, ValueFactory $valueFactory, - \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider, - \Magento\Catalog\Model\Config $catalogConfig, - \Magento\Store\Model\StoreManagerInterface $storeManager + \Magento\CatalogGraphQl\Model\Resolver\Layer\DataProvider\Filters $filtersDataProvider ) { $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->searchQuery = $searchQuery; @@ -88,8 +74,6 @@ public function __construct( $this->searchFilter = $searchFilter; $this->valueFactory = $valueFactory; $this->filtersDataProvider = $filtersDataProvider; - $this->catalogConfig = $catalogConfig; - $this->storeManager = $storeManager; } /** @@ -134,25 +118,14 @@ public function resolve( ); } - $options = $this->catalogConfig->getAttributeUsedForSortByArray(); - - $sortFields = [ - 'default' => $this->catalogConfig->getProductListDefaultSortBy($this->storeManager->getStore()->getId()), - 'options' => [] - ]; - - $sortFields['options'][] = ['key' => 'position', 'label' => 'Position']; - foreach ($this->catalogConfig->getAttributesUsedForSortBy() as $attribute) { - $sortFields['options'][] = ['key' => $attribute->getAttributeCode(), 'label' => $attribute->getStoreLabel()]; - } - + $data = [ 'total_count' => $searchResult->getTotalCount(), 'items' => $searchResult->getProductsSearchResult(), 'page_info' => [ 'page_size' => $searchCriteria->getPageSize(), 'current_page' => $currentPage, - 'sort_fields' => $sortFields, + 'sort_fields' => [], ], 'filters' => $this->filtersDataProvider->getData($layerType), diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index 4e6bb1cc32efa..0c6d41a3051e2 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -39,6 +39,6 @@ type SortField { } type SortFields @doc(description: "SortFields contains a default value for sort fields and all available sort fields") { - default: String @doc(description: "Default value of sort fields") - options: [SortField] @doc(description: "Available sort fields") + default: String @doc(description: "Default value of sort fields") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFieldDefault") + options: [SortField] @doc(description: "Available sort fields") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFieldsOptions") } \ No newline at end of file From d804babe4d2c5cf71e0c46b72221f566df24978b Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras <zbigniew.kuras@gate-software.com> Date: Wed, 16 May 2018 21:04:35 +0200 Subject: [PATCH 054/333] GraphQL - Removed unnecessary empty lines. --- app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php index f696c52cf6848..70c3f1c00b27f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php @@ -118,7 +118,6 @@ public function resolve( ); } - $data = [ 'total_count' => $searchResult->getTotalCount(), 'items' => $searchResult->getProductsSearchResult(), @@ -128,7 +127,6 @@ public function resolve( 'sort_fields' => [], ], 'filters' => $this->filtersDataProvider->getData($layerType), - ]; $result = function () use ($data) { From f03e784b52a23e8d66f14570a10df5da2cbbf59d Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 16 May 2018 14:32:04 -0500 Subject: [PATCH 055/333] MAGETWO-91575: Print page of compare page is not correctly aligned - creating a class to hide from printing needed elements --- .../view/frontend/templates/product/compare/list.phtml | 6 +++--- app/design/frontend/Magento/blank/web/css/print.less | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) 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 6a2dd1f27d4a9..f1d4b32031e49 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 @@ -10,7 +10,7 @@ ?> <?php $_total = $block->getItems()->getSize() ?> <?php if ($_total): ?> - <a href="#" class="action print" title="<?= /* @escapeNotVerified */ __('Print This Page') ?>"> + <a href="#" class="action print hidden-print" title="<?= /* @escapeNotVerified */ __('Print This Page') ?>"> <span><?= /* @escapeNotVerified */ __('Print This Page') ?></span> </a> <div class="table-wrapper comparison"> @@ -29,7 +29,7 @@ <?php if ($_i++ == 0): ?> <th scope="row" class="cell label remove"><span><?= /* @escapeNotVerified */ __('Remove Product') ?></span></th> <?php endif; ?> - <td class="cell remove product"> + <td class="cell remove product hidden-print"> <?php $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare');?> <a href="#" data-post='<?= /* @escapeNotVerified */ $compareHelper->getPostDataRemove($_item) ?>' class="action delete" title="<?= /* @escapeNotVerified */ __('Remove Product') ?>"> @@ -59,7 +59,7 @@ </strong> <?= $block->getReviewsSummaryHtml($_item, 'short') ?> <?= /* @escapeNotVerified */ $block->getProductPrice($_item, '-compare-list-top') ?> - <div class="product-item-actions"> + <div class="product-item-actions hidden-print"> <div class="actions-primary"> <?php if ($_item->isSaleable()): ?> <form data-role="tocart-form" action="<?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Product\Compare')->getAddToCartUrl($_item) ?>" method="post"> diff --git a/app/design/frontend/Magento/blank/web/css/print.less b/app/design/frontend/Magento/blank/web/css/print.less index 474bc27b0751b..ebfc6ce4300b7 100644 --- a/app/design/frontend/Magento/blank/web/css/print.less +++ b/app/design/frontend/Magento/blank/web/css/print.less @@ -132,4 +132,8 @@ .footer.content { padding: 0; } + + .hidden-print { + display: none !important; + } } From 366e0b78a0b9c557d1506b97aaf4541df7f45aba Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras <zbigniew.kuras@gate-software.com> Date: Wed, 16 May 2018 22:09:51 +0200 Subject: [PATCH 056/333] GraphQL - added functional test, removed unnecessary empty lines --- .../Model/Resolver/UrlRewrite.php | 4 +- .../GraphQl/UrlRewrite/UrlResolverTest.php | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 1cef3770d78fa..8ec3874d9095b 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -15,6 +15,7 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\Store\Model\ScopeInterface; +use Magento\Cms\Helper\Page; /** * UrlRewrite field resolver, used for GraphQL request processing. @@ -41,7 +42,6 @@ class UrlRewrite implements ResolverInterface */ private $scopeConfig; - /** * @param UrlFinderInterface $urlFinder * @param StoreManagerInterface $storeManager @@ -80,7 +80,7 @@ public function resolve( $url = ltrim($url, '/'); } else if ($url === '/') { $homePageIdentifier = $this->scopeConfig->getValue( - \Magento\Cms\Helper\Page::XML_PATH_HOME_PAGE, + Page::XML_PATH_HOME_PAGE, ScopeInterface::SCOPE_STORE ); $url = $homePageIdentifier; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php index 8adc2428ad8e7..97c682b3fe0cf 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php @@ -272,4 +272,46 @@ public function testInvalidUrlResolverInput() $this->assertArrayHasKey('urlResolver', $response); $this->assertNull($response['urlResolver']); } + + /** + * Test for category entity with leading slash + */ + public function testCategoryUrlWithLeadingSlash() + { + $productSku = 'p002'; + $urlPath2 = 'cat-1.html'; + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $product = $productRepository->get($productSku, false, null, true); + $storeId = $product->getStoreId(); + + /** @var UrlFinderInterface $urlFinder */ + $urlFinder = $this->objectManager->get(UrlFinderInterface::class); + $actualUrls = $urlFinder->findOneByData( + [ + 'request_path' => $urlPath, + 'store_id' => $storeId + ] + ); + $categoryId = $actualUrls->getEntityId(); + $targetPath = $actualUrls->getTargetPath(); + $expectedType = $actualUrls->getEntityType(); + + $query + = <<<QUERY +{ + urlResolver(url:"/{$urlPath2}") + { + id + canonical_url + type + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('urlResolver', $response); + $this->assertEquals($categoryId, $response['urlResolver']['id']); + $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']); + $this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']); + } } From e39d356a166b9294f63362d5ef5e629dec96dc20 Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras <zbigniew.kuras@gate-software.com> Date: Wed, 16 May 2018 23:12:13 +0200 Subject: [PATCH 057/333] GraphQL - Added missed dependencies, removed unused variable --- .../Magento/UrlRewriteGraphQl/composer.json | 52 +++++++++---------- .../GraphQl/UrlRewrite/UrlResolverTest.php | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/UrlRewriteGraphQl/composer.json b/app/code/Magento/UrlRewriteGraphQl/composer.json index 4a69c88071757..a1db660ab1526 100644 --- a/app/code/Magento/UrlRewriteGraphQl/composer.json +++ b/app/code/Magento/UrlRewriteGraphQl/composer.json @@ -1,27 +1,27 @@ { - "name": "magento/module-url-rewrite-graph-ql", - "description": "N/A", - "type": "magento2-module", - "require": { - "php": "~7.1.3||~7.2.0", - "magento/framework": "*", - "magento/module-url-rewrite": "*", - "magento/module-store": "*" - - }, - "suggest": { - "magento/module-graph-ql": "*" - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\UrlRewriteGraphQl\\": "" - } - } -} + "name" : "magento/module-url-rewrite-graph-ql", + "description" : "N/A", + "type" : "magento2-module", + "require" : { + "php" : "~7.1.3||~7.2.0", + "magento/framework" : "*", + "magento/module-url-rewrite" : "*", + "magento/module-store" : "*", + "magento/cms" : "*" + }, + "suggest" : { + "magento/module-graph-ql" : "*" + }, + "license" : [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload" : { + "files" : [ + "registration.php" + ], + "psr-4" : { + "Magento\\UrlRewriteGraphQl\\" : "" + } + } +} \ No newline at end of file diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php index 97c682b3fe0cf..4b6e00ba5cf74 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php @@ -289,7 +289,7 @@ public function testCategoryUrlWithLeadingSlash() $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $actualUrls = $urlFinder->findOneByData( [ - 'request_path' => $urlPath, + 'request_path' => $urlPath2, 'store_id' => $storeId ] ); From 38c48a6c3e6435f1920d7b83a0745dd19f6c44f1 Mon Sep 17 00:00:00 2001 From: Robert He <rohe@magento.com> Date: Wed, 16 May 2018 17:05:57 -0500 Subject: [PATCH 058/333] MAGETWO-91450: date reverts in past after clicking out of product edit #11517 - provided initial value for shiftedValue --- app/code/Magento/Ui/view/base/web/js/grid/filters/range.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 438d6ccd58a55..ccfba8e98b6f4 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 @@ -27,7 +27,8 @@ define([ }, date: { component: 'Magento_Ui/js/form/element/date', - dateFormat: 'MM/dd/YYYY' + dateFormat: 'MM/dd/YYYY', + shiftedValue: 'filter' }, text: { component: 'Magento_Ui/js/form/element/abstract' From 730d0c96b7797e48b4971b2b6379de7f7cc89285 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 16 May 2018 17:38:20 -0500 Subject: [PATCH 059/333] MAGETWO-90569: Empty product attributes of dropdown/swatch types display as "No" of the storefront - type check safety added for boolean vs string/int on dropdown that was causing this bug --- .../Entity/Attribute/Frontend/AbstractFrontend.php | 2 +- .../Magento/Catalog/_files/dropdown_attribute.php | 4 ++-- .../Attribute/Frontend/DefaultFrontendTest.php | 12 ++++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php index 67a0bbefe7510..3d4c9e89a035f 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php @@ -165,7 +165,7 @@ public function getValue(\Magento\Framework\DataObject $object) $options = $opt->getAllOptions(); if ($options) { foreach ($options as $option) { - if ($option['value'] == $value) { + if ($option['value'] === $value) { $valueOption = $option['label']; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute.php index f55afc8c6a211..bb7e241d972e5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute.php @@ -32,8 +32,8 @@ '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, + 'is_visible_on_front' => 1, + 'used_in_product_listing' => 1, 'used_for_sort_by' => 0, 'frontend_label' => ['Drop-Down Attribute'], 'backend_type' => 'varchar', diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php index 7d5cb55ec2f96..3b889c39dca36 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php @@ -77,6 +77,18 @@ public function testGetSelectOptions() $this->cache->load($this->getCacheKey()) ); } + /** + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + */ + public function testNotSetAttributeEntityValue() + { + $entity = $this->objectManager->create(\Magento\Catalog\Model\Product::class); + $entity->setStoreId(0); + $entity->load(1); + $frontEnd = $this->attribute->loadByCode('catalog_product', 'dropdown_attribute'); + $value = $frontEnd->getFrontend()->getValue($entity); + $this->assertFalse($value); + } /** * Cache key generation From 3c39047b2271364fcd1ebeb94dee2157684a85dd Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Thu, 17 May 2018 10:18:37 +0300 Subject: [PATCH 060/333] [Forwardport] Fixed typo in method name --- lib/internal/Magento/Framework/App/Request/Http.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php index 5d4ca42d66cbe..4421903f40c2e 100644 --- a/lib/internal/Magento/Framework/App/Request/Http.php +++ b/lib/internal/Magento/Framework/App/Request/Http.php @@ -149,7 +149,7 @@ public function setPathInfo($pathInfo = null) return $this; } - $requestUri = $this->removeRepitedSlashes($requestUri); + $requestUri = $this->removeRepeatedSlashes($requestUri); $parsedRequestUri = explode('?', $requestUri, 2); $queryString = !isset($parsedRequestUri[1]) ? '' : '?' . $parsedRequestUri[1]; $baseUrl = $this->getBaseUrl(); @@ -172,7 +172,7 @@ public function setPathInfo($pathInfo = null) * @param string $pathInfo * @return string */ - private function removeRepitedSlashes($pathInfo) + private function removeRepeatedSlashes($pathInfo) { $firstChar = (string)substr($pathInfo, 0, 1); if ($firstChar == '/') { From 5b92f7b2f0ed7737a3de2275aa4384e7f68c4222 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 17 May 2018 10:45:27 +0300 Subject: [PATCH 061/333] MAGETWO-89412: [2.3] Composer static test of PHP versions is failed for non-core modules --- .../Magento/Test/Integrity/ComposerTest.php | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index c70cac369af6d..eea91092316f8 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -210,6 +210,17 @@ private function assertMagentoConventions($dir, $packageType, \StdClass $json) } } + /** + * Checks if package vendor is Magento. + * + * @param string $packageName + * @return bool + */ + private function isVendorMagento(string $packageName) + { + return strpos($packageName, 'magento/') === 0; + } + /** * Assert that component registrar is autoloaded in composer json * @@ -313,12 +324,24 @@ private function assertDependsOnFramework(\StdClass $json) private function assertPhpVersionInSync($name, $phpVersion) { if (isset(self::$rootJson['require']['php'])) { - $this->assertEquals( - self::$rootJson['require']['php'], - $phpVersion, - "PHP version {$phpVersion} in component {$name} is inconsistent with version " - . self::$rootJson['require']['php'] . ' in root composer.json' - ); + if ($this->isVendorMagento($name)) { + $this->assertEquals( + self::$rootJson['require']['php'], + $phpVersion, + "PHP version {$phpVersion} in component {$name} is inconsistent with version " + . self::$rootJson['require']['php'] . ' in root composer.json' + ); + } else { + $composerVersionsPattern = '{\s*\|\|?\s*}'; + $rootPhpVersions = preg_split($composerVersionsPattern, self::$rootJson['require']['php']); + $modulePhpVersions = preg_split($composerVersionsPattern, $phpVersion); + + $this->assertEmpty( + array_diff($rootPhpVersions, $modulePhpVersions), + "PHP version {$phpVersion} in component {$name} is inconsistent with version " + . self::$rootJson['require']['php'] . ' in root composer.json' + ); + } } } From 087cf5dc3fb87bcb6d2fbacaa7893b3d6ff21b39 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 17 May 2018 11:02:39 +0300 Subject: [PATCH 062/333] MAGETWO-90350: Admin Section is not visible in backend on production mode - GitHub issue #14104 --- app/code/Magento/Deploy/etc/di.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Deploy/etc/di.xml b/app/code/Magento/Deploy/etc/di.xml index e47fca3a6b946..ce7c84c95538a 100644 --- a/app/code/Magento/Deploy/etc/di.xml +++ b/app/code/Magento/Deploy/etc/di.xml @@ -31,6 +31,7 @@ <item name="dumpApplicationCommand" xsi:type="object">\Magento\Deploy\Console\Command\App\ApplicationDumpCommand</item> <item name="sensitiveConfigSetCommand" xsi:type="object">\Magento\Deploy\Console\Command\App\SensitiveConfigSetCommand</item> <item name="configImportCommand" xsi:type="object">Magento\Deploy\Console\Command\App\ConfigImportCommand</item> + <item name="configStatusCommand" xsi:type="object">Magento\Deploy\Console\Command\App\ConfigStatusCommand</item> </argument> </arguments> </type> From d050393093916e91270a6374202cfb411822d1d3 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 17 May 2018 13:39:25 +0300 Subject: [PATCH 063/333] MAGETWO-90818: Files and folders symlinked in pub/media cannot be deleted from media gallery browser --- .../Cms/Model/Wysiwyg/Images/Storage.php | 2 +- .../Unit/Model/Wysiwyg/Images/StorageTest.php | 16 ++-- .../Wysiwyg/Images/DeleteFilesTest.php | 44 ++++++++-- .../Wysiwyg/Images/DeleteFolderTest.php | 33 ++++++- .../Wysiwyg/Images/NewFolderTest.php | 37 +++++++- .../Adminhtml/Wysiwyg/Images/UploadTest.php | 42 +++++++-- .../Magento/Cms/_files/linked_media.php | 23 +++++ .../Cms/_files/linked_media_rollback.php | 21 +++++ .../App/Filesystem/DirectoryResolver.php | 20 ++++- .../Unit/Filesystem/DirectoryResolverTest.php | 85 +++++++++++++++++++ 10 files changed, 291 insertions(+), 32 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Cms/_files/linked_media.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/_files/linked_media_rollback.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/Filesystem/DirectoryResolverTest.php diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index 0c8ff7d0b2b78..4b7cd239a66f5 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -739,7 +739,7 @@ protected function _validatePath($path) */ protected function _sanitizePath($path) { - return rtrim(preg_replace('~[/\\\]+~', '/', $this->_directory->getDriver()->getRealPath($path)), '/'); + return rtrim(preg_replace('~[/\\\]+~', '/', $this->_directory->getDriver()->getRealPathSafety($path)), '/'); } /** 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 2fb9649fc61de..25134451d5a56 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 @@ -114,16 +114,9 @@ class StorageTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->filesystemMock = $this->createMock(\Magento\Framework\Filesystem::class); - $this->driverMock = $this->getMockForAbstractClass( - \Magento\Framework\Filesystem\DriverInterface::class, - [], - '', - false, - false, - true, - ['getRealPath'] - ); - $this->driverMock->expects($this->any())->method('getRealPath')->will($this->returnArgument(0)); + $this->driverMock = $this->getMockBuilder(\Magento\Framework\Filesystem\DriverInterface::class) + ->setMethods(['getRealPathSafety']) + ->getMockForAbstractClass(); $this->directoryMock = $this->createPartialMock( \Magento\Framework\Filesystem\Directory\Write::class, @@ -243,6 +236,7 @@ public function testDeleteDirectoryOverRoot() $this->expectExceptionMessage( sprintf('Directory %s is not under storage root path.', self::INVALID_DIRECTORY_OVER_ROOT) ); + $this->driverMock->expects($this->atLeastOnce())->method('getRealPathSafety')->will($this->returnArgument(0)); $this->imagesStorage->deleteDirectory(self::INVALID_DIRECTORY_OVER_ROOT); } @@ -253,7 +247,7 @@ public function testDeleteRootDirectory() { $this->expectException(\Magento\Framework\Exception\LocalizedException::class); $this->expectExceptionMessage(sprintf('We can\'t delete root directory %s right now.', self::STORAGE_ROOT_DIR)); - + $this->driverMock->expects($this->atLeastOnce())->method('getRealPathSafety')->will($this->returnArgument(0)); $this->imagesStorage->deleteDirectory(self::STORAGE_ROOT_DIR); } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php index 6da582195a823..bbcbc40dc6640 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php @@ -38,23 +38,33 @@ class DeleteFilesTest extends \PHPUnit\Framework\TestCase */ private $fileName = 'magento_small_image.jpg'; + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + /** * @inheritdoc */ protected function setUp() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $directoryName = 'directory1'; - $filesystem = $objectManager->get(\Magento\Framework\Filesystem::class); + $this->filesystem = $this->objectManager->get(\Magento\Framework\Filesystem::class); /** @var \Magento\Cms\Helper\Wysiwyg\Images $imagesHelper */ - $this->imagesHelper = $objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); - $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->imagesHelper = $this->objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); + $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); $this->fullDirectoryPath = $this->imagesHelper->getStorageRoot() . '/' . $directoryName; $this->mediaDirectory->create($this->mediaDirectory->getRelativePath($this->fullDirectoryPath)); $filePath = $this->fullDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName; $fixtureDir = realpath(__DIR__ . '/../../../../../Catalog/_files'); copy($fixtureDir . '/' . $this->fileName, $filePath); - $this->model = $objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFiles::class); + $this->model = $this->objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFiles::class); } /** @@ -98,6 +108,30 @@ public function testExecuteWithWrongFileName() ); } + /** + * Execute method with correct directory path and file name to check that files under linked media directory + * can be removed. + * + * @return void + * @magentoDataFixture Magento/Cms/_files/linked_media.php + */ + public function testExecuteWithLinkedMedia() + { + $directoryName = 'linked_media'; + $fullDirectoryPath = $this->filesystem->getDirectoryRead(DirectoryList::PUB) + ->getAbsolutePath() . DIRECTORY_SEPARATOR . $directoryName; + $filePath = $fullDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName; + $fixtureDir = realpath(__DIR__ . '/../../../../../Catalog/_files'); + copy($fixtureDir . '/' . $this->fileName, $filePath); + + $wysiwygDir = $this->mediaDirectory->getAbsolutePath() . '/wysiwyg'; + $this->model->getRequest()->setMethod('POST') + ->setPostValue('files', [$this->imagesHelper->idEncode($this->fileName)]); + $this->model->getStorage()->getSession()->setCurrentPath($wysiwygDir); + $this->model->execute(); + $this->assertFalse(is_file($fullDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName)); + } + /** * @inheritdoc */ diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php index 2b59963b21dca..8e30e85541a42 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php @@ -33,14 +33,19 @@ class DeleteFolderTest extends \PHPUnit\Framework\TestCase */ private $fullDirectoryPath; + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + /** * @inheritdoc */ protected function setUp() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $filesystem = $objectManager->get(\Magento\Framework\Filesystem::class); - $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->filesystem = $objectManager->get(\Magento\Framework\Filesystem::class); + $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); /** @var \Magento\Cms\Helper\Wysiwyg\Images $imagesHelper */ $this->imagesHelper = $objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); $this->fullDirectoryPath = $this->imagesHelper->getStorageRoot(); @@ -52,6 +57,7 @@ protected function setUp() * can be removed. * * @return void + * @magentoAppIsolation enabled */ public function testExecute() { @@ -71,6 +77,29 @@ public function testExecute() ); } + /** + * Execute method with correct directory path to check that directories under linked media directory + * can be removed. + * + * @magentoDataFixture Magento/Cms/_files/linked_media.php + */ + public function testExecuteWithLinkedMedia() + { + $linkedDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::PUB); + $linkedDirectoryPath = $this->filesystem->getDirectoryRead(DirectoryList::PUB) + ->getAbsolutePath() . 'linked_media'; + $directoryName = 'NewDirectory'; + + $linkedDirectory->create( + $linkedDirectory->getRelativePath($linkedDirectoryPath . DIRECTORY_SEPARATOR . $directoryName) + ); + $this->model->getRequest()->setParams( + ['node' => $this->imagesHelper->idEncode('wysiwyg' . DIRECTORY_SEPARATOR . $directoryName)] + ); + $this->model->execute(); + $this->assertFalse(is_dir($linkedDirectoryPath . DIRECTORY_SEPARATOR . $directoryName)); + } + /** * Execute method with traversal directory path to check that there is no ability to remove folder which is not * under media directory. diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php index b20b5a04a2a50..0c74f18e9c44a 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolderTest.php @@ -33,17 +33,27 @@ class NewFolderTest extends \PHPUnit\Framework\TestCase */ private $dirName= 'NewDirectory'; + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + + /** + * @var \Magento\Cms\Helper\Wysiwyg\Images + */ + private $imagesHelper; + /** * @inheritdoc */ protected function setUp() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $filesystem = $objectManager->get(\Magento\Framework\Filesystem::class); - $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->filesystem = $objectManager->get(\Magento\Framework\Filesystem::class); /** @var \Magento\Cms\Helper\Wysiwyg\Images $imagesHelper */ - $imagesHelper = $objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); - $this->fullDirectoryPath = $imagesHelper->getStorageRoot(); + $this->imagesHelper = $objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); + $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->fullDirectoryPath = $this->imagesHelper->getStorageRoot(); $this->model = $objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\NewFolder::class); } @@ -68,6 +78,25 @@ public function testExecute() ); } + /** + * Execute method with correct directory path to check that new folder can be created under WYSIWYG media directory. + * + * @magentoDataFixture Magento/Cms/_files/linked_media.php + */ + public function testExecuteWithLinkedMedia() + { + $linkedDirectoryPath = $this->filesystem->getDirectoryRead(DirectoryList::PUB) + ->getAbsolutePath() . 'linked_media'; + $this->model->getRequest()->setMethod('POST') + ->setPostValue('name', $this->dirName); + $this->model->getStorage() + ->getSession() + ->setCurrentPath($this->fullDirectoryPath . DIRECTORY_SEPARATOR . 'wysiwyg'); + $this->model->execute(); + + $this->assertTrue(is_dir($linkedDirectoryPath . DIRECTORY_SEPARATOR . $this->dirName)); + } + /** * Execute method with traversal directory path to check that there is no ability to create new folder not * under media directory. diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php index ba2527e3e449e..534eb3db35b3f 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php @@ -33,22 +33,32 @@ class UploadTest extends \PHPUnit\Framework\TestCase */ private $fileName = 'magento_small_image.jpg'; + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + /** * @inheritdoc */ protected function setUp() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $directoryName = 'directory1'; - $filesystem = $objectManager->get(\Magento\Framework\Filesystem::class); + $this->filesystem = $this->objectManager->get(\Magento\Framework\Filesystem::class); /** @var \Magento\Cms\Helper\Wysiwyg\Images $imagesHelper */ - $imagesHelper = $objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); - $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $imagesHelper = $this->objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class); + $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); $this->fullDirectoryPath = $imagesHelper->getStorageRoot() . DIRECTORY_SEPARATOR . $directoryName; $this->mediaDirectory->create($this->mediaDirectory->getRelativePath($this->fullDirectoryPath)); - $this->model = $objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\Upload::class); + $this->model = $this->objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\Upload::class); $fixtureDir = realpath(__DIR__ . '/../../../../../Catalog/_files'); - $tmpFile = __DIR__ . DIRECTORY_SEPARATOR . $this->fileName; + $tmpFile = $this->filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath() . $this->fileName; copy($fixtureDir . DIRECTORY_SEPARATOR . $this->fileName, $tmpFile); $_FILES = [ 'image' => [ @@ -66,6 +76,7 @@ protected function setUp() * located under WYSIWYG media. * * @return void + * @magentoAppIsolation enabled */ public function testExecute() { @@ -82,6 +93,25 @@ public function testExecute() ); } + /** + * Execute method with correct directory path and file name to check that file can be uploaded to the directory + * located under linked folder. + * + * @return void + * @magentoDataFixture Magento/Cms/_files/linked_media.php + */ + public function testExecuteWithLinkedMedia() + { + $directoryName = 'linked_media'; + $fullDirectoryPath = $this->filesystem->getDirectoryRead(DirectoryList::PUB) + ->getAbsolutePath() . DIRECTORY_SEPARATOR . $directoryName; + $wysiwygDir = $this->mediaDirectory->getAbsolutePath() . '/wysiwyg'; + $this->model->getRequest()->setParams(['type' => 'image/png']); + $this->model->getStorage()->getSession()->setCurrentPath($wysiwygDir); + $this->model->execute(); + $this->assertTrue(is_file($fullDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName)); + } + /** * Execute method with traversal directory path to check that there is no ability to create file not * under media directory. diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/linked_media.php b/dev/tests/integration/testsuite/Magento/Cms/_files/linked_media.php new file mode 100644 index 0000000000000..5921f871aaeb2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/linked_media.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$directoryName = 'linked_media'; +/** @var \Magento\Framework\Filesystem $filesystem */ +$filesystem = $objectManager->get(\Magento\Framework\Filesystem::class); +$fullDirectoryPath = $filesystem->getDirectoryRead(Magento\Framework\App\Filesystem\DirectoryList::PUB) + ->getAbsolutePath() . $directoryName; +$mediaDirectory = $filesystem->getDirectoryWrite(Magento\Framework\App\Filesystem\DirectoryList::MEDIA); + +$wysiwygDir = $mediaDirectory->getAbsolutePath() . 'wysiwyg'; +$mediaDirectory->delete($wysiwygDir); +if (!is_dir($fullDirectoryPath)) { + mkdir($fullDirectoryPath); +} +if (is_dir($fullDirectoryPath) && !is_dir($wysiwygDir)) { + symlink($fullDirectoryPath, $wysiwygDir); +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/linked_media_rollback.php b/dev/tests/integration/testsuite/Magento/Cms/_files/linked_media_rollback.php new file mode 100644 index 0000000000000..803fa67aff9de --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/linked_media_rollback.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$directoryName = 'linked_media'; +/** @var \Magento\Framework\Filesystem $filesystem */ +$filesystem = $objectManager->get(\Magento\Framework\Filesystem::class); +$pubDir = $filesystem->getDirectoryWrite(Magento\Framework\App\Filesystem\DirectoryList::PUB); +$fullDirectoryPath = $pubDir->getAbsolutePath() . DIRECTORY_SEPARATOR . $directoryName; +$mediaDirectory = $filesystem->getDirectoryWrite(Magento\Framework\App\Filesystem\DirectoryList::MEDIA); +$wysiwygDir = $mediaDirectory->getAbsolutePath() . 'wysiwyg'; +if (is_link($wysiwygDir)) { + unlink($wysiwygDir); +} +if (is_dir($fullDirectoryPath)) { + $pubDir->delete($directoryName); +} diff --git a/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php b/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php index 2bc76198bf37f..849cfa261ebe1 100644 --- a/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php +++ b/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php @@ -3,8 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\App\Filesystem; +use Magento\Framework\Filesystem; +use Magento\Framework\App\ObjectManager; + /** * Magento directories resolver. */ @@ -15,12 +20,20 @@ class DirectoryResolver */ private $directoryList; + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + /** * @param DirectoryList $directoryList + * @param Filesystem|null $filesystem + * @throws \RuntimeException */ - public function __construct(DirectoryList $directoryList) + public function __construct(DirectoryList $directoryList, Filesystem $filesystem = null) { $this->directoryList = $directoryList; + $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class); } /** @@ -38,8 +51,9 @@ public function __construct(DirectoryList $directoryList) */ public function validatePath($path, $directoryConfig = DirectoryList::MEDIA) { - $realPath = realpath($path); - $root = realpath($this->directoryList->getPath($directoryConfig)); + $directory = $this->filesystem->getDirectoryWrite($directoryConfig); + $realPath = $directory->getDriver()->getRealPathSafety($path); + $root = $this->directoryList->getPath($directoryConfig); return strpos($realPath, $root) === 0; } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Filesystem/DirectoryResolverTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Filesystem/DirectoryResolverTest.php new file mode 100644 index 0000000000000..b74cf51fa9e2f --- /dev/null +++ b/lib/internal/Magento/Framework/App/Test/Unit/Filesystem/DirectoryResolverTest.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\App\Test\Unit\Filesystem; + +/** + * Unit tests for the \Magento\Framework\App\Filesystem\DirectoryResolver class. + */ +class DirectoryResolverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\App\Filesystem\DirectoryList|\PHPUnit_Framework_MockObject_MockObject + */ + private $directoryList; + + /** + * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + private $filesystem; + + /** + * @var \Magento\Framework\App\Filesystem\DirectoryResolver + */ + private $directoryResolver; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->directoryList = $this->getMockBuilder(\Magento\Framework\App\Filesystem\DirectoryList::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->filesystem = $this->getMockBuilder(\Magento\Framework\Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->directoryResolver = new \Magento\Framework\App\Filesystem\DirectoryResolver( + $this->directoryList, + $this->filesystem + ); + } + + /** + * @dataProvider validatePathDataProvider + * @param string $path + * @param bool $expectedResult + * @return void + */ + public function testValidatePath($path, $expectedResult) + { + $rootPath = '/path/root'; + $directoryConfig = 'directory_config'; + $directory = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) + ->setMethods(['getDriver']) + ->getMockForAbstractClass(); + $driver = $this->getMockBuilder(\Magento\Framework\Filesystem\DriverInterface::class) + ->setMethods(['getRealPathSafety']) + ->getMockForAbstractClass(); + $directory->expects($this->atLeastOnce())->method('getDriver')->willReturn($driver); + $driver->expects($this->atLeastOnce())->method('getRealPathSafety')->with($path) + ->willReturnArgument(0); + $this->filesystem->expects($this->atLeastOnce())->method('getDirectoryWrite')->with($directoryConfig) + ->willReturn($directory); + $this->directoryList->expects($this->atLeastOnce())->method('getPath')->with($directoryConfig) + ->willReturn($rootPath); + $this->assertEquals($expectedResult, $this->directoryResolver->validatePath($path, $directoryConfig)); + } + + /** + * @return array + */ + public function validatePathDataProvider() + { + return [ + ['/path/root/for/validation', true], + ['/path/invalid/for/validation', false], + ]; + } +} From 6817451be5216dee735d5b1107841349cb1e1107 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 17 May 2018 16:03:37 +0300 Subject: [PATCH 064/333] MAGETWO-89409: [2.3] Test Magento\Ui\Component\ConfigurationTest::testConfiguration failing with bundled extensions --- .../Ui/Component/ConfigurationTest.php | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Ui/Component/ConfigurationTest.php b/dev/tests/integration/testsuite/Magento/Ui/Component/ConfigurationTest.php index 8a8ba9db564a2..826afe82e2bb6 100644 --- a/dev/tests/integration/testsuite/Magento/Ui/Component/ConfigurationTest.php +++ b/dev/tests/integration/testsuite/Magento/Ui/Component/ConfigurationTest.php @@ -7,13 +7,17 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Component\ComponentFile; +use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\Component\DirSearch; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\ReadInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\Ui\Config\Reader\DefinitionMap; -use Magento\Framework\Component\ComponentRegistrar; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ConfigurationTest extends \PHPUnit\Framework\TestCase { /** @@ -26,6 +30,11 @@ class ConfigurationTest extends \PHPUnit\Framework\TestCase */ private $appDir; + /** + * @var ReadInterface + */ + private $rootDir; + /** * @var \DOMDocument */ @@ -67,6 +76,7 @@ public function setUp() /** @var Filesystem $filesystem */ $filesystem = $objectManager->create(Filesystem::class); $this->appDir = $filesystem->getDirectoryRead(DirectoryList::APP); + $this->rootDir = $filesystem->getDirectoryRead(DirectoryList::ROOT); } /** @@ -84,7 +94,14 @@ public function testConfiguration() /** @var ComponentFile $file */ foreach ($uiConfigurationFiles as $file) { $this->currentFile = $file; - $content = $this->appDir->readFile($this->appDir->getRelativePath($file->getFullPath())); + $fullPath = $file->getFullPath(); + // by default search files in `app` directory but Magento can be installed via composer + // or some modules can be in `vendor` directory (like bundled extensions) + try { + $content = $this->appDir->readFile($this->appDir->getRelativePath($fullPath)); + } catch (FileSystemException $e) { + $content = $this->rootDir->readFile($this->rootDir->getRelativePath($fullPath)); + } $this->assertConfigurationSemantic($this->getDom($content), $result); } if (!empty($result)) { From 08cf8b878a9919b95b44ef69b04830532d487c24 Mon Sep 17 00:00:00 2001 From: Jeroen <jeroen@reachdigital.nl> Date: Tue, 8 May 2018 17:32:50 +0200 Subject: [PATCH 065/333] Don't recalculate tax for every price on category page --- app/code/Magento/Tax/Model/Config.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Tax/Model/Config.php b/app/code/Magento/Tax/Model/Config.php index b30e5afb85142..09212ce90bf58 100644 --- a/app/code/Magento/Tax/Model/Config.php +++ b/app/code/Magento/Tax/Model/Config.php @@ -832,12 +832,12 @@ public function getInfoUrl($store = null) * If it necessary will be returned conversion type (minus or plus) * * @param null|int|string|Store $store - * @return bool + * @return bool|int * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function needPriceConversion($store = null) { - $res = false; + $res = 0; $priceIncludesTax = $this->priceIncludesTax($store) || $this->getNeedUseShippingExcludeTax(); if ($priceIncludesTax) { switch ($this->getPriceDisplayType($store)) { @@ -845,7 +845,7 @@ public function needPriceConversion($store = null) case self::DISPLAY_TYPE_BOTH: return self::PRICE_CONVERSION_MINUS; case self::DISPLAY_TYPE_INCLUDING_TAX: - $res = true; + $res = false; break; default: break; From 86aa6e3850cd6c56ca59b4a9016e7bc237544c36 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 17 May 2018 09:30:12 -0500 Subject: [PATCH 066/333] MAGETWO-90569: Empty product attributes of dropdown/swatch types display as "No" of the storefront - static fix --- .../Model/Entity/Attribute/Frontend/DefaultFrontendTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php index 3b889c39dca36..5e8239586d76b 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php @@ -77,10 +77,11 @@ public function testGetSelectOptions() $this->cache->load($this->getCacheKey()) ); } + /** * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php */ - public function testNotSetAttributeEntityValue() + public function testAttributeEntityValueNotSet() { $entity = $this->objectManager->create(\Magento\Catalog\Model\Product::class); $entity->setStoreId(0); From 9cbef5e6cf8a1a853bfc83b2ce59dffaaf443eea Mon Sep 17 00:00:00 2001 From: Adam Paterson <hello@adampaterson.co.uk> Date: Wed, 25 Apr 2018 01:38:49 +0100 Subject: [PATCH 067/333] MAGETWO-84124: Add bundle parent/child relationship during import Resolves: #12330 --- .../Model/Import/Product/Type/Bundle.php | 27 +++++++++++++++++ .../Type/Bundle/RelationsDataSaver.php | 25 ++++++++++++++-- .../Type/Bundle/RelationsDataSaverTest.php | 30 +++++++++++++++++-- 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index 1d2ac3a87ab77..a2d7b05f8dfdb 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -411,6 +411,7 @@ public function saveData() $this->populateExistingOptions(); $this->insertOptions(); $this->insertSelections(); + $this->insertParentChildRelations(); $this->clear(); } } @@ -659,6 +660,32 @@ protected function insertSelections() return $this; } + /** + * Insert parent/child product relations + * + * @return \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType + */ + protected function insertParentChildRelations() + { + foreach ($this->_cachedOptions as $productId => $options) { + $childIds = []; + foreach ($options as $option) { + foreach ($option['selections'] as $selection) { + if (!isset($selection['parent_product_id'])) { + if (!isset($this->_cachedSkuToProducts[$selection['sku']])) { + continue; + } + $childIds[] = $this->_cachedSkuToProducts[$selection['sku']]; + } + } + + $this->relationsDataSaver->saveProductRelations($productId, $childIds); + } + } + + return $this; + } + /** * Initialize attributes parameters for all attributes' sets. * diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle/RelationsDataSaver.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle/RelationsDataSaver.php index a58195f823bf1..e526e685afad0 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle/RelationsDataSaver.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle/RelationsDataSaver.php @@ -17,13 +17,21 @@ class RelationsDataSaver */ private $resource; + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\Relation + */ + private $productRelation; + /** * @param \Magento\Framework\App\ResourceConnection $resource + * @param \Magento\Catalog\Model\ResourceModel\Product\Relation $productRelation */ public function __construct( - \Magento\Framework\App\ResourceConnection $resource + \Magento\Framework\App\ResourceConnection $resource, + \Magento\Catalog\Model\ResourceModel\Product\Relation $productRelation ) { - $this->resource = $resource; + $this->resource = $resource; + $this->productRelation = $productRelation; } /** @@ -92,4 +100,17 @@ public function saveSelections(array $selections) ); } } + + /** + * Saves given parent/child relations. + * + * @param int $parentId + * @param array $childIds + * + * @return void + */ + public function saveProductRelations($parentId, $childIds) + { + $this->productRelation->processRelations($parentId, $childIds); + } } diff --git a/app/code/Magento/BundleImportExport/Test/Unit/Model/Import/Product/Type/Bundle/RelationsDataSaverTest.php b/app/code/Magento/BundleImportExport/Test/Unit/Model/Import/Product/Type/Bundle/RelationsDataSaverTest.php index 42d508cdfb195..d50243b3656f3 100644 --- a/app/code/Magento/BundleImportExport/Test/Unit/Model/Import/Product/Type/Bundle/RelationsDataSaverTest.php +++ b/app/code/Magento/BundleImportExport/Test/Unit/Model/Import/Product/Type/Bundle/RelationsDataSaverTest.php @@ -7,6 +7,7 @@ namespace Magento\BundleImportExport\Test\Unit\Model\Import\Product\Type\Bundle; use Magento\BundleImportExport\Model\Import\Product\Type\Bundle\RelationsDataSaver; +use Magento\Catalog\Model\ResourceModel\Product\Relation; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; @@ -30,6 +31,11 @@ class RelationsDataSaverTest extends \PHPUnit\Framework\TestCase */ private $connectionMock; + /** + * @var Relation|\PHPUnit_Framework_MockObject_MockObject + */ + private $productRelationMock; + protected function setUp() { $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -39,12 +45,16 @@ protected function setUp() $this->connectionMock = $this->getMockBuilder(AdapterInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->resourceMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); + + $this->productRelationMock = $this->getMockBuilder(Relation::class) + ->disableOriginalConstructor() + ->getMock(); $this->relationsDataSaver = $helper->getObject( RelationsDataSaver::class, [ - 'resource' => $this->resourceMock + 'resource' => $this->resourceMock, + 'productRelation' => $this->productRelationMock ] ); } @@ -53,7 +63,7 @@ public function testSaveOptions() { $options = [1, 2]; $table_name= 'catalog_product_bundle_option'; - + $this->resourceMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); $this->resourceMock->expects($this->once()) ->method('getTableName') ->with('catalog_product_bundle_option') @@ -78,6 +88,7 @@ public function testSaveOptionValues() $optionsValues = [1, 2]; $table_name= 'catalog_product_bundle_option_value'; + $this->resourceMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); $this->resourceMock->expects($this->once()) ->method('getTableName') ->with('catalog_product_bundle_option_value') @@ -98,6 +109,7 @@ public function testSaveSelections() $selections = [1, 2]; $table_name= 'catalog_product_bundle_selection'; + $this->resourceMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); $this->resourceMock->expects($this->once()) ->method('getTableName') ->with('catalog_product_bundle_selection') @@ -121,4 +133,16 @@ public function testSaveSelections() $this->relationsDataSaver->saveSelections($selections); } + + public function testSaveProductRelations() + { + $parentId = 1; + $children = [2, 3]; + + $this->productRelationMock->expects($this->once()) + ->method('processRelations') + ->with($parentId, $children); + + $this->relationsDataSaver->saveProductRelations($parentId, $children); + } } From e07b0749f0af08ff7fcec2c33f49324e52e122ce Mon Sep 17 00:00:00 2001 From: Adam Paterson <hello@adampaterson.co.uk> Date: Thu, 26 Apr 2018 09:20:56 +0100 Subject: [PATCH 068/333] MAGETWO-84124: Add backwards compatibility for RelationsDataSaver --- .../Product/Type/Bundle/RelationsDataSaver.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle/RelationsDataSaver.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle/RelationsDataSaver.php index e526e685afad0..5409d12ac56d3 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle/RelationsDataSaver.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle/RelationsDataSaver.php @@ -5,6 +5,9 @@ */ namespace Magento\BundleImportExport\Model\Import\Product\Type\Bundle; +use Magento\Catalog\Model\ResourceModel\Product\Relation; +use Magento\Framework\App\ObjectManager; + /** * A bundle product relations (options, selections, etc.) data saver. * @@ -18,20 +21,21 @@ class RelationsDataSaver private $resource; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\Relation + * @var Relation */ private $productRelation; /** * @param \Magento\Framework\App\ResourceConnection $resource - * @param \Magento\Catalog\Model\ResourceModel\Product\Relation $productRelation + * @param Relation $productRelation */ public function __construct( \Magento\Framework\App\ResourceConnection $resource, - \Magento\Catalog\Model\ResourceModel\Product\Relation $productRelation + Relation $productRelation = null ) { $this->resource = $resource; - $this->productRelation = $productRelation; + $this->productRelation = $productRelation + ?: ObjectManager::getInstance()->get(Relation::class); } /** From f1657b82c5a25ea35ce410264268371b5bfe35b9 Mon Sep 17 00:00:00 2001 From: Adam Paterson <hello@adampaterson.co.uk> Date: Fri, 4 May 2018 09:58:37 +0100 Subject: [PATCH 069/333] Change method visibility --- .../BundleImportExport/Model/Import/Product/Type/Bundle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index a2d7b05f8dfdb..3ed7e144ddd5a 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -665,7 +665,7 @@ protected function insertSelections() * * @return \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType */ - protected function insertParentChildRelations() + private function insertParentChildRelations() { foreach ($this->_cachedOptions as $productId => $options) { $childIds = []; From e2c06cedcfd5e2cf829633f2ee0924df74f6b0f0 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Thu, 17 May 2018 13:16:43 -0500 Subject: [PATCH 070/333] MC-81: Customer should not be able to add the product to the cart if options are not selected. - Updated mftf test. --- ...orefrontConfigurableProductDetailsTest.xml | 24 +++++++++---------- .../StorefrontConfigurableProductViewTest.xml | 6 ++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml index 790606fbf59eb..e7f733cf010f3 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductDetailsTest.xml @@ -11,9 +11,9 @@ <test name="StorefrontConfigurableProductBasicInfoTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Storefront configurable product details"/> - <title value="Guest customer should see the basic configurable product details in product view"/> - <description value="Guest customer should see the basic configurable product details in product view"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should see basic Configurable Product details"/> + <description value="Guest customer should see basic Configurable Product details"/> <severity value="MAJOR"/> <testCaseId value="MC-77"/> <group value="ConfigurableProduct"/> @@ -52,9 +52,9 @@ <test name="StorefrontConfigurableProductOptionsTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Storefront configurable product details"/> - <title value="Guest customer should see configurable product options in product view"/> - <description value="Guest customer should see configurable product options in product view"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should be able to see product configuration options"/> + <description value="Guest customer should be able to see product configuration options"/> <severity value="MAJOR"/> <testCaseId value="MC-92"/> <group value="ConfigurableProduct"/> @@ -93,9 +93,9 @@ <test name="StorefrontConfigurableProductCanAddToCartTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Storefront configurable product details"/> - <title value="Guest customer should be able to add configurable product to cart after an option is selected in product view"/> - <description value="Guest customer should be able to add configurable product to cart after an option is selected in product view"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should be able to successfully add the product to the cart"/> + <description value="Guest customer should be able to successfully add the product to the cart"/> <severity value="MAJOR"/> <testCaseId value="MC-97"/> <group value="ConfigurableProduct"/> @@ -131,9 +131,9 @@ <test name="StorefrontConfigurableProductCantAddToCartTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Storefront configurable product details"/> - <title value="Guest customer should not be able to add configurable product to cart if no option is selected in product view"/> - <description value="Guest customer should not be able to add configurable product to cart if no option is selected in product view"/> + <stories value="View configurable product details in storefront"/> + <title value="Guest customer should not be able to add the product to the cart if options are not selected"/> + <description value="Guest customer should not be able to add the product to the cart if options are not selected"/> <severity value="MAJOR"/> <testCaseId value="MC-81"/> <group value="ConfigurableProduct"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductViewTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductViewTest.xml index 447bb6683bca0..baaa5c4ef938f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductViewTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/StorefrontConfigurableProductViewTest.xml @@ -11,7 +11,7 @@ <test name="StorefrontConfigurableProductGridViewTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Storefront"/> + <stories value="View configurable product in a category in storefront"/> <title value="customer should see the configurable product in the category grid"/> <description value="customer should see the configurable product in the category grid"/> <severity value="CRITICAL"/> @@ -47,7 +47,7 @@ <test name="StorefrontConfigurableProductListViewTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Storefront"/> + <stories value="View configurable product in a category in storefront"/> <title value="customer should see the configurable product in the category list"/> <description value="customer should see the configurable product in the category list"/> <severity value="CRITICAL"/> @@ -85,7 +85,7 @@ <test name="StorefrontConfigurableProductAddToCartTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="Storefront"/> + <stories value="View configurable product in a category in storefront"/> <title value="customer should be taken to the product details page when clicking add to cart"/> <description value="customer should be taken to the product details page when clicking add to cart"/> <severity value="CRITICAL"/> From f36bb6870771634cf41fb535c6959f7add998c2f Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Thu, 17 May 2018 13:40:53 -0500 Subject: [PATCH 071/333] MAGETWO-91241: Build stabilization for Product list configuration with Magento Condition Tree --- .../view/adminhtml/web/conditions-data-normalizer.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js index e2307e05e8101..21c04d098ae6c 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js @@ -29,6 +29,7 @@ define([ foo: 'bar', bar: 123 }); + expect(normal.foo).toEqual('bar'); expect(normal.bar).toEqual(123); }); @@ -38,6 +39,7 @@ define([ 'foo[value]': 'bar', 'foo[name]': 123 }); + expect(normal.foo.value).toEqual('bar'); expect(normal.foo.name).toEqual(123); }); @@ -47,6 +49,7 @@ define([ 'foo[prefix][value]': 'bar', 'foo[prefix][name]': 123 }); + expect(normal.foo.prefix.value).toEqual('bar'); expect(normal.foo.prefix.name).toEqual(123); }); @@ -57,6 +60,7 @@ define([ 'foo[1][value]': 123, 'foo[1--1]': 321 }); + expect(normal.foo['1'].name).toEqual('bar'); expect(normal.foo['1'].value).toEqual(123); expect(normal.foo['1--1']).toEqual(321); From cde5322bbb14982daa217c88fb42775cdd0921a9 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Thu, 17 May 2018 14:21:40 -0500 Subject: [PATCH 072/333] MAGETWO-91241: Build stabilization for Product list configuration with Magento Condition Tree --- app/code/Magento/Ui/etc/ui_configuration.xsd | 1 + .../ui_component/etc/definition/colorPicker.xsd | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/app/code/Magento/Ui/etc/ui_configuration.xsd b/app/code/Magento/Ui/etc/ui_configuration.xsd index c43e45eb8e111..b839593a38f47 100644 --- a/app/code/Magento/Ui/etc/ui_configuration.xsd +++ b/app/code/Magento/Ui/etc/ui_configuration.xsd @@ -219,6 +219,7 @@ <xs:element name="checkbox" type="formElementCheckbox" maxOccurs="unbounded"/> <xs:element name="checkboxset" type="formElementCheckboxset" maxOccurs="unbounded"/> <xs:element name="email" type="formElementEmail" maxOccurs="unbounded"/> + <xs:element name="colorPicker" type="formElementColorPicker" maxOccurs="unbounded"/> <xs:element name="select" type="formElementSelect" maxOccurs="unbounded"/> <xs:element name="multiselect" type="formElementMultiselect" maxOccurs="unbounded"/> <xs:element name="text" type="formElementText" maxOccurs="unbounded"/> diff --git a/app/code/Magento/Ui/view/base/ui_component/etc/definition/colorPicker.xsd b/app/code/Magento/Ui/view/base/ui_component/etc/definition/colorPicker.xsd index 509ef2aad2dee..dea1f7ce059e9 100644 --- a/app/code/Magento/Ui/view/base/ui_component/etc/definition/colorPicker.xsd +++ b/app/code/Magento/Ui/view/base/ui_component/etc/definition/colorPicker.xsd @@ -22,6 +22,19 @@ <xs:attributeGroup ref="ui_element_attributes"/> </xs:complexType> + <xs:complexType name="formElementColorPicker"> + <xs:sequence> + <xs:element name="settings" minOccurs="0" maxOccurs="1"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:group ref="componentColorPickerSettings"/> + </xs:choice> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attributeGroup ref="ui_element_attributes"/> + </xs:complexType> + <xs:group name="componentColorPickerSettings"> <xs:choice> <xs:group ref="abstractSettings"/> From 9962c66890db9fb5f58e09da995b6ecf502c6d88 Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras <zbigniew.kuras@gate-software.com> Date: Thu, 17 May 2018 22:44:05 +0200 Subject: [PATCH 073/333] GraphQL - Created one resolver for sort_fields. Added tests. --- .../{SortFieldDefault.php => SortFields.php} | 23 +++++-- .../Resolver/Category/SortFieldsOptions.php | 61 ------------------- app/code/Magento/GraphQl/etc/schema.graphqls | 8 +-- .../GraphQl/Catalog/ProductSearchTest.php | 5 +- 4 files changed, 25 insertions(+), 72 deletions(-) rename app/code/Magento/CatalogGraphQl/Model/Resolver/Category/{SortFieldDefault.php => SortFields.php} (67%) delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldsOptions.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldDefault.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFields.php similarity index 67% rename from app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldDefault.php rename to app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFields.php index 37b9d5f9c5e0b..2b482f51df616 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldDefault.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFields.php @@ -14,9 +14,9 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * Retrieves the sort field default value + * Retrieves the sort fields data */ -class SortFieldDefault implements ResolverInterface +class SortFields implements ResolverInterface { /** * @var ValueFactory @@ -41,7 +41,8 @@ class SortFieldDefault implements ResolverInterface public function __construct( ValueFactory $valueFactory, \Magento\Catalog\Model\Config $catalogConfig, - \Magento\Store\Model\StoreManagerInterface $storeManager + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Catalog\Model\Category\Attribute\Source\Sortby $ss ) { $this->valueFactory = $valueFactory; $this->catalogConfig = $catalogConfig; @@ -53,10 +54,20 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) : Value { - $sortFieldDefault = $this->catalogConfig->getProductListDefaultSortBy($this->storeManager->getStore()->getId()); + $sortFieldsOptions = [ + ['value' => 'position', 'label' => 'Position'] + ]; + foreach ($this->catalogConfig->getAttributesUsedForSortBy() as $attribute) { + $sortFieldsOptions[] = ['value' => $attribute->getAttributeCode(), 'label' => $attribute->getStoreLabel()]; + } - $result = function () use ($sortFieldDefault) { - return $sortFieldDefault; + $data = [ + 'default' => $this->catalogConfig->getProductListDefaultSortBy($this->storeManager->getStore()->getId()), + 'options' => $sortFieldsOptions, + ]; + + $result = function () use ($data) { + return $data; }; return $this->valueFactory->create($result); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldsOptions.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldsOptions.php deleted file mode 100644 index b644fa10a21c8..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFieldsOptions.php +++ /dev/null @@ -1,61 +0,0 @@ -<?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\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\Resolver\Value; -use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; -use Magento\Framework\GraphQl\Query\ResolverInterface; - -/** - * Retrieves the sort fields options information object - */ -class SortFieldsOptions implements ResolverInterface -{ - /** - * @var ValueFactory - */ - private $valueFactory; - - /** - * @var \Magento\Catalog\Model\Config - */ - private $catalogConfig; - - /** - * @param ValueFactory $valueFactory - * @param \Magento\Catalog\Model\Config $catalogConfig - */ - public function __construct( - ValueFactory $valueFactory, - \Magento\Catalog\Model\Config $catalogConfig - ) { - $this->valueFactory = $valueFactory; - $this->catalogConfig = $catalogConfig; - } - - /** - * {@inheritDoc} - */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) : Value - { - $sortFieldsOptions = [ - ['key' => 'position', 'label' => 'Position'] - ]; - foreach ($this->catalogConfig->getAttributesUsedForSortBy() as $attribute) { - $sortFieldsOptions[] = ['key' => $attribute->getAttributeCode(), 'label' => $attribute->getStoreLabel()]; - } - - $result = function () use ($sortFieldsOptions) { - return $sortFieldsOptions; - }; - - return $this->valueFactory->create($result); - } -} diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index 0c6d41a3051e2..5446e99c4c314 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -25,7 +25,7 @@ input FilterTypeInput @doc(description: "FilterTypeInput specifies which action type SearchResultPageInfo @doc(description: "SearchResultPageInfo provides navigation for the query response") { page_size: Int @doc(description: "Specifies the maximum number of items to return") current_page: Int @doc(description: "Specifies which page of results to return") - sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields") + sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFields") } enum SortEnum @doc(description: "This enumeration indicates whether to return results in ascending or descending order") { @@ -34,11 +34,11 @@ enum SortEnum @doc(description: "This enumeration indicates whether to return re } type SortField { - key: String @doc(description: "Attribute code of sort field") + value: String @doc(description: "Attribute code of sort field") label: String @doc(description: "Label of sort field") } type SortFields @doc(description: "SortFields contains a default value for sort fields and all available sort fields") { - default: String @doc(description: "Default value of sort fields") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFieldDefault") - options: [SortField] @doc(description: "Available sort fields") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFieldsOptions") + default: String @doc(description: "Default value of sort fields") + options: [SortField] @doc(description: "Available sort fields") } \ No newline at end of file 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 54380bf79937a..49371973f1001 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -516,7 +516,7 @@ public function testQueryProductsInCurrentPageSortedByPriceASC() default options { - key + value label } } @@ -543,6 +543,9 @@ public function testQueryProductsInCurrentPageSortedByPriceASC() $this->assertArrayHasKey('options', $response['products']['page_info']['sort_fields']); $this->assertArrayHasKey('default', $response['products']['page_info']['sort_fields']); $this->assertEquals('position', $response['products']['page_info']['sort_fields']['default']); + $this->assertArrayHasKey('value', $response['products']['page_info']['sort_fields']['options'][0]); + $this->assertArrayHasKey('label', $response['products']['page_info']['sort_fields']['options'][0]); + $this->assertEquals(['value'=>'position', 'label' => 'Position'], $response['products']['page_info']['sort_fields']['options'][0]); } /** From ba3a3fc682e89d322b7e3a67611c8e09228fef48 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Thu, 17 May 2018 16:16:43 -0500 Subject: [PATCH 074/333] MC-86: Admin should be able to remove a configuration from a Configurable Product --- ...reateProductConfigurationsPanelSection.xml | 1 + .../AdminConfigurableProductUpdateTest.xml | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml index 737d2b7cd0740..a097dfa4ad764 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -21,6 +21,7 @@ <element name="createNewValue" type="input" selector=".action-create-new" timeout="30"/> <element name="attributeName" type="input" selector="li[data-attribute-option-title=''] .admin__field-create-new .admin__control-text"/> <element name="saveAttribute" type="button" selector="li[data-attribute-option-title=''] .action-save" timeout="30"/> + <element name="attributeCheckboxByIndex" type="input" selector="li.attribute-option:nth-of-type({{var1}}) input" parameterized="true"/> <element name="applyUniquePricesByAttributeToEachSku" type="radio" selector=".admin__field-label[for='apply-unique-prices-radio']"/> <element name="selectAttribute" type="select" selector="#select-each-price" timeout="30"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml index 646bf9f65bbe5..26316dda3ad26 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml @@ -246,4 +246,52 @@ <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> </test> + + <test name="AdminConfigurableProductRemoveConfigurationTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to remove a configuration from a Configurable Product"/> + <description value="Admin should be able to remove a configuration from a Configurable Product"/> + <testCaseId value="MC-86"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="logout"/> + </after> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- + After saving, we are still on the product edit page. There is no need to reload or go to this page + again, because a round trip to the server has already happened. + --> + + <!-- Remove a configuration option from the configurable product --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckboxByIndex('1')}}" stepKey="deselectOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!-- Verify that the removed option is not present in the storefront --> + <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPage"/> + <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> + </test> </tests> From 5c277cff86956e4c7e40ab836ffb33693f9bfc36 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Thu, 17 May 2018 13:40:53 -0500 Subject: [PATCH 075/333] MAGETWO-91241: Build stabilization for Product list configuration with Magento Condition Tree --- .../web/conditions-data-normalizer.js | 44 ++++++++++++++++--- app/code/Magento/Ui/etc/ui_configuration.xsd | 1 + .../etc/definition/colorPicker.xsd | 13 ++++++ .../web/conditions-data-normalizer.test.js | 4 ++ 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js b/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js index 5a9ee9b4f68d8..ce50a3139445d 100644 --- a/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js +++ b/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js @@ -7,9 +7,15 @@ define([ 'jquery', 'underscore' ], function ($, _) { + 'use strict'; + var ConditionsDataNormalizer = new Class.create(); ConditionsDataNormalizer.prototype = { + + /** + * Initializes component. + */ initialize: function () { this.patterns = { validate: /^[a-z0-9_-][a-z0-9_-]*(?:\[(?:\d*|[a-z0-9_-]+)\])*$/i, @@ -46,13 +52,13 @@ define([ * */ normalize: function normalize(value) { - var _this = this; + var el, _this = this; this.pushes = {}; this.data = {}; _.each(value, function (e, i) { - var el = {}; + el = {}; el[i] = e; _this._addPair({ @@ -64,20 +70,33 @@ define([ return this.data; }, + /** + * @param {Object} base + * @param {String} key + * @param {String} value + * @return {Object} + * @private + */ _build: function build(base, key, value) { base[key] = value; return base; }, + /** + * @param {Object} root + * @param {String} value + * @return {*} + * @private + */ _makeObject: function makeObject(root, value) { var keys = root.match(this.patterns.key), - k; // nest, nest, ..., nest + k, idx; // nest, nest, ..., nest while ((k = keys.pop()) !== undefined) { // foo[] if (this.patterns.push.test(k)) { - var idx = this._incrementPush(root.replace(/\[\]$/, '')); + idx = this._incrementPush(root.replace(/\[\]$/, '')); value = this._build([], idx, value); } // foo[n] else if (this.patterns.fixed.test(k)) { @@ -91,6 +110,11 @@ define([ return value; }, + /** + * @param {String} key + * @return {Number} + * @private + */ _incrementPush: function incrementPush(key) { if (this.pushes[key] === undefined) { this.pushes[key] = 0; @@ -99,10 +123,20 @@ define([ return this.pushes[key]++; }, + /** + * @param {Object} pair + * @return {Object} + * @private + */ _addPair: function addPair(pair) { - if (!this.patterns.validate.test(pair.name)) return this; var obj = this._makeObject(pair.name, pair.value); + + if (!this.patterns.validate.test(pair.name)) { + return this; + } + this.data = $.extend(true, this.data, obj); + return this; } }; diff --git a/app/code/Magento/Ui/etc/ui_configuration.xsd b/app/code/Magento/Ui/etc/ui_configuration.xsd index c43e45eb8e111..b839593a38f47 100644 --- a/app/code/Magento/Ui/etc/ui_configuration.xsd +++ b/app/code/Magento/Ui/etc/ui_configuration.xsd @@ -219,6 +219,7 @@ <xs:element name="checkbox" type="formElementCheckbox" maxOccurs="unbounded"/> <xs:element name="checkboxset" type="formElementCheckboxset" maxOccurs="unbounded"/> <xs:element name="email" type="formElementEmail" maxOccurs="unbounded"/> + <xs:element name="colorPicker" type="formElementColorPicker" maxOccurs="unbounded"/> <xs:element name="select" type="formElementSelect" maxOccurs="unbounded"/> <xs:element name="multiselect" type="formElementMultiselect" maxOccurs="unbounded"/> <xs:element name="text" type="formElementText" maxOccurs="unbounded"/> diff --git a/app/code/Magento/Ui/view/base/ui_component/etc/definition/colorPicker.xsd b/app/code/Magento/Ui/view/base/ui_component/etc/definition/colorPicker.xsd index 509ef2aad2dee..dea1f7ce059e9 100644 --- a/app/code/Magento/Ui/view/base/ui_component/etc/definition/colorPicker.xsd +++ b/app/code/Magento/Ui/view/base/ui_component/etc/definition/colorPicker.xsd @@ -22,6 +22,19 @@ <xs:attributeGroup ref="ui_element_attributes"/> </xs:complexType> + <xs:complexType name="formElementColorPicker"> + <xs:sequence> + <xs:element name="settings" minOccurs="0" maxOccurs="1"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:group ref="componentColorPickerSettings"/> + </xs:choice> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attributeGroup ref="ui_element_attributes"/> + </xs:complexType> + <xs:group name="componentColorPickerSettings"> <xs:choice> <xs:group ref="abstractSettings"/> diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js index e2307e05e8101..21c04d098ae6c 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.test.js @@ -29,6 +29,7 @@ define([ foo: 'bar', bar: 123 }); + expect(normal.foo).toEqual('bar'); expect(normal.bar).toEqual(123); }); @@ -38,6 +39,7 @@ define([ 'foo[value]': 'bar', 'foo[name]': 123 }); + expect(normal.foo.value).toEqual('bar'); expect(normal.foo.name).toEqual(123); }); @@ -47,6 +49,7 @@ define([ 'foo[prefix][value]': 'bar', 'foo[prefix][name]': 123 }); + expect(normal.foo.prefix.value).toEqual('bar'); expect(normal.foo.prefix.name).toEqual(123); }); @@ -57,6 +60,7 @@ define([ 'foo[1][value]': 123, 'foo[1--1]': 321 }); + expect(normal.foo['1'].name).toEqual('bar'); expect(normal.foo['1'].value).toEqual(123); expect(normal.foo['1--1']).toEqual(321); From f7f2f8121221c3295dd327803e3a46da734f1e91 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Thu, 17 May 2018 13:40:53 -0500 Subject: [PATCH 076/333] MAGETWO-91241: Build stabilization for Product list configuration with Magento Condition Tree --- .../Rule/view/adminhtml/web/conditions-data-normalizer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js b/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js index ce50a3139445d..5a5d73ec34ef1 100644 --- a/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js +++ b/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js @@ -5,7 +5,8 @@ define([ 'jquery', - 'underscore' + 'underscore', + 'prototype' ], function ($, _) { 'use strict'; From 7f124c6848d22ca4bf51fe18e6891039dc707d08 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Thu, 17 May 2018 23:15:01 -0500 Subject: [PATCH 077/333] MAGETWO-71425: Product list configuration with Magento Condition Tree - Updated js to remove prototype - Fixed static errors --- .../web/conditions-data-normalizer.js | 32 ++++++++----------- .../Test/Less/_files/whitelist/common.txt | 4 +-- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js b/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js index 5a5d73ec34ef1..c9c36c4fa585a 100644 --- a/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js +++ b/app/code/Magento/Rule/view/adminhtml/web/conditions-data-normalizer.js @@ -5,28 +5,24 @@ define([ 'jquery', - 'underscore', - 'prototype' + 'underscore' ], function ($, _) { 'use strict'; - var ConditionsDataNormalizer = new Class.create(); + /** + * @constructor + */ + var ConditionsDataNormalizer = function () { + this.patterns = { + validate: /^[a-z0-9_-][a-z0-9_-]*(?:\[(?:\d*|[a-z0-9_-]+)\])*$/i, + key: /[a-z0-9_-]+|(?=\[\])/gi, + push: /^$/, + fixed: /^\d+$/, + named: /^[a-z0-9_-]+$/i + }; + }; ConditionsDataNormalizer.prototype = { - - /** - * Initializes component. - */ - initialize: function () { - this.patterns = { - validate: /^[a-z0-9_-][a-z0-9_-]*(?:\[(?:\d*|[a-z0-9_-]+)\])*$/i, - key: /[a-z0-9_-]+|(?=\[\])/gi, - push: /^$/, - fixed: /^\d+$/, - named: /^[a-z0-9_-]+$/i - }; - }, - /** * Will convert an object: * { @@ -49,8 +45,6 @@ define([ * } * } * } - * - * */ normalize: function normalize(value) { var el, _this = this; diff --git a/dev/tests/static/testsuite/Magento/Test/Less/_files/whitelist/common.txt b/dev/tests/static/testsuite/Magento/Test/Less/_files/whitelist/common.txt index 4e4fbefd7871d..60f6f9b105ee2 100644 --- a/dev/tests/static/testsuite/Magento/Test/Less/_files/whitelist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Less/_files/whitelist/common.txt @@ -1,2 +1,2 @@ -theme * / -library * / \ No newline at end of file +# Format: <componentType=module|library|theme|language|*> <componentName> <globPattern> or simply <globPattern> +* * / From 060fdf6edfbd14b2a785b7b0302a414438550c2a Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 18 May 2018 11:25:53 +0300 Subject: [PATCH 078/333] MAGETWO-89408: [2.3] Failed Magento\Test\Integrity\ClassesTest::testClassReferences --- .../Magento/Test/Integrity/ClassesTest.php | 120 ++++++++++++------ .../Integrity/_files/blacklist/reference.txt | 6 +- 2 files changed, 84 insertions(+), 42 deletions(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php index b754536fd5b18..5c1e342e1bc81 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php @@ -11,20 +11,40 @@ use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\App\Utility\Files; +/** + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + */ class ClassesTest extends \PHPUnit\Framework\TestCase { + /** + * @var ComponentRegistrar + */ + private $componentRegistrar; + /** * List of already found classes to avoid checking them over and over again * * @var array */ - protected static $_existingClasses = []; + private $existingClasses = []; - protected static $_keywordsBlacklist = ["String", "Array", "Boolean", "Element"]; + /** + * @var array + */ + private static $keywordsBlacklist = ["String", "Array", "Boolean", "Element"]; - protected static $_namespaceBlacklist = null; + /** + * @var array|null + */ + private $referenceBlackList = null; - protected static $_referenceBlackList = null; + /** + * Set Up + */ + protected function setUp() + { + $this->componentRegistrar = new ComponentRegistrar(); + } public function testPhpFiles() { @@ -69,9 +89,9 @@ function ($file) { $classes ); - $this->_collectResourceHelpersPhp($contents, $classes); + $this->collectResourceHelpersPhp($contents, $classes); - $this->_assertClassesExist($classes, $file); + $this->assertClassesExist($classes, $file); }, Files::init()->getPhpFiles( Files::INCLUDE_APP_CODE @@ -90,7 +110,7 @@ function ($file) { * @param string $contents * @param array &$classes */ - protected function _collectResourceHelpersPhp($contents, &$classes) + private function collectResourceHelpersPhp($contents, &$classes) { $regex = '/(?:\:\:|\->)getResourceHelper\(\s*\'([a-z\d\\\\]+)\'\s*\)/ix'; $matches = Classes::getAllMatches($contents, $regex); @@ -108,7 +128,7 @@ public function testConfigFiles() */ function ($path) { $classes = Classes::collectClassesInConfig(simplexml_load_file($path)); - $this->_assertClassesExist($classes, $path); + $this->assertClassesExist($classes, $path); }, Files::init()->getMainConfigFiles() ); @@ -145,7 +165,7 @@ function ($path) { } $classes = array_merge($classes, Classes::collectLayoutClasses($xml)); - $this->_assertClassesExist(array_unique($classes), $path); + $this->assertClassesExist(array_unique($classes), $path); }, Files::init()->getLayoutFiles() ); @@ -161,7 +181,7 @@ function ($path) { * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - protected function _assertClassesExist($classes, $path) + private function assertClassesExist($classes, $path) { if (!$classes) { return; @@ -177,7 +197,7 @@ protected function _assertClassesExist($classes, $path) } else { $this->assertTrue( isset( - self::$_existingClasses[$class] + $this->existingClasses[$class] ) || Files::init()->classFileExists( $class ) || Classes::isVirtual( @@ -187,7 +207,7 @@ protected function _assertClassesExist($classes, $path) ) ); } - self::$_existingClasses[$class] = 1; + $this->existingClasses[$class] = 1; } catch (\PHPUnit\Framework\AssertionFailedError $e) { $badClasses[] = '\\' . $class; } @@ -230,7 +250,7 @@ function ($file) { $classParts = explode(' ', $classNameMatch[0]); $className = array_pop($classParts); - $this->_assertClassNamespace($file, $relativePath, $contents, $className); + $this->assertClassNamespace($file, $relativePath, $contents, $className); }, Files::init()->getPhpFiles() ); @@ -245,7 +265,7 @@ function ($file) { * @param string $contents * @param string $className */ - protected function _assertClassNamespace($file, $relativePath, $contents, $className) + private function assertClassNamespace($file, $relativePath, $contents, $className) { $namespacePattern = '/(Magento|Zend)\/[a-zA-Z]+[^\.]+/'; $formalPattern = '/^namespace\s[a-zA-Z]+(\\\\[a-zA-Z0-9]+)*/m'; @@ -349,7 +369,7 @@ function ($file) { $vendorClasses = array_filter($vendorClasses, 'strlen'); $vendorClasses = $this->referenceBlacklistFilter($vendorClasses); if (!empty($vendorClasses)) { - $this->_assertClassesExist($vendorClasses, $file); + $this->assertClassesExist($vendorClasses, $file); } if (!empty($result3['exception']) && $result3['exception'][0] != "") { @@ -368,7 +388,7 @@ function ($file) { $badClasses = $this->referenceBlacklistFilter($badClasses); $badClasses = $this->removeSpecialCases($badClasses, $file, $contents, $namespacePath); - $this->_assertClassReferences($badClasses, $file); + $this->assertClassReferences($badClasses, $file); }, Files::init()->getPhpFiles() ); @@ -380,7 +400,7 @@ function ($file) { * @param $aliasClasses * @param $badClasses */ - protected function handleAliasClasses($aliasClasses, $badClasses) + private function handleAliasClasses($aliasClasses, $badClasses) { foreach ($aliasClasses as $aliasClass) { foreach ($badClasses as $badClass) { @@ -397,24 +417,33 @@ protected function handleAliasClasses($aliasClasses, $badClasses) * @param $classes * @return array */ - protected function referenceBlacklistFilter($classes) + private function referenceBlacklistFilter($classes) { // exceptions made for the files from the blacklist - self::_setReferenceBlacklist(); + $classes = $this->getReferenceBlacklist(); foreach ($classes as $class) { - if (in_array($class, self::$_referenceBlackList)) { + if (in_array($class, $this->referenceBlackList)) { unset($classes[array_search($class, $classes)]); } } return $classes; } - protected function _setReferenceBlacklist() + /** + * Returns array of class names from black list. + * + * @return array + */ + private function getReferenceBlacklist() { - if (!isset(self::$_referenceBlackList)) { - $blackList = file(__DIR__ . '/_files/blacklist/reference.txt', FILE_IGNORE_NEW_LINES); - self::$_referenceBlackList = $blackList; + if (!isset($this->referenceBlackList)) { + $this->referenceBlackList = file( + __DIR__ . '/_files/blacklist/reference.txt', + FILE_IGNORE_NEW_LINES + ); } + + return $this->referenceBlackList; } /** @@ -426,18 +455,21 @@ protected function _setReferenceBlacklist() * @param string $namespacePath * @return array */ - protected function removeSpecialCases($badClasses, $file, $contents, $namespacePath) + private function removeSpecialCases($badClasses, $file, $contents, $namespacePath) { foreach ($badClasses as $badClass) { // Remove valid usages of Magento modules from the list // for example: 'Magento_Sales::actions_edit' - if (preg_match('/Magento_[A-Z0-9][a-z0-9]*/', $badClass)) { - unset($badClasses[array_search($badClass, $badClasses)]); - continue; + if (preg_match('/^[A-Z][a-z]+_[A-Z0-9][a-z0-9]+$/', $badClass)) { + $moduleDir = $this->componentRegistrar->getPath(ComponentRegistrar::MODULE, $badClass); + if ($moduleDir !== null) { + unset($badClasses[array_search($badClass, $badClasses)]); + continue; + } } // Remove usage of key words such as "Array", "String", and "Boolean" - if (in_array($badClass, self::$_keywordsBlacklist)) { + if (in_array($badClass, self::$keywordsBlacklist)) { unset($badClasses[array_search($badClass, $badClasses)]); continue; } @@ -482,12 +514,11 @@ protected function removeSpecialCases($badClasses, $file, $contents, $namespaceP */ private function removeSpecialCasesNonFullyQualifiedClassNames($namespacePath, &$badClasses, $badClass) { - $componentRegistrar = new ComponentRegistrar(); $namespaceParts = explode('/', $namespacePath); $moduleDir = null; if (isset($namespaceParts[1])) { $moduleName = array_shift($namespaceParts) . '_' . array_shift($namespaceParts); - $moduleDir = $componentRegistrar->getPath(ComponentRegistrar::MODULE, $moduleName); + $moduleDir = $this->componentRegistrar->getPath(ComponentRegistrar::MODULE, $moduleName); } if ($moduleDir) { $fullPath = $moduleDir . '/' . implode('/', $namespaceParts) . '/' . @@ -505,7 +536,7 @@ private function removeSpecialCasesNonFullyQualifiedClassNames($namespacePath, & unset($badClasses[array_search($badClass, $badClasses)]); return true; } else { - return $this->removeSpecialCasesForAllOthers($componentRegistrar, $namespacePath, $badClass, $badClasses); + return $this->removeSpecialCasesForAllOthers($namespacePath, $badClass, $badClasses); } } @@ -516,11 +547,10 @@ private function removeSpecialCasesNonFullyQualifiedClassNames($namespacePath, & * @param string $badClass * @return null|string */ - protected function getLibraryDirByPath($namespacePath, $badClass) + private function getLibraryDirByPath($namespacePath, $badClass) { $libraryDir = null; $fullPath = null; - $componentRegistrar = new ComponentRegistrar(); $namespaceParts = explode('/', $namespacePath); if (isset($namespaceParts[1]) && $namespaceParts[1]) { $vendor = array_shift($namespaceParts); @@ -529,17 +559,26 @@ protected function getLibraryDirByPath($namespacePath, $badClass) $subLib = $namespaceParts[0]; $subLib = strtolower(preg_replace('/(.)([A-Z])/', "$1-$2", $subLib)); $libraryName = $vendor . '/' . $lib . '-' . $subLib; - $libraryDir = $componentRegistrar->getPath(ComponentRegistrar::LIBRARY, strtolower($libraryName)); + $libraryDir = $this->componentRegistrar->getPath( + ComponentRegistrar::LIBRARY, + strtolower($libraryName) + ); if ($libraryDir) { array_shift($namespaceParts); } else { $libraryName = $vendor . '/' . $lib; - $libraryDir = $componentRegistrar->getPath(ComponentRegistrar::LIBRARY, strtolower($libraryName)); + $libraryDir = $this->componentRegistrar->getPath( + ComponentRegistrar::LIBRARY, + strtolower($libraryName) + ); } } else { $lib = strtolower(preg_replace('/(.)([A-Z])/', "$1-$2", $lib)); $libraryName = $vendor . '/' . $lib; - $libraryDir = $componentRegistrar->getPath(ComponentRegistrar::LIBRARY, strtolower($libraryName)); + $libraryDir = $this->componentRegistrar->getPath( + ComponentRegistrar::LIBRARY, + strtolower($libraryName) + ); } } if ($libraryDir) { @@ -550,13 +589,12 @@ protected function getLibraryDirByPath($namespacePath, $badClass) } /** - * @param ComponentRegistrar $componentRegistrar * @param string $namespacePath * @param string $badClass * @param array $badClasses * @return bool */ - private function removeSpecialCasesForAllOthers($componentRegistrar, $namespacePath, $badClass, &$badClasses) + private function removeSpecialCasesForAllOthers($namespacePath, $badClass, &$badClasses) { // Remove usage of classes that do NOT using fully-qualified class names (possibly under same namespace) $directories = [ @@ -571,7 +609,7 @@ private function removeSpecialCasesForAllOthers($componentRegistrar, $namespaceP BP . '/dev/tests/static/testsuite/', BP . '/setup/src/', ]; - $libraryPaths = $componentRegistrar->getPaths(ComponentRegistrar::LIBRARY); + $libraryPaths = $this->componentRegistrar->getPaths(ComponentRegistrar::LIBRARY); $directories = array_merge($directories, $libraryPaths); // Full list of directories where there may be namespace classes foreach ($directories as $directory) { @@ -590,7 +628,7 @@ private function removeSpecialCasesForAllOthers($componentRegistrar, $namespaceP * @param array $badClasses * @param string $file */ - protected function _assertClassReferences($badClasses, $file) + private function assertClassReferences($badClasses, $file) { if (empty($badClasses)) { return; diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/reference.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/reference.txt index 2348a3b974b20..277532ce88bb1 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/reference.txt +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/reference.txt @@ -120,4 +120,8 @@ DoubleColon \Magento\Mtf\Client\ElementInterface BarFactory PartialNamespace\BarFactory -Product\OptionFactory \ No newline at end of file +Product\OptionFactory +Magento_Module +Magento_Framework +Magento_TestModule +Magento_Test \ No newline at end of file From 283a799a1b90419b029b2e26d8f5931086a2c735 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 18 May 2018 12:03:22 +0300 Subject: [PATCH 079/333] MAGETWO-90801: Can't create customer account on checkout success page when gender field is required --- .../Magento/Checkout/Block/Registration.php | 2 +- .../Checkout/Controller/Account/Create.php | 4 + .../Controller/Account/DelegateCreate.php | 58 ++++++ .../view/frontend/web/js/view/registration.js | 11 +- .../frontend/web/template/registration.html | 9 +- .../Api/AccountDelegationInterface.php | 31 +++ .../Model/Delegation/AccountDelegation.php | 53 +++++ .../Model/Delegation/Data/NewOperation.php | 54 +++++ .../Customer/Model/Delegation/Storage.php | 151 ++++++++++++++ .../ResourceModel/CustomerRepository.php | 66 +++++-- .../ResourceModel/CustomerRepositoryTest.php | 12 +- app/code/Magento/Customer/etc/di.xml | 3 + .../Api/OrderCustomerDelegateInterface.php | 25 +++ .../Sales/Model/Order/CustomerManagement.php | 118 +++++------ .../Model/Order/OrderCustomerDelegate.php | 54 +++++ .../Model/Order/OrderCustomerExtractor.php | 142 ++++++++++++++ .../AssignOrderToCustomerObserver.php | 54 +++++ .../Model/Order/CustomerManagementTest.php | 185 ------------------ app/code/Magento/Sales/etc/di.xml | 3 + app/code/Magento/Sales/etc/events.xml | 5 + .../Test/Block/Onepage/Registration.php | 2 +- ...ePageCheckoutOfflinePaymentMethodsTest.xml | 3 +- .../TestStep/CreateCustomerAccountStep.php | 51 ++++- .../TestCase/CreateSalesRuleEntityTest.xml | 2 + .../TrackingShipmentForPlacedOrderTest.xml | 2 + .../OrderCustomerDelegateInterfaceTest.php | 179 +++++++++++++++++ 26 files changed, 1008 insertions(+), 271 deletions(-) create mode 100644 app/code/Magento/Checkout/Controller/Account/DelegateCreate.php create mode 100644 app/code/Magento/Customer/Api/AccountDelegationInterface.php create mode 100644 app/code/Magento/Customer/Model/Delegation/AccountDelegation.php create mode 100644 app/code/Magento/Customer/Model/Delegation/Data/NewOperation.php create mode 100644 app/code/Magento/Customer/Model/Delegation/Storage.php create mode 100644 app/code/Magento/Sales/Api/OrderCustomerDelegateInterface.php create mode 100644 app/code/Magento/Sales/Model/Order/OrderCustomerDelegate.php create mode 100644 app/code/Magento/Sales/Model/Order/OrderCustomerExtractor.php create mode 100644 app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php delete mode 100644 app/code/Magento/Sales/Test/Unit/Model/Order/CustomerManagementTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Api/OrderCustomerDelegateInterfaceTest.php diff --git a/app/code/Magento/Checkout/Block/Registration.php b/app/code/Magento/Checkout/Block/Registration.php index 91ec85c1db0ed..e880230f50a74 100644 --- a/app/code/Magento/Checkout/Block/Registration.php +++ b/app/code/Magento/Checkout/Block/Registration.php @@ -91,7 +91,7 @@ public function getEmailAddress() */ public function getCreateAccountUrl() { - return $this->getUrl('checkout/account/create'); + return $this->getUrl('checkout/account/delegateCreate'); } /** diff --git a/app/code/Magento/Checkout/Controller/Account/Create.php b/app/code/Magento/Checkout/Controller/Account/Create.php index 2ee5d6d5528c3..dae0bb98be453 100644 --- a/app/code/Magento/Checkout/Controller/Account/Create.php +++ b/app/code/Magento/Checkout/Controller/Account/Create.php @@ -9,6 +9,10 @@ use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Exception\NoSuchEntityException; +/** + * @deprecated + * @see DelegateCreate + */ class Create extends \Magento\Framework\App\Action\Action { /** diff --git a/app/code/Magento/Checkout/Controller/Account/DelegateCreate.php b/app/code/Magento/Checkout/Controller/Account/DelegateCreate.php new file mode 100644 index 0000000000000..6c4c8b053e2ae --- /dev/null +++ b/app/code/Magento/Checkout/Controller/Account/DelegateCreate.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Controller\Account; + +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\Context; +use Magento\Checkout\Model\Session; +use Magento\Sales\Api\OrderCustomerDelegateInterface; + +/** + * Redirect guest customer for registration. + */ +class DelegateCreate extends Action +{ + /** + * @var OrderCustomerDelegateInterface + */ + private $delegateService; + + /** + * @var Session + */ + private $session; + + /** + * @param Context $context + * @param OrderCustomerDelegateInterface $customerDelegation + * @param Session $session + */ + public function __construct( + Context $context, + OrderCustomerDelegateInterface $customerDelegation, + Session $session + ) { + parent::__construct($context); + $this->delegateService = $customerDelegation; + $this->session = $session; + } + + /** + * {@inheritdoc} + */ + public function execute() + { + /** @var string|null $orderId */ + $orderId = $this->session->getLastOrderId(); + if (!$orderId) { + return $this->resultRedirectFactory->create()->setPath('/'); + } + + return $this->delegateService->delegateNew((int)$orderId); + } +} diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/registration.js b/app/code/Magento/Checkout/view/frontend/web/js/view/registration.js index c715b5c4d45ce..a7b3e18c06088 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/registration.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/registration.js @@ -38,7 +38,16 @@ define([ }, /** - * Create new user account + * @return String + */ + getUrl: function () { + return this.registrationUrl; + }, + + /** + * Create new user account. + * + * @deprecated */ createAccount: function () { this.creationStarted(true); diff --git a/app/code/Magento/Checkout/view/frontend/web/template/registration.html b/app/code/Magento/Checkout/view/frontend/web/template/registration.html index 256fc1968abfc..ea94726e5443e 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/registration.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/registration.html @@ -11,11 +11,8 @@ <!-- ko if: isFormVisible --> <p data-bind="i18n: 'You can track your order status by creating an account.'"></p> <p><span data-bind="i18n: 'Email Address'"></span>: <span data-bind="text: getEmailAddress()"></span></p> - <form method="post" data-bind="submit: createAccount"> - <input type="submit" class="action primary" data-bind="value: $t('Create an Account'), disable: creationStarted" /> + <form method="get" data-bind="attr: { action: getUrl() }"> + <input type="submit" class="action primary" data-bind="value: $t('Create an Account')" /> </form> - <!-- /ko --> - <!-- ko if: accountCreated --> - <p data-bind="i18n: 'A letter with further instructions will be sent to your email.'"></p> - <!-- /ko --> + <!--/ko--> </div> diff --git a/app/code/Magento/Customer/Api/AccountDelegationInterface.php b/app/code/Magento/Customer/Api/AccountDelegationInterface.php new file mode 100644 index 0000000000000..e3a738530c49d --- /dev/null +++ b/app/code/Magento/Customer/Api/AccountDelegationInterface.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Api; + +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\Controller\Result\Redirect; + +/** + * Delegating account actions from outside of customer module. + */ +interface AccountDelegationInterface +{ + /** + * Create redirect to default new account form. + * + * @param CustomerInterface $customer Pre-filled customer data. + * @param array|null $mixedData Add this data to new-customer event + * if the new customer is created. + * + * @return Redirect + */ + public function createRedirectForNew( + CustomerInterface $customer, + array $mixedData = null + ): Redirect; +} diff --git a/app/code/Magento/Customer/Model/Delegation/AccountDelegation.php b/app/code/Magento/Customer/Model/Delegation/AccountDelegation.php new file mode 100644 index 0000000000000..85c67213c4613 --- /dev/null +++ b/app/code/Magento/Customer/Model/Delegation/AccountDelegation.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Delegation; + +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\AccountDelegationInterface; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Controller\Result\RedirectFactory; + +/** + * {@inheritdoc} + */ +class AccountDelegation implements AccountDelegationInterface +{ + /** + * @var RedirectFactory + */ + private $redirectFactory; + + /** + * @var Storage + */ + private $storage; + + /** + * @param RedirectFactory $redirectFactory + * @param Storage $storage + */ + public function __construct( + RedirectFactory $redirectFactory, + Storage $storage + ) { + $this->redirectFactory = $redirectFactory; + $this->storage = $storage; + } + + /** + * {@inheritdoc} + */ + public function createRedirectForNew( + CustomerInterface $customer, + array $mixedData = null + ): Redirect { + $this->storage->storeNewOperation($customer, $mixedData); + + return $this->redirectFactory->create()->setPath('customer/account/create'); + } +} diff --git a/app/code/Magento/Customer/Model/Delegation/Data/NewOperation.php b/app/code/Magento/Customer/Model/Delegation/Data/NewOperation.php new file mode 100644 index 0000000000000..5dcefc2326794 --- /dev/null +++ b/app/code/Magento/Customer/Model/Delegation/Data/NewOperation.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Delegation\Data; + +use Magento\Customer\Api\Data\CustomerInterface; + +/** + * Data required for delegated new-account operation. + */ +class NewOperation +{ + /** + * @var CustomerInterface + */ + private $customer; + + /** + * @var array + */ + private $additionalData; + + /** + * @param CustomerInterface $customer + * @param array $additionalData + */ + public function __construct( + CustomerInterface $customer, + array $additionalData + ) { + $this->customer = $customer; + $this->additionalData = $additionalData; + } + + /** + * @return CustomerInterface + */ + public function getCustomer(): CustomerInterface + { + return $this->customer; + } + + /** + * @return array + */ + public function getAdditionalData(): array + { + return $this->additionalData; + } +} diff --git a/app/code/Magento/Customer/Model/Delegation/Storage.php b/app/code/Magento/Customer/Model/Delegation/Storage.php new file mode 100644 index 0000000000000..2b7e30a20f175 --- /dev/null +++ b/app/code/Magento/Customer/Model/Delegation/Storage.php @@ -0,0 +1,151 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Delegation; + +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\RegionInterface; +use Magento\Customer\Api\Data\RegionInterfaceFactory; +use Magento\Customer\Model\Delegation\Data\NewOperation; +use Magento\Customer\Model\Data\Customer; +use Magento\Customer\Model\Data\Address; +use Magento\Customer\Model\Session; +use Magento\Customer\Model\Session\Proxy as SessionProxy; +use Magento\Customer\Model\Delegation\Data\NewOperationFactory; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Psr\Log\LoggerInterface; + +/** + * Store data for delegated operations. + */ +class Storage +{ + /** + * @var Session + */ + private $session; + + /** + * @var NewOperationFactory + */ + private $newFactory; + + /** + * @var CustomerInterfaceFactory + */ + private $customerFactory; + + /** + * @var AddressInterfaceFactory + */ + private $addressFactory; + + /** + * @var RegionInterfaceFactory + */ + private $regionFactory; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param NewOperationFactory $newFactory + * @param CustomerInterfaceFactory $customerFactory + * @param AddressInterfaceFactory $addressFactory + * @param RegionInterfaceFactory $regionFactory + * @param LoggerInterface $logger + * @param SessionProxy $session + */ + public function __construct( + NewOperationFactory $newFactory, + CustomerInterfaceFactory $customerFactory, + AddressInterfaceFactory $addressFactory, + RegionInterfaceFactory $regionFactory, + LoggerInterface $logger, + SessionProxy $session + ) { + $this->newFactory = $newFactory; + $this->customerFactory = $customerFactory; + $this->addressFactory = $addressFactory; + $this->regionFactory = $regionFactory; + $this->logger = $logger; + $this->session = $session; + } + + /** + * Store data for new account operation. + * + * @param CustomerInterface $customer + * @param array $delegatedData + * + * @return void + */ + public function storeNewOperation(CustomerInterface $customer, array $delegatedData): void + { + /** @var Customer $customer */ + $customerData = $customer->__toArray(); + $addressesData = []; + if ($customer->getAddresses()) { + /** @var Address $address */ + foreach ($customer->getAddresses() as $address) { + $addressesData[] = $address->__toArray(); + } + } + $this->session->setCustomerFormData($customerData); + $this->session->setDelegatedNewCustomerData([ + 'customer' => $customerData, + 'addresses' => $addressesData, + 'delegated_data' => $delegatedData, + ]); + } + + /** + * Retrieve delegated new operation data and mark it as used. + * + * @return NewOperation|null + */ + public function consumeNewOperation() + { + try { + $serialized = $this->session->getDelegatedNewCustomerData(true); + } catch (\Throwable $exception) { + $this->logger->error($exception); + $serialized = null; + } + if (!$serialized) { + return null; + } + + /** @var AddressInterface[] $addresses */ + $addresses = []; + foreach ($serialized['addresses'] as $addressData) { + if (isset($addressData['region'])) { + /** @var RegionInterface $region */ + $region = $this->regionFactory->create( + ['data' => $addressData['region']] + ); + $addressData['region'] = $region; + } + $addresses[] = $this->addressFactory->create( + ['data' => $addressData] + ); + } + $customerData = $serialized['customer']; + $customerData['addresses'] = $addresses; + + return $this->newFactory->create([ + 'customer' => $this->customerFactory->create( + ['data' => $customerData] + ), + 'additionalData' => $serialized['delegated_data'], + ]); + } +} diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 91a593c347806..a0b9b83c982ed 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -7,16 +7,19 @@ namespace Magento\Customer\Model\ResourceModel; use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\Delegation\Data\NewOperation; use Magento\Customer\Model\Customer\NotificationStorage; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\ImageProcessorInterface; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Customer\Model\Delegation\Storage as DelegatedStorage; use Magento\Framework\App\ObjectManager; /** * Customer repository. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) */ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInterface { @@ -95,6 +98,11 @@ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInte */ private $notificationStorage; + /** + * @var DelegatedStorage + */ + private $delegatedStorage; + /** * @param \Magento\Customer\Model\CustomerFactory $customerFactory * @param \Magento\Customer\Model\Data\CustomerSecureFactory $customerSecureFactory @@ -111,6 +119,7 @@ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInte * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor * @param CollectionProcessorInterface $collectionProcessor * @param NotificationStorage $notificationStorage + * @param DelegatedStorage|null $delegatedStorage * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -128,7 +137,8 @@ public function __construct( ImageProcessorInterface $imageProcessor, \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor, CollectionProcessorInterface $collectionProcessor, - NotificationStorage $notificationStorage + NotificationStorage $notificationStorage, + DelegatedStorage $delegatedStorage = null ) { $this->customerFactory = $customerFactory; $this->customerSecureFactory = $customerSecureFactory; @@ -145,15 +155,21 @@ public function __construct( $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; $this->collectionProcessor = $collectionProcessor; $this->notificationStorage = $notificationStorage; + $this->delegatedStorage = $delegatedStorage + ?? ObjectManager::getInstance()->get(DelegatedStorage::class); } /** * {@inheritdoc} * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $passwordHash = null) { + /** @var NewOperation|null $delegatedNewOperation */ + $delegatedNewOperation = !$customer->getId() + ? $this->delegatedStorage->consumeNewOperation() : null; $prevCustomerData = null; $prevCustomerDataArr = null; if ($customer->getId()) { @@ -177,12 +193,18 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa ); $customer->setAddresses($origAddresses); - $customerModel = $this->customerFactory->create(['data' => $customerData]); + /** @var Customer $customerModel */ + $customerModel = $this->customerFactory->create( + ['data' => $customerData] + ); + //Model's actual ID field maybe different than "id" + //so "id" field from $customerData may be ignored. + $customerModel->setId($customer->getId()); + $storeId = $customerModel->getStoreId(); if ($storeId === null) { $customerModel->setStoreId($this->storeManager->getStore()->getId()); } - $customerModel->setId($customer->getId()); // Need to use attribute set or future updates can cause data loss if (!$customerModel->getAttributeSetId()) { @@ -199,24 +221,35 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa $customerModel->setRpToken(null); $customerModel->setRpTokenCreatedAt(null); } - if (!array_key_exists('default_billing', $customerArr) && - null !== $prevCustomerDataArr && - array_key_exists('default_billing', $prevCustomerDataArr) + if (!array_key_exists('default_billing', $customerArr) + && null !== $prevCustomerDataArr + && array_key_exists('default_billing', $prevCustomerDataArr) ) { - $customerModel->setDefaultBilling($prevCustomerDataArr['default_billing']); + $customerModel->setDefaultBilling( + $prevCustomerDataArr['default_billing'] + ); } - - if (!array_key_exists('default_shipping', $customerArr) && - null !== $prevCustomerDataArr && - array_key_exists('default_shipping', $prevCustomerDataArr) + if (!array_key_exists('default_shipping', $customerArr) + && null !== $prevCustomerDataArr + && array_key_exists('default_shipping', $prevCustomerDataArr) ) { - $customerModel->setDefaultShipping($prevCustomerDataArr['default_shipping']); + $customerModel->setDefaultShipping( + $prevCustomerDataArr['default_shipping'] + ); } $customerModel->save(); $this->customerRegistry->push($customerModel); $customerId = $customerModel->getId(); + if (!$customer->getAddresses() + && $delegatedNewOperation + && $delegatedNewOperation->getCustomer()->getAddresses() + ) { + $customer->setAddresses( + $delegatedNewOperation->getCustomer()->getAddresses() + ); + } if ($customer->getAddresses() !== null) { if ($customer->getId()) { $existingAddresses = $this->getById($customer->getId())->getAddresses(); @@ -245,10 +278,17 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa } $this->customerRegistry->remove($customerId); $savedCustomer = $this->get($customer->getEmail(), $customer->getWebsiteId()); + $this->eventManager->dispatch( 'customer_save_after_data_object', - ['customer_data_object' => $savedCustomer, 'orig_customer_data_object' => $prevCustomerData] + [ + 'customer_data_object' => $savedCustomer, + 'orig_customer_data_object' => $prevCustomerData, + 'delegate_data' => $delegatedNewOperation + ? $delegatedNewOperation->getAdditionalData() : [] + ] ); + return $savedCustomer; } 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 06133dd89d754..bd1dc774b5319 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -419,7 +419,11 @@ public function testSave() ->method('dispatch') ->with( 'customer_save_after_data_object', - ['customer_data_object' => $this->customer, 'orig_customer_data_object' => $origCustomer] + [ + 'customer_data_object' => $this->customer, + 'orig_customer_data_object' => $origCustomer, + 'delegate_data' => [], + ] ); $this->model->save($this->customer); @@ -646,7 +650,11 @@ public function testSaveWithPasswordHash() ->method('dispatch') ->with( 'customer_save_after_data_object', - ['customer_data_object' => $this->customer, 'orig_customer_data_object' => $origCustomer] + [ + 'customer_data_object' => $this->customer, + 'orig_customer_data_object' => $origCustomer, + 'delegate_data' => [], + ] ); $this->model->save($this->customer, $passwordHash); diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index 43c2b9cf7bb80..0d99c1145e81b 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -433,4 +433,7 @@ </argument> </arguments> </type> + <preference + for="Magento\Customer\Api\AccountDelegationInterface" + type="Magento\Customer\Model\Delegation\AccountDelegation" /> </config> diff --git a/app/code/Magento/Sales/Api/OrderCustomerDelegateInterface.php b/app/code/Magento/Sales/Api/OrderCustomerDelegateInterface.php new file mode 100644 index 0000000000000..2902903b0b7d0 --- /dev/null +++ b/app/code/Magento/Sales/Api/OrderCustomerDelegateInterface.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Api; + +use Magento\Framework\Controller\Result\Redirect; + +/** + * Delegate related to orders customers operations to Customer module. + */ +interface OrderCustomerDelegateInterface +{ + /** + * Redirect to Customer module new-account page to finish creating customer based on order data. + * + * @param int $orderId + * + * @return Redirect + */ + public function delegateNew(int $orderId): Redirect; +} diff --git a/app/code/Magento/Sales/Model/Order/CustomerManagement.php b/app/code/Magento/Sales/Model/Order/CustomerManagement.php index 466f3ff8adddb..ae3f940dbb2ba 100644 --- a/app/code/Magento/Sales/Model/Order/CustomerManagement.php +++ b/app/code/Magento/Sales/Model/Order/CustomerManagement.php @@ -6,12 +6,18 @@ namespace Magento\Sales\Model\Order; +use Magento\Customer\Api\Data\AddressInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\AlreadyExistsException; +use Magento\Sales\Api\Data\OrderAddressInterface; use Magento\Quote\Model\Quote\Address as QuoteAddress; +use Magento\Quote\Model\Quote\AddressFactory as QuoteAddressFactory; +use Magento\Sales\Api\Data\OrderInterface; /** * Class CustomerManagement + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CustomerManagement implements \Magento\Sales\Api\OrderCustomerManagementInterface { @@ -21,17 +27,17 @@ class CustomerManagement implements \Magento\Sales\Api\OrderCustomerManagementIn protected $accountManagement; /** - * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory + * @deprecated */ protected $customerFactory; /** - * @var \Magento\Customer\Api\Data\AddressInterfaceFactory + * @deprecated */ protected $addressFactory; /** - * @var \Magento\Customer\Api\Data\RegionInterfaceFactory + * @deprecated */ protected $regionFactory; @@ -41,15 +47,20 @@ class CustomerManagement implements \Magento\Sales\Api\OrderCustomerManagementIn protected $orderRepository; /** - * @var \Magento\Framework\DataObject\Copy + * @deprecated */ protected $objectCopyService; /** - * @var \Magento\Quote\Model\Quote\AddressFactory + * @var QuoteAddressFactory */ private $quoteAddressFactory; + /** + * @var OrderCustomerExtractor + */ + private $customerExtractor; + /** * @param \Magento\Framework\DataObject\Copy $objectCopyService * @param \Magento\Customer\Api\AccountManagementInterface $accountManagement @@ -57,7 +68,8 @@ class CustomerManagement implements \Magento\Sales\Api\OrderCustomerManagementIn * @param \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory * @param \Magento\Customer\Api\Data\RegionInterfaceFactory $regionFactory * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository - * @param \Magento\Quote\Model\Quote\AddressFactory|null $quoteAddressFactory + * @param QuoteAddressFactory|null $quoteAddressFactory + * @param OrderCustomerExtractor|null $orderCustomerExtractor */ public function __construct( \Magento\Framework\DataObject\Copy $objectCopyService, @@ -66,7 +78,8 @@ public function __construct( \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory, \Magento\Customer\Api\Data\RegionInterfaceFactory $regionFactory, \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, - \Magento\Quote\Model\Quote\AddressFactory $quoteAddressFactory = null + QuoteAddressFactory $quoteAddressFactory = null, + OrderCustomerExtractor $orderCustomerExtractor = null ) { $this->objectCopyService = $objectCopyService; $this->accountManagement = $accountManagement; @@ -74,9 +87,10 @@ public function __construct( $this->customerFactory = $customerFactory; $this->addressFactory = $addressFactory; $this->regionFactory = $regionFactory; - $this->quoteAddressFactory = $quoteAddressFactory ?: ObjectManager::getInstance()->get( - \Magento\Quote\Model\Quote\AddressFactory::class - ); + $this->quoteAddressFactory = $quoteAddressFactory + ?: ObjectManager::getInstance()->get(QuoteAddressFactory::class); + $this->customerExtractor = $orderCustomerExtractor + ?? ObjectManager::getInstance()->get(OrderCustomerExtractor::class); } /** @@ -86,50 +100,23 @@ public function create($orderId) { $order = $this->orderRepository->get($orderId); if ($order->getCustomerId()) { - throw new AlreadyExistsException(__("This order already has associated customer account")); - } - $customerData = $this->objectCopyService->copyFieldsetToTarget( - 'order_address', - 'to_customer', - $order->getBillingAddress(), - [] - ); - $addresses = $order->getAddresses(); - foreach ($addresses as $address) { - if (!$this->isNeededToSaveAddress($address->getData('quote_address_id'))) { - continue; - } - $addressData = $this->objectCopyService->copyFieldsetToTarget( - 'order_address', - 'to_customer_address', - $address, - [] + throw new AlreadyExistsException( + __('This order already has associated customer account') ); - /** @var \Magento\Customer\Api\Data\AddressInterface $customerAddress */ - $customerAddress = $this->addressFactory->create(['data' => $addressData]); - switch ($address->getAddressType()) { - case QuoteAddress::ADDRESS_TYPE_BILLING: - $customerAddress->setIsDefaultBilling(true); - break; - case QuoteAddress::ADDRESS_TYPE_SHIPPING: - $customerAddress->setIsDefaultShipping(true); - break; - } + } - if (is_string($address->getRegion())) { - /** @var \Magento\Customer\Api\Data\RegionInterface $region */ - $region = $this->regionFactory->create(); - $region->setRegion($address->getRegion()); - $region->setRegionCode($address->getRegionCode()); - $region->setRegionId($address->getRegionId()); - $customerAddress->setRegion($region); + $customer = $this->customerExtractor->extract($orderId); + /** @var AddressInterface[] $filteredAddresses */ + $filteredAddresses = []; + foreach ($customer->getAddresses() as $address) { + if ($this->needToSaveAddress($order, $address)) { + $filteredAddresses[] = $address; } - $customerData['addresses'][] = $customerAddress; } + $customer->setAddresses($filteredAddresses); - /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ - $customer = $this->customerFactory->create(['data' => $customerData]); $account = $this->accountManagement->createAccount($customer); + $order = $this->orderRepository->get($orderId); $order->setCustomerId($account->getId()); $order->setCustomerIsGuest(0); $this->orderRepository->save($order); @@ -138,21 +125,36 @@ public function create($orderId) } /** - * Check if we need to save address in address book. - * - * @param int $quoteAddressId + * @param OrderInterface $order + * @param AddressInterface $address * * @return bool */ - private function isNeededToSaveAddress($quoteAddressId) - { - $saveInAddressBook = true; + private function needToSaveAddress( + OrderInterface $order, + AddressInterface $address + ): bool { + /** @var OrderAddressInterface|null $orderAddress */ + $orderAddress = null; + if ($address->isDefaultBilling()) { + $orderAddress = $order->getBillingAddress(); + } elseif ($address->isDefaultShipping()) { + $orderAddress = $order->getShippingAddress(); + } + if ($orderAddress) { + $quoteAddressId = $orderAddress->getData('quote_address_id'); + if ($quoteAddressId) { + /** @var QuoteAddress $quote */ + $quote = $this->quoteAddressFactory->create() + ->load($quoteAddressId); + if ($quote && $quote->getId()) { + return (bool)(int)$quote->getData('save_in_address_book'); + } + } - $quoteAddress = $this->quoteAddressFactory->create()->load($quoteAddressId); - if ($quoteAddress && $quoteAddress->getId()) { - $saveInAddressBook = (int)$quoteAddress->getData('save_in_address_book'); + return true; } - return $saveInAddressBook; + return false; } } diff --git a/app/code/Magento/Sales/Model/Order/OrderCustomerDelegate.php b/app/code/Magento/Sales/Model/Order/OrderCustomerDelegate.php new file mode 100644 index 0000000000000..5d0cd4f37df5a --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/OrderCustomerDelegate.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\Order; + +use Magento\Customer\Api\AccountDelegationInterface; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Sales\Api\OrderCustomerDelegateInterface; +use Magento\Sales\Observer\AssignOrderToCustomerObserver; + +/** + * {@inheritdoc} + * + * @see AssignOrderToCustomerObserver + */ +class OrderCustomerDelegate implements OrderCustomerDelegateInterface +{ + /** + * @var OrderCustomerExtractor + */ + private $customerExtractor; + + /** + * @var AccountDelegationInterface + */ + private $delegateService; + + /** + * @param OrderCustomerExtractor $customerExtractor + * @param AccountDelegationInterface $delegateService + */ + public function __construct( + OrderCustomerExtractor $customerExtractor, + AccountDelegationInterface $delegateService + ) { + $this->customerExtractor = $customerExtractor; + $this->delegateService = $delegateService; + } + + /** + * {@inheritdoc} + */ + public function delegateNew(int $orderId): Redirect + { + return $this->delegateService->createRedirectForNew( + $this->customerExtractor->extract($orderId), + ['__sales_assign_order_id' => $orderId] + ); + } +} diff --git a/app/code/Magento/Sales/Model/Order/OrderCustomerExtractor.php b/app/code/Magento/Sales/Model/Order/OrderCustomerExtractor.php new file mode 100644 index 0000000000000..2a93f389e569f --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/OrderCustomerExtractor.php @@ -0,0 +1,142 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\Order; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Framework\DataObject\Copy as CopyService; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\RegionInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory as AddressFactory; +use Magento\Quote\Model\Quote\Address as QuoteAddress; +use Magento\Customer\Api\Data\RegionInterfaceFactory as RegionFactory; +use Magento\Customer\Api\Data\CustomerInterfaceFactory as CustomerFactory; +use Magento\Quote\Api\Data\AddressInterfaceFactory as QuoteAddressFactory; + +/** + * Extract customer data from an order. + */ +class OrderCustomerExtractor +{ + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var CopyService + */ + private $objectCopyService; + + /** + * @var AddressFactory + */ + private $addressFactory; + + /** + * @var RegionFactory + */ + private $regionFactory; + + /** + * @var CustomerFactory + */ + private $customerFactory; + + /** + * @var QuoteAddressFactory + */ + private $quoteAddressFactory; + + /** + * @param OrderRepositoryInterface $orderRepository + * @param CustomerRepositoryInterface $customerRepository + * @param CopyService $objectCopyService + * @param AddressFactory $addressFactory + * @param RegionFactory $regionFactory + * @param CustomerFactory $customerFactory + * @param QuoteAddressFactory $quoteAddressFactory + */ + public function __construct( + OrderRepositoryInterface $orderRepository, + CustomerRepositoryInterface $customerRepository, + CopyService $objectCopyService, + AddressFactory $addressFactory, + RegionFactory $regionFactory, + CustomerFactory $customerFactory, + QuoteAddressFactory $quoteAddressFactory + ) { + $this->orderRepository = $orderRepository; + $this->customerRepository = $customerRepository; + $this->objectCopyService = $objectCopyService; + $this->addressFactory = $addressFactory; + $this->regionFactory = $regionFactory; + $this->customerFactory = $customerFactory; + $this->quoteAddressFactory = $quoteAddressFactory; + } + + /** + * @param int $orderId + * + * @return CustomerInterface + */ + public function extract(int $orderId): CustomerInterface + { + $order = $this->orderRepository->get($orderId); + + //Simply return customer from DB. + if ($order->getCustomerId()) { + return $this->customerRepository->getById($order->getCustomerId()); + } + + //Prepare customer data from order data if customer doesn't exist yet. + $customerData = $this->objectCopyService->copyFieldsetToTarget( + 'order_address', + 'to_customer', + $order->getBillingAddress(), + [] + ); + $addresses = $order->getAddresses(); + foreach ($addresses as $address) { + $addressData = $this->objectCopyService->copyFieldsetToTarget( + 'order_address', + 'to_customer_address', + $address, + [] + ); + /** @var AddressInterface $customerAddress */ + $customerAddress = $this->addressFactory->create(['data' => $addressData]); + switch ($address->getAddressType()) { + case QuoteAddress::ADDRESS_TYPE_BILLING: + $customerAddress->setIsDefaultBilling(true); + break; + case QuoteAddress::ADDRESS_TYPE_SHIPPING: + $customerAddress->setIsDefaultShipping(true); + break; + } + + if (is_string($address->getRegion())) { + /** @var RegionInterface $region */ + $region = $this->regionFactory->create(); + $region->setRegion($address->getRegion()); + $region->setRegionCode($address->getRegionCode()); + $region->setRegionId($address->getRegionId()); + $customerAddress->setRegion($region); + } + $customerData['addresses'][] = $customerAddress; + } + + return $this->customerFactory->create(['data' => $customerData]); + } +} diff --git a/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php b/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php new file mode 100644 index 0000000000000..cade86d18e935 --- /dev/null +++ b/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Observer; + +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + +/** + * Assign order to customer created after issuing guest order. + */ +class AssignOrderToCustomerObserver implements ObserverInterface +{ + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @param OrderRepositoryInterface $orderRepository + */ + public function __construct(OrderRepositoryInterface $orderRepository) + { + $this->orderRepository = $orderRepository; + } + + /** + * {@inheritdoc} + */ + public function execute(Observer $observer) + { + $event = $observer->getEvent(); + /** @var CustomerInterface $customer */ + $customer = $event->getData('customer_data_object'); + /** @var array $delegateData */ + $delegateData = $event->getData('delegate_data'); + if (array_key_exists('__sales_assign_order_id', $delegateData)) { + $orderId = $delegateData['__sales_assign_order_id']; + $order = $this->orderRepository->get($orderId); + if (!$order->getCustomerId()) { + //if customer ID wasn't already assigned then assigning. + $order->setCustomerId($customer->getId()); + $order->setCustomerIsGuest(0); + $this->orderRepository->save($order); + } + } + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/CustomerManagementTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/CustomerManagementTest.php deleted file mode 100644 index 2794860793ed6..0000000000000 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/CustomerManagementTest.php +++ /dev/null @@ -1,185 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Test\Unit\Model\Order; - -use Magento\Quote\Model\Quote\Address; -use Magento\Sales\Api\Data\OrderAddressInterface; - -/** - * Class BuilderTest - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class CustomerManagementTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $objectCopyService; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $accountManagement; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $customerFactory; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $addressFactory; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $orderRepository; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $regionFactory; - - /** - * @var \Magento\Sales\Model\Order\CustomerManagement - */ - protected $service; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $quoteAddressFactory; - - protected function setUp() - { - $this->objectCopyService = $this->createMock(\Magento\Framework\DataObject\Copy::class); - $this->accountManagement = $this->createMock(\Magento\Customer\Api\AccountManagementInterface::class); - $this->customerFactory = $this->createPartialMock( - \Magento\Customer\Api\Data\CustomerInterfaceFactory::class, - ['create'] - ); - $this->addressFactory = $this->createPartialMock( - \Magento\Customer\Api\Data\AddressInterfaceFactory::class, - ['create'] - ); - $this->regionFactory = $this->createPartialMock( - \Magento\Customer\Api\Data\RegionInterfaceFactory::class, - ['create'] - ); - $this->orderRepository = $this->createMock(\Magento\Sales\Api\OrderRepositoryInterface::class); - $this->quoteAddressFactory = $this->createMock(\Magento\Quote\Model\Quote\AddressFactory::class); - - $this->service = new \Magento\Sales\Model\Order\CustomerManagement( - $this->objectCopyService, - $this->accountManagement, - $this->customerFactory, - $this->addressFactory, - $this->regionFactory, - $this->orderRepository, - $this->quoteAddressFactory - ); - } - - /** - * @expectedException \Magento\Framework\Exception\AlreadyExistsException - */ - public function testCreateThrowsExceptionIfCustomerAlreadyExists() - { - $orderMock = $this->createMock(\Magento\Sales\Api\Data\OrderInterface::class); - $orderMock->expects($this->once())->method('getCustomerId')->will($this->returnValue('customer_id')); - $this->orderRepository->expects($this->once())->method('get')->with(1)->will($this->returnValue($orderMock)); - $this->service->create(1); - } - - public function testCreateCreatesCustomerBasedonGuestOrder() - { - $orderMock = $this->createMock(\Magento\Sales\Model\Order::class); - $orderMock->expects($this->once())->method('getCustomerId')->will($this->returnValue(null)); - $orderMock->expects($this->any())->method('getBillingAddress')->will($this->returnValue('billing_address')); - - $orderBillingAddress = $this->createPartialMockForAbstractClass(OrderAddressInterface::class, ['getData']); - $orderBillingAddress->expects($this->once()) - ->method('getAddressType') - ->willReturn(Address::ADDRESS_TYPE_BILLING); - - $orderShippingAddress = $this->createPartialMockForAbstractClass(OrderAddressInterface::class, ['getData']); - $orderShippingAddress->expects($this->once()) - ->method('getAddressType') - ->willReturn(Address::ADDRESS_TYPE_SHIPPING); - - $orderMock->expects($this->any()) - ->method('getAddresses') - ->will($this->returnValue([$orderBillingAddress, $orderShippingAddress])); - - $billingQuoteAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $billingQuoteAddress->expects($this->once())->method('load')->willReturn($billingQuoteAddress); - $billingQuoteAddress->expects($this->once())->method('getId')->willReturn(4); - $billingQuoteAddress->expects($this->once())->method('getData')->with('save_in_address_book')->willReturn(1); - - $shippingQuoteAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $shippingQuoteAddress->expects($this->once())->method('load')->willReturn($shippingQuoteAddress); - $shippingQuoteAddress->expects($this->once())->method('getId')->willReturn(5); - $shippingQuoteAddress->expects($this->once())->method('getData')->with('save_in_address_book')->willReturn(1); - $this->quoteAddressFactory->expects($this->exactly(2))->method('create') - ->willReturnOnConsecutiveCalls($billingQuoteAddress, $shippingQuoteAddress); - $this->orderRepository->expects($this->once())->method('get')->with(1)->will($this->returnValue($orderMock)); - $this->objectCopyService->expects($this->any())->method('copyFieldsetToTarget')->will($this->returnValueMap( - [ - ['order_address', 'to_customer', 'billing_address', [], 'global', ['customer_data' => []]], - ['order_address', 'to_customer_address', $orderBillingAddress, [], 'global', 'address_data'], - ['order_address', 'to_customer_address', $orderShippingAddress, [], 'global', 'address_data'], - ] - )); - - $addressMock = $this->createMock(\Magento\Customer\Api\Data\AddressInterface::class); - $addressMock->expects($this->any()) - ->method('setIsDefaultBilling') - ->with(true) - ->willReturnSelf(); - $addressMock->expects($this->any()) - ->method('setIsDefaultShipping') - ->with(true) - ->willReturnSelf(); - - $this->addressFactory->expects($this->any())->method('create')->with(['data' => 'address_data'])->will( - $this->returnValue($addressMock) - ); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $customerMock->expects($this->any())->method('getId')->will($this->returnValue('customer_id')); - $this->customerFactory->expects($this->once())->method('create')->with( - ['data' => ['customer_data' => [], 'addresses' => [$addressMock, $addressMock]]] - )->will($this->returnValue($customerMock)); - $this->accountManagement->expects($this->once())->method('createAccount')->with($customerMock)->will( - $this->returnValue($customerMock) - ); - $orderMock->expects($this->once())->method('setCustomerId')->with('customer_id'); - $this->orderRepository->expects($this->once())->method('save')->with($orderMock); - $this->assertEquals($customerMock, $this->service->create(1)); - } - - /** - * Get mock for abstract class with methods. - * - * @param string $className - * @param array $methods - * - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function createPartialMockForAbstractClass($className, $methods = []) - { - return $this->getMockForAbstractClass( - $className, - [], - '', - true, - true, - true, - $methods - ); - } -} diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index cbc06856f5283..ce2948983edbe 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -991,4 +991,7 @@ </argument> </arguments> </type> + <preference + for="Magento\Sales\Api\OrderCustomerDelegateInterface" + type="Magento\Sales\Model\Order\OrderCustomerDelegate" /> </config> diff --git a/app/code/Magento/Sales/etc/events.xml b/app/code/Magento/Sales/etc/events.xml index 9ec983acab5bd..b3a7a4ab99577 100644 --- a/app/code/Magento/Sales/etc/events.xml +++ b/app/code/Magento/Sales/etc/events.xml @@ -51,4 +51,9 @@ <event name="store_add"> <observer name="magento_sequence" instance="Magento\SalesSequence\Observer\SequenceCreatorObserver" /> </event> + <event name="customer_save_after_data_object"> + <observer + name="sales_assign_order_to_customer" + instance="Magento\Sales\Observer\AssignOrderToCustomerObserver" /> + </event> </config> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Registration.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Registration.php index 0952ebf46d711..c1bab0ae68897 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Registration.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Registration.php @@ -19,7 +19,7 @@ class Registration extends Block * * @var string */ - protected $createAccountButton = '[data-bind*="createAccount"] input'; + protected $createAccountButton = 'input[data-bind*="Create an Account"]'; /** * Click 'Create an Account' button and wait until button will be not visible. diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml index 4052e1f732975..5e08ad3097ed3 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.xml @@ -198,6 +198,7 @@ <data name="tag" xsi:type="string">severity:S0</data> <data name="products/0" xsi:type="string">catalogProductSimple::default</data> <data name="customer/dataset" xsi:type="string">default</data> + <data name="customerPassword" xsi:type="string">12312Qa.</data> <data name="checkoutMethod" xsi:type="string">register</data> <data name="shippingAddress/dataset" xsi:type="string">UK_address_2</data> <data name="billingAddress/dataset" xsi:type="string">UK_address_without_email</data> @@ -207,7 +208,7 @@ <item name="grandTotal" xsi:type="string">565.00</item> </data> <data name="payment/method" xsi:type="string">checkmo</data> - <constraint name="Magento\Checkout\Test\Constraint\AssertOrderSuccessPlacedMessage" /> + <constraint name="Magento\Customer\Test\Constraint\AssertCustomerRedirectToDashboard" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> </variation> <variation name="OnePageCheckoutTestVariation9" summary="One Page Checkout Products with different shipping/billing address and Tier Prices" ticketId="MAGETWO-42604"> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/CreateCustomerAccountStep.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/CreateCustomerAccountStep.php index 816659284a93d..13e4cfb195f9d 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/CreateCustomerAccountStep.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/CreateCustomerAccountStep.php @@ -7,7 +7,9 @@ namespace Magento\Checkout\Test\TestStep; use Magento\Checkout\Test\Page\CheckoutOnepageSuccess; +use Magento\Customer\Test\Page\CustomerAccountCreate; use Magento\Mtf\TestStep\TestStepInterface; +use Magento\Mtf\Fixture\FixtureFactory; /** * Create customer account on checkout one page success after place order. @@ -15,7 +17,7 @@ class CreateCustomerAccountStep implements TestStepInterface { /** - * Checkout one page success. + * "Success One Page Checkout" Storefront page. * * @var CheckoutOnepageSuccess */ @@ -28,15 +30,47 @@ class CreateCustomerAccountStep implements TestStepInterface */ private $checkoutMethod; + /** + * Fixture factory. + * + * @var FixtureFactory + */ + private $fixtureFactory; + + /** + * "Create New Customer Account" Storefront page. + * + * @var CustomerAccountCreate + */ + private $customerAccountCreate; + + /** + * Customer specifies this password while registration. + * + * @var string + */ + private $customerPassword; + /** * @constructor * @param CheckoutOnepageSuccess $checkoutOnepageSuccess * @param string $checkoutMethod + * @param FixtureFactory $fixtureFactory + * @param CustomerAccountCreate $customerAccountCreate + * @param null|string $customerPassword */ - public function __construct(CheckoutOnepageSuccess $checkoutOnepageSuccess, $checkoutMethod) - { + public function __construct( + CheckoutOnepageSuccess $checkoutOnepageSuccess, + $checkoutMethod, + FixtureFactory $fixtureFactory, + CustomerAccountCreate $customerAccountCreate, + $customerPassword = null + ) { $this->checkoutOnepageSuccess = $checkoutOnepageSuccess; $this->checkoutMethod = $checkoutMethod; + $this->fixtureFactory = $fixtureFactory; + $this->customerAccountCreate = $customerAccountCreate; + $this->customerPassword = $customerPassword; } /** @@ -48,6 +82,17 @@ public function run() { if ($this->checkoutMethod === 'register') { $this->checkoutOnepageSuccess->getRegistrationBlock()->createAccount(); + + $customerFixture = $this->fixtureFactory->createByCode( + 'customer', + [ + 'data' => [ + 'password' => $this->customerPassword, + 'password_confirmation' => $this->customerPassword, + ], + ] + ); + $this->customerAccountCreate->getRegisterForm()->registerCustomer($customerFixture); } } } diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml index 586ad2acee203..eba2f95a1f720 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml @@ -116,6 +116,8 @@ <constraint name="Magento\SalesRule\Test\Constraint\AssertCartPriceRuleConditionIsApplied" /> </variation> <variation name="CreateSalesRuleEntityTestVariation5"> + <data name="issue" xsi:type="string">MAGETWO-89831: Unstable FAT: Magento\SalesRule\Test\TestCase\CreateSalesRuleEntityTest</data> + <data name="tag" xsi:type="string">stable:no</data> <data name="address/data/country_id" xsi:type="string">United States</data> <data name="address/data/region_id" xsi:type="string">California</data> <data name="address/data/postcode" xsi:type="string">95814</data> diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/TrackingShipmentForPlacedOrderTest.xml b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/TrackingShipmentForPlacedOrderTest.xml index 6df9a51e51125..e366a9cfb546a 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/TrackingShipmentForPlacedOrderTest.xml +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/TrackingShipmentForPlacedOrderTest.xml @@ -8,6 +8,8 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Shipping\Test\TestCase\TrackingShipmentForPlacedOrderTest" summary="Create shipment for order."> <variation name="TrackingShipmentForPlacedOrderTestVariation1" summary="Creating shipment for order placed within Flat Rate." ticketId="MAGETWO-65163"> + <data name="issue" xsi:type="string">MAGETWO-89858: Unstable FAT: Magento\Shipping\Test\TestCase\TrackingShipmentForPlacedOrderTest</data> + <data name="tag" xsi:type="string">stable:no</data> <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> diff --git a/dev/tests/integration/testsuite/Magento/Sales/Api/OrderCustomerDelegateInterfaceTest.php b/dev/tests/integration/testsuite/Magento/Sales/Api/OrderCustomerDelegateInterfaceTest.php new file mode 100644 index 0000000000000..1b6ef8fc26810 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Api/OrderCustomerDelegateInterfaceTest.php @@ -0,0 +1,179 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Api; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Sales\Api\Data\OrderAddressInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\OrderFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * @magentoAppIsolation enabled + */ +class OrderCustomerDelegateInterfaceTest extends TestCase +{ + /** + * @var OrderCustomerDelegateInterface + */ + private $delegate; + + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var CustomerInterfaceFactory + */ + private $customerFactory; + + /** + * @var AccountManagementInterface + */ + private $accountManagement; + + /** + * @var OrderFactory + */ + private $orderFactory; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->delegate = Bootstrap::getObjectManager()->get( + OrderCustomerDelegateInterface::class + ); + $this->orderRepository = Bootstrap::getObjectManager()->get( + OrderRepositoryInterface::class + ); + $this->customerFactory = Bootstrap::getObjectManager()->get( + CustomerInterfaceFactory::class + ); + $this->accountManagement = Bootstrap::getObjectManager()->get( + AccountManagementInterface::class + ); + $this->orderFactory = Bootstrap::getObjectManager()->get( + OrderFactory::class + ); + } + + /** + * @param OrderAddressInterface $orderAddress + * @param AddressInterface $address + * + * @return void + */ + private function compareAddresses( + OrderAddressInterface $orderAddress, + AddressInterface $address + ): void { + $this->assertEquals( + $orderAddress->getFirstname(), + $address->getFirstname() + ); + $this->assertEquals( + $orderAddress->getLastname(), + $address->getLastname() + ); + $this->assertEquals( + $orderAddress->getCompany(), + $address->getCompany() + ); + $this->assertEquals( + $orderAddress->getStreet(), + $address->getStreet() + ); + $this->assertEquals( + $orderAddress->getCity(), + $address->getCity() + ); + if (!$address->getRegionId()) { + $this->assertEmpty($address->getRegionId()); + } else { + $this->assertEquals( + $orderAddress->getRegionId(), + $address->getRegionId() + ); + } + $this->assertEquals( + $orderAddress->getPostcode(), + $address->getPostcode() + ); + $this->assertEquals( + $orderAddress->getCountryId(), + $address->getCountryId() + ); + $this->assertEquals( + $orderAddress->getTelephone(), + $address->getTelephone() + ); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Sales/_files/order.php + * @return void + */ + public function testDelegateNew(): void + { + $orderAutoincrementId = '100000001'; + /** @var Order $orderModel */ + $orderModel = $this->orderFactory->create(); + $orderModel->loadByIncrementId($orderAutoincrementId); + $orderId = (int)$orderModel->getId(); + unset($orderModel); + + $this->delegate->delegateNew($orderId); + + //Saving new customer with prepared data from order. + /** @var CustomerInterface $customer */ + $customer = $this->customerFactory->create(); + $customer->setWebsiteId(1) + ->setEmail('customer_order_delegate@example.com') + ->setGroupId(1) + ->setStoreId(1) + ->setPrefix('Mr.') + ->setFirstname('John') + ->setMiddlename('A') + ->setLastname('Smith') + ->setSuffix('Esq.') + ->setTaxvat('12') + ->setGender(0); + $createdCustomer = $this->accountManagement->createAccount( + $customer, + '12345abcD' + ); + + //Testing that addresses from order and the order itself are assigned + //to customer. + $order = $this->orderRepository->get($orderId); + $this->assertCount(2, $createdCustomer->getAddresses()); + $this->assertNotNull($createdCustomer->getDefaultBilling()); + $this->assertNotNull($createdCustomer->getDefaultShipping()); + foreach ($createdCustomer->getAddresses() as $address) { + $this->assertTrue( + $address->isDefaultBilling() || $address->isDefaultShipping() + ); + if ($address->isDefaultBilling()) { + $this->compareAddresses($order->getBillingAddress(), $address); + } elseif ($address->isDefaultShipping()) { + $this->compareAddresses($order->getShippingAddress(), $address); + } + } + + $this->assertEquals($order->getCustomerId(), $createdCustomer->getId()); + } +} From 2d37d37ff7e21ec88a7c97313254197ad71fb499 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 18 May 2018 12:07:57 +0300 Subject: [PATCH 080/333] MAGETWO-77759: [2.3] - Related Products Rule for Up-sell Products with Customer Segments Specified Doesn't Work --- .../Rule/Model/Condition/Sql/Builder.php | 100 +++++++++++++++--- .../Unit/Model/Condition/Sql/BuilderTest.php | 14 ++- .../Rule/Model/Condition/Sql/BuilderTest.php | 46 ++++---- 3 files changed, 121 insertions(+), 39 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index 41a55f4c25166..e1c9bf99f2675 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -6,9 +6,14 @@ namespace Magento\Rule\Model\Condition\Sql; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Select; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Rule\Model\Condition\AbstractCondition; use Magento\Rule\Model\Condition\Combine; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Eav\Model\Entity\Collection\AbstractCollection; /** * Class SQL Builder @@ -41,12 +46,22 @@ class Builder */ protected $_expressionFactory; + /** + * @var AttributeRepositoryInterface + */ + private $attributeRepository; + /** * @param ExpressionFactory $expressionFactory + * @param AttributeRepositoryInterface|null $attributeRepository */ - public function __construct(ExpressionFactory $expressionFactory) - { + public function __construct( + ExpressionFactory $expressionFactory, + AttributeRepositoryInterface $attributeRepository = null + ) { $this->_expressionFactory = $expressionFactory; + $this->attributeRepository = $attributeRepository ?: + ObjectManager::getInstance()->get(AttributeRepositoryInterface::class); } /** @@ -88,14 +103,14 @@ protected function _getChildCombineTablesToJoin(Combine $combine, $tables = []) /** * Join tables from conditions combination to collection * - * @param \Magento\Eav\Model\Entity\Collection\AbstractCollection $collection + * @param AbstractCollection $collection * @param Combine $combine * @return $this */ protected function _joinTablesToCollection( - \Magento\Eav\Model\Entity\Collection\AbstractCollection $collection, + AbstractCollection $collection, Combine $combine - ) { + ): Builder { foreach ($this->_getCombineTablesToJoin($combine) as $alias => $joinTable) { /** @var $condition AbstractCondition */ $collection->getSelect()->joinLeft( @@ -104,6 +119,7 @@ protected function _joinTablesToCollection( isset($joinTable['columns']) ? $joinTable['columns'] : '*' ); } + return $this; } @@ -112,11 +128,15 @@ protected function _joinTablesToCollection( * * @param AbstractCondition $condition * @param string $value + * @param bool $isDefaultStoreUsed * @return string * @throws \Magento\Framework\Exception\LocalizedException */ - protected function _getMappedSqlCondition(AbstractCondition $condition, $value = '') - { + protected function _getMappedSqlCondition( + AbstractCondition $condition, + string $value = '', + bool $isDefaultStoreUsed = true + ): string { $argument = $condition->getMappedSqlField(); // If rule hasn't valid argument - create negative expression to prevent incorrect rule behavior. @@ -130,9 +150,16 @@ protected function _getMappedSqlCondition(AbstractCondition $condition, $value = throw new \Magento\Framework\Exception\LocalizedException(__('Unknown condition operator')); } + $defaultValue = 0; + // Check if attribute has a table with default value and add it to the query + if ($this->canAttributeHaveDefaultValue($condition->getAttribute(), $isDefaultStoreUsed)) { + $defaultField = 'at_' . $condition->getAttribute() . '_default.value'; + $defaultValue = $this->_connection->quoteIdentifier($defaultField); + } + $sql = str_replace( ':field', - $this->_connection->getIfNullSql($this->_connection->quoteIdentifier($argument), 0), + $this->_connection->getIfNullSql($this->_connection->quoteIdentifier($argument), $defaultValue), $this->_conditionOperatorMap[$conditionOperator] ); @@ -144,11 +171,15 @@ protected function _getMappedSqlCondition(AbstractCondition $condition, $value = /** * @param Combine $combine * @param string $value + * @param bool $isDefaultStoreUsed * @return string * @SuppressWarnings(PHPMD.NPathComplexity) */ - protected function _getMappedSqlCombination(Combine $combine, $value = '') - { + protected function _getMappedSqlCombination( + Combine $combine, + string $value = '', + bool $isDefaultStoreUsed = true + ): string { $out = (!empty($value) ? $value : ''); $value = ($combine->getValue() ? '' : ' NOT '); $getAggregator = $combine->getAggregator(); @@ -158,33 +189,68 @@ protected function _getMappedSqlCombination(Combine $combine, $value = '') $con = ($getAggregator == 'any' ? Select::SQL_OR : Select::SQL_AND); $con = (isset($conditions[$key+1]) ? $con : ''); if ($condition instanceof Combine) { - $out .= $this->_getMappedSqlCombination($condition, $value); + $out .= $this->_getMappedSqlCombination($condition, $value, $isDefaultStoreUsed); } else { - $out .= $this->_getMappedSqlCondition($condition, $value); + $out .= $this->_getMappedSqlCondition($condition, $value, $isDefaultStoreUsed); } $out .= $out ? (' ' . $con) : ''; } + return $this->_expressionFactory->create(['expression' => $out]); } /** * Attach conditions filter to collection * - * @param \Magento\Eav\Model\Entity\Collection\AbstractCollection $collection + * @param AbstractCollection $collection * @param Combine $combine - * * @return void */ public function attachConditionToCollection( - \Magento\Eav\Model\Entity\Collection\AbstractCollection $collection, + AbstractCollection $collection, Combine $combine - ) { + ): void { $this->_connection = $collection->getResource()->getConnection(); $this->_joinTablesToCollection($collection, $combine); - $whereExpression = (string)$this->_getMappedSqlCombination($combine); + $isDefaultStoreUsed = $this->checkIsDefaultStoreUsed($collection); + $whereExpression = (string)$this->_getMappedSqlCombination($combine, '', $isDefaultStoreUsed); if (!empty($whereExpression)) { // Select ::where method adds braces even on empty expression $collection->getSelect()->where($whereExpression); } } + + /** + * Check is default store used. + * + * @param AbstractCollection $collection + * @return bool + */ + private function checkIsDefaultStoreUsed(AbstractCollection $collection): bool + { + return (int)$collection->getStoreId() === (int)$collection->getDefaultStoreId(); + } + + /** + * Check if attribute can have default value. + * + * @param string $attributeCode + * @param bool $isDefaultStoreUsed + * @return bool + */ + private function canAttributeHaveDefaultValue(string $attributeCode, bool $isDefaultStoreUsed): bool + { + if ($isDefaultStoreUsed) { + return false; + } + + try { + $attribute = $this->attributeRepository->get(Product::ENTITY, $attributeCode); + } catch (NoSuchEntityException $e) { + // It's not exceptional case as we want to check if we have such attribute or not + return false; + } + + return !$attribute->isScopeGlobal(); + } } diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php index f53098c4bb97e..daf7b1462c722 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php @@ -35,7 +35,12 @@ public function testAttachConditionToCollection() { $collection = $this->createPartialMock( \Magento\Eav\Model\Entity\Collection\AbstractCollection::class, - ['getResource', 'getSelect'] + [ + 'getResource', + 'getSelect', + 'getStoreId', + 'getDefaultStoreId', + ] ); $combine = $this->createPartialMock(\Magento\Rule\Model\Condition\Combine::class, ['getConditions']); $resource = $this->createPartialMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class, ['getConnection']); @@ -53,10 +58,15 @@ public function testAttachConditionToCollection() $collection->expects($this->once()) ->method('getResource') ->will($this->returnValue($resource)); - $collection->expects($this->any()) ->method('getSelect') ->will($this->returnValue($select)); + $collection->expects($this->once()) + ->method('getStoreId') + ->willReturn(1); + $collection->expects($this->once()) + ->method('getDefaultStoreId') + ->willReturn(1); $resource->expects($this->once()) ->method('getConnection') diff --git a/dev/tests/integration/testsuite/Magento/Rule/Model/Condition/Sql/BuilderTest.php b/dev/tests/integration/testsuite/Magento/Rule/Model/Condition/Sql/BuilderTest.php index c9640ceba87d2..4473d31047d4b 100644 --- a/dev/tests/integration/testsuite/Magento/Rule/Model/Condition/Sql/BuilderTest.php +++ b/dev/tests/integration/testsuite/Magento/Rule/Model/Condition/Sql/BuilderTest.php @@ -7,60 +7,66 @@ namespace Magento\Rule\Model\Condition\Sql; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; +use Magento\CatalogWidget\Model\RuleFactory; +use Magento\CatalogWidget\Model\Rule\Condition\Combine as CombineCondition; +use Magento\CatalogWidget\Model\Rule\Condition\Product as ProductCondition; +/** + * Test for Magento\Rule\Model\Condition\Sql\Builder + */ class BuilderTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Rule\Model\Condition\Sql\Builder + * @var Builder */ private $model; protected function setUp() { - $this->model = Bootstrap::getObjectManager()->create(\Magento\Rule\Model\Condition\Sql\Builder::class); + $this->model = Bootstrap::getObjectManager()->create(Builder::class); } - public function testAttachConditionToCollection() + /** + * @return void + */ + public function testAttachConditionToCollection(): void { - /** @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory */ - $collectionFactory = Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class - ); - /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ + /** @var ProductCollectionFactory $collectionFactory */ + $collectionFactory = Bootstrap::getObjectManager()->create(ProductCollectionFactory::class); $collection = $collectionFactory->create(); - /** @var \Magento\CatalogWidget\Model\RuleFactory $ruleFactory */ - $ruleFactory = Bootstrap::getObjectManager()->create(\Magento\CatalogWidget\Model\RuleFactory::class); - /** @var \Magento\CatalogWidget\Model\Rule $rule */ + /** @var RuleFactory $ruleFactory */ + $ruleFactory = Bootstrap::getObjectManager()->create(RuleFactory::class); $rule = $ruleFactory->create(); $ruleConditionArray = [ 'conditions' => [ '1' => [ - 'type' => \Magento\CatalogWidget\Model\Rule\Condition\Combine::class, + 'type' => CombineCondition::class, 'aggregator' => 'all', 'value' => '1', - 'new_child' => '' + 'new_child' => '', ], '1--1' => [ - 'type' => \Magento\CatalogWidget\Model\Rule\Condition\Product::class, + 'type' => ProductCondition::class, 'attribute' => 'category_ids', 'operator' => '==', - 'value' => '3' + 'value' => '3', ], '1--2' => [ - 'type' => \Magento\CatalogWidget\Model\Rule\Condition\Product::class, + 'type' => ProductCondition::class, 'attribute' => 'special_to_date', 'operator' => '==', - 'value' => '2017-09-15' + 'value' => '2017-09-15', ], - ] + ], ]; $rule->loadPost($ruleConditionArray); $this->model->attachConditionToCollection($collection, $rule->getConditions()); - $whereString = 'WHERE (category_id IN (\'3\')))) AND(IFNULL(`e`.`entity_id`, 0) = \'2017-09-15\') ))'; - $this->assertNotFalse(strpos($collection->getSelectSql(true), $whereString)); + $whereString = "/\(category_id IN \('3'\).+\(IFNULL\(`e`\.`entity_id`,.+\) = '2017-09-15'\)/"; + $this->assertNotFalse(preg_match($whereString, $collection->getSelectSql(true))); } } From dec4c87b37c0d76e0c9dd98fa45d8e74c2c2f45b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 18 May 2018 12:31:26 +0300 Subject: [PATCH 081/333] MAGETWO-61364: [GitHub] Dependency check in the static tests fails for non core modules #7421 --- .../Magento/Checkout/etc/adminhtml/routes.xml | 14 ++ .../etc/adminhtml/routes.xml | 2 +- .../Magento/Test/Integrity/DependencyTest.php | 167 +++++++++++------- 3 files changed, 115 insertions(+), 68 deletions(-) create mode 100644 app/code/Magento/Checkout/etc/adminhtml/routes.xml diff --git a/app/code/Magento/Checkout/etc/adminhtml/routes.xml b/app/code/Magento/Checkout/etc/adminhtml/routes.xml new file mode 100644 index 0000000000000..e537861059870 --- /dev/null +++ b/app/code/Magento/Checkout/etc/adminhtml/routes.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:App/etc/routes.xsd"> + <router id="admin"> + <route id="checkout" frontName="checkout"> + <module name="Magento_Checkout" before="Magento_Backend" /> + </route> + </router> +</config> diff --git a/app/code/Magento/CheckoutAgreements/etc/adminhtml/routes.xml b/app/code/Magento/CheckoutAgreements/etc/adminhtml/routes.xml index 1249ea44b991e..5a708f49a7034 100644 --- a/app/code/Magento/CheckoutAgreements/etc/adminhtml/routes.xml +++ b/app/code/Magento/CheckoutAgreements/etc/adminhtml/routes.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="admin"> - <route id="checkout" frontName="checkout"> + <route id="checkout"> <module name="Magento_CheckoutAgreements" before="Magento_Backend" /> </route> </router> diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php index 3e7f7804222e2..d30e5b4b44ae0 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php @@ -124,7 +124,7 @@ class DependencyTest extends \PHPUnit\Framework\TestCase * ))) * @var array */ - protected static $_mapDependencies = []; + protected static $mapDependencies = []; /** * Regex pattern for validation file path of theme @@ -410,7 +410,7 @@ private function collectDependency($dependency, $currentModule, &$undeclared) $this->_setDependencies($currentModule, $type, self::MAP_TYPE_REDUNDANT, $module); } - $this->_addDependencies($currentModule, $type, self::MAP_TYPE_FOUND, $nsModule); + $this->addDependency($currentModule, $type, self::MAP_TYPE_FOUND, $nsModule); } /** @@ -421,7 +421,7 @@ private function collectDependency($dependency, $currentModule, &$undeclared) */ public function collectRedundant() { - foreach (array_keys(self::$_mapDependencies) as $module) { + foreach (array_keys(self::$mapDependencies) as $module) { $declared = $this->_getDependencies($module, self::TYPE_HARD, self::MAP_TYPE_DECLARED); $found = array_merge( $this->_getDependencies($module, self::TYPE_HARD, self::MAP_TYPE_FOUND), @@ -440,7 +440,7 @@ public function collectRedundant() public function testRedundant() { $output = []; - foreach (array_keys(self::$_mapDependencies) as $module) { + foreach (array_keys(self::$mapDependencies) as $module) { $result = []; $redundant = $this->_getDependencies($module, self::TYPE_HARD, self::MAP_TYPE_REDUNDANT); if (count($redundant)) { @@ -687,37 +687,40 @@ protected static function _getTypes() * Converts a composer json component name into the Magento Module form * * @param string $jsonName The name of a composer json component or dependency e.g. 'magento/module-theme' + * @param array $packageModuleMap Mapping package name with module namespace. * @return string The corresponding Magento Module e.g. 'Magento\Theme' */ - protected static function convertModuleName($jsonName) + protected static function convertModuleName(string $jsonName, array $packageModuleMap): string { - if (strpos($jsonName, 'magento/module') !== false) { - $moduleName = str_replace('-', ' ', $jsonName); - $moduleName = ucwords($moduleName); - $moduleName = str_replace('module ', '', $moduleName); - $moduleName = str_replace(' ', '', $moduleName); - $moduleName = str_replace('/', '\\', $moduleName); - return $moduleName; - } elseif (strpos($jsonName, 'magento/magento') !== false || strpos($jsonName, 'magento/framework') !== false) { + if (isset($packageModuleMap[$jsonName])) { + return $packageModuleMap[$jsonName]; + } + + if (strpos($jsonName, 'magento/magento') !== false || strpos($jsonName, 'magento/framework') !== false) { $moduleName = str_replace('/', "\t", $jsonName); $moduleName = str_replace('framework-', "Framework\t", $moduleName); $moduleName = str_replace('-', ' ', $moduleName); $moduleName = ucwords($moduleName); $moduleName = str_replace("\t", '\\', $moduleName); $moduleName = str_replace(' ', '', $moduleName); + return $moduleName; } + return $jsonName; } /** - * Initialise map of dependencies + * Initialise map of dependencies. * + * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws \Exception */ protected static function _initDependencies() { + $packageModuleMap = self::getPackageModuleMapping(); $jsonFiles = Files::init()->getComposerFiles(ComponentRegistrar::MODULE, false); foreach ($jsonFiles as $file) { $contents = file_get_contents($file); @@ -726,12 +729,13 @@ protected static function _initDependencies() throw new \Exception("Invalid Json: $file"); } $json = new \Magento\Framework\Config\Composer\Package(json_decode($contents)); - $moduleName = self::convertModuleName($json->get('name')); - self::$_mapDependencies[$moduleName] = @(self::$_mapDependencies[$moduleName] ?: []); - + $moduleName = self::convertModuleName($json->get('name'), $packageModuleMap); + if (!isset(self::$mapDependencies[$moduleName])) { + self::$mapDependencies[$moduleName] = []; + } foreach (self::_getTypes() as $type) { - if (!isset(self::$_mapDependencies[$moduleName][$type])) { - self::$_mapDependencies[$moduleName][$type] = [ + if (!isset(self::$mapDependencies[$moduleName][$type])) { + self::$mapDependencies[$moduleName][$type] = [ self::MAP_TYPE_DECLARED => [], self::MAP_TYPE_FOUND => [], self::MAP_TYPE_REDUNDANT => [], @@ -739,61 +743,89 @@ protected static function _initDependencies() } } - $require = $json->get('require'); - if (isset($require) && !empty($require)) { - foreach ($require as $requiredModule => $version) { - if (0 === strpos($requiredModule, 'magento/') - && 'magento/magento-composer-installer' != $requiredModule - ) { - $type = self::TYPE_HARD; - self::_addDependencies( - $moduleName, - $type, - self::MAP_TYPE_DECLARED, - self::convertModuleName($requiredModule) - ); - } - } - } - $suggest = $json->get('suggest'); - if (isset($suggest) && !empty($suggest)) { - foreach ($suggest as $requiredModule => $version) { - if (0 === strpos($requiredModule, 'magento/') - && 'magento/magento-composer-installer' != $requiredModule - ) { - $type = self::TYPE_SOFT; - self::_addDependencies( - $moduleName, - $type, - self::MAP_TYPE_DECLARED, - self::convertModuleName($requiredModule) - ); - } - } - } + $require = array_keys((array)$json->get('require')); + self::addDependencies($moduleName, $require, self::TYPE_HARD, $packageModuleMap); + + $suggest = array_keys((array)$json->get('suggest')); + self::addDependencies($moduleName, $suggest, self::TYPE_SOFT, $packageModuleMap); } } /** - * Add dependency map items + * Add dependencies to dependency list. * - * @param $module - * @param $type - * @param $mapType - * @param $dependencies + * @param string $moduleName + * @param array $packageNames + * @param string $type + * @param array $packageModuleMap + * + * @return void */ - protected static function _addDependencies($module, $type, $mapType, $dependencies) + private static function addDependencies( + string $moduleName, + array $packageNames, + string $type, + array $packageModuleMap + ): void { + $packageNames = array_filter($packageNames, function ($packageName) use ($packageModuleMap) { + return isset($packageModuleMap[$packageName]) || + 0 === strpos($packageName, 'magento/') && 'magento/magento-composer-installer' != $packageName; + }); + + foreach ($packageNames as $packageName) { + self::addDependency( + $moduleName, + $type, + self::MAP_TYPE_DECLARED, + self::convertModuleName($packageName, $packageModuleMap) + ); + } + } + + /** + * Add dependency map items. + * + * @param string $module + * @param string $type + * @param string $mapType + * @param string $dependency + * + * @return void + */ + private static function addDependency(string $module, string $type, string $mapType, string $dependency): void { - if (!is_array($dependencies)) { - $dependencies = [$dependencies]; + if (isset(self::$mapDependencies[$module][$type][$mapType])) { + self::$mapDependencies[$module][$type][$mapType][$dependency] = $dependency; } - foreach ($dependencies as $dependency) { - if (isset(self::$_mapDependencies[$module][$type][$mapType])) { - self::$_mapDependencies[$module][$type][$mapType][$dependency] = $dependency; + } + + /** + * Returns package name on module name mapping. + * + * @return array + * @throws \Exception + */ + private static function getPackageModuleMapping(): array + { + $jsonFiles = Files::init()->getComposerFiles(ComponentRegistrar::MODULE, false); + + $packageModuleMapping = []; + foreach ($jsonFiles as $file) { + $contents = file_get_contents($file); + $composerJson = json_decode($contents); + if (null == $composerJson) { + throw new \Exception("Invalid Json: $file"); } + $moduleXml = simplexml_load_file(dirname($file) . '/etc/module.xml'); + $moduleName = str_replace('_', '\\', (string)$moduleXml->module->attributes()->name); + $packageName = $composerJson->name; + $packageModuleMapping[$packageName] = $moduleName; } + + return $packageModuleMapping; } + /** * Retrieve array of dependency items * @@ -804,9 +836,10 @@ protected static function _addDependencies($module, $type, $mapType, $dependenci */ protected function _getDependencies($module, $type, $mapType) { - if (isset(self::$_mapDependencies[$module][$type][$mapType])) { - return self::$_mapDependencies[$module][$type][$mapType]; + if (isset(self::$mapDependencies[$module][$type][$mapType])) { + return self::$mapDependencies[$module][$type][$mapType]; } + return []; } @@ -823,8 +856,8 @@ protected function _setDependencies($module, $type, $mapType, $dependencies) if (!is_array($dependencies)) { $dependencies = [$dependencies]; } - if (isset(self::$_mapDependencies[$module][$type][$mapType])) { - self::$_mapDependencies[$module][$type][$mapType] = $dependencies; + if (isset(self::$mapDependencies[$module][$type][$mapType])) { + self::$mapDependencies[$module][$type][$mapType] = $dependencies; } } @@ -836,6 +869,6 @@ protected function _setDependencies($module, $type, $mapType, $dependencies) */ protected function _isFake($module) { - return isset(self::$_mapDependencies[$module]) ? false : true; + return isset(self::$mapDependencies[$module]) ? false : true; } } From fd561acc5cc4e1fde44cb0f7b399fbf2fb25e2c7 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 18 May 2018 12:39:13 +0300 Subject: [PATCH 082/333] MAGETWO-89412: [2.3] Composer static test of PHP versions is failed for non-core modules --- .../static/testsuite/Magento/Test/Integrity/ComposerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index eea91092316f8..2d438cd372b24 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -216,7 +216,7 @@ private function assertMagentoConventions($dir, $packageType, \StdClass $json) * @param string $packageName * @return bool */ - private function isVendorMagento(string $packageName) + private function isVendorMagento(string $packageName): bool { return strpos($packageName, 'magento/') === 0; } From 7cda89518a77c6b7aaaa63c50dbff3b66fcde247 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 18 May 2018 13:49:20 +0300 Subject: [PATCH 083/333] MAGETWO-61364: [GitHub] Dependency check in the static tests fails for non core modules #7421 --- .../static/testsuite/Magento/Test/Integrity/DependencyTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php index d30e5b4b44ae0..a4113abed8030 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php @@ -20,6 +20,7 @@ /** * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DependencyTest extends \PHPUnit\Framework\TestCase { @@ -825,7 +826,6 @@ private static function getPackageModuleMapping(): array return $packageModuleMapping; } - /** * Retrieve array of dependency items * From 2340c2987901c897fbd3c36488fe41dfc4e8e49c Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 18 May 2018 14:37:34 +0300 Subject: [PATCH 084/333] MAGETWO-89408: [2.3] Failed Magento\Test\Integrity\ClassesTest::testClassReferences --- .../Magento/Test/Integrity/ClassesTest.php | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php index 5c1e342e1bc81..6d627574a3a18 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php @@ -109,8 +109,9 @@ function ($file) { * * @param string $contents * @param array &$classes + * @return void */ - private function collectResourceHelpersPhp($contents, &$classes) + private function collectResourceHelpersPhp(string $contents, array &$classes): void { $regex = '/(?:\:\:|\->)getResourceHelper\(\s*\'([a-z\d\\\\]+)\'\s*\)/ix'; $matches = Classes::getAllMatches($contents, $regex); @@ -178,10 +179,12 @@ function ($path) { * Suppressing "unused variable" because of the "catch" block * * @param array $classes + * @param string $path + * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - private function assertClassesExist($classes, $path) + private function assertClassesExist(array $classes, string $path): void { if (!$classes) { return; @@ -264,8 +267,9 @@ function ($file) { * @param string $relativePath * @param string $contents * @param string $className + * @return void */ - private function assertClassNamespace($file, $relativePath, $contents, $className) + private function assertClassNamespace(string $file, string $relativePath, string $contents, string $className): void { $namespacePattern = '/(Magento|Zend)\/[a-zA-Z]+[^\.]+/'; $formalPattern = '/^namespace\s[a-zA-Z]+(\\\\[a-zA-Z0-9]+)*/m'; @@ -397,10 +401,11 @@ function ($file) { /** * Remove alias class name references that have been identified as 'bad'. * - * @param $aliasClasses - * @param $badClasses + * @param array $aliasClasses + * @param array $badClasses + * @return array */ - private function handleAliasClasses($aliasClasses, $badClasses) + private function handleAliasClasses(array $aliasClasses, array $badClasses): array { foreach ($aliasClasses as $aliasClass) { foreach ($badClasses as $badClass) { @@ -409,15 +414,17 @@ private function handleAliasClasses($aliasClasses, $badClasses) } } } + return $badClasses; } /** * This function is to remove legacy code usages according to _files/blacklist/reference.txt - * @param $classes + * + * @param array $classes * @return array */ - private function referenceBlacklistFilter($classes) + private function referenceBlacklistFilter(array $classes): array { // exceptions made for the files from the blacklist $classes = $this->getReferenceBlacklist(); @@ -426,6 +433,7 @@ private function referenceBlacklistFilter($classes) unset($classes[array_search($class, $classes)]); } } + return $classes; } @@ -434,7 +442,7 @@ private function referenceBlacklistFilter($classes) * * @return array */ - private function getReferenceBlacklist() + private function getReferenceBlacklist(): array { if (!isset($this->referenceBlackList)) { $this->referenceBlackList = file( @@ -455,7 +463,7 @@ private function getReferenceBlacklist() * @param string $namespacePath * @return array */ - private function removeSpecialCases($badClasses, $file, $contents, $namespacePath) + private function removeSpecialCases(array $badClasses, string $file, string $contents, string $namespacePath): array { foreach ($badClasses as $badClass) { // Remove valid usages of Magento modules from the list @@ -500,6 +508,7 @@ private function removeSpecialCases($badClasses, $file, $contents, $namespacePat continue; } } + return $badClasses; } @@ -547,7 +556,7 @@ private function removeSpecialCasesNonFullyQualifiedClassNames($namespacePath, & * @param string $badClass * @return null|string */ - private function getLibraryDirByPath($namespacePath, $badClass) + private function getLibraryDirByPath(string $namespacePath, string $badClass) { $libraryDir = null; $fullPath = null; @@ -585,6 +594,7 @@ private function getLibraryDirByPath($namespacePath, $badClass) $fullPath = $libraryDir . '/' . implode('/', $namespaceParts) . '/' . str_replace('\\', '/', $badClass) . '.php'; } + return $fullPath; } @@ -594,7 +604,7 @@ private function getLibraryDirByPath($namespacePath, $badClass) * @param array $badClasses * @return bool */ - private function removeSpecialCasesForAllOthers($namespacePath, $badClass, &$badClasses) + private function removeSpecialCasesForAllOthers(string $namespacePath, string $badClass, array &$badClasses): bool { // Remove usage of classes that do NOT using fully-qualified class names (possibly under same namespace) $directories = [ @@ -616,9 +626,11 @@ private function removeSpecialCasesForAllOthers($namespacePath, $badClass, &$bad $fullPath = $directory . $namespacePath . '/' . str_replace('\\', '/', $badClass) . '.php'; if (file_exists($fullPath)) { unset($badClasses[array_search($badClass, $badClasses)]); + return true; } } + return false; } @@ -627,8 +639,9 @@ private function removeSpecialCasesForAllOthers($namespacePath, $badClass, &$bad * * @param array $badClasses * @param string $file + * @return void */ - private function assertClassReferences($badClasses, $file) + private function assertClassReferences(array $badClasses, string $file): void { if (empty($badClasses)) { return; From 4bbe677a7862a788d8c5be516bd00e33405f5e25 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 18 May 2018 14:52:10 +0300 Subject: [PATCH 085/333] MAGETWO-90818: Files and folders symlinked in pub/media cannot be deleted from media gallery browser --- .../Magento/Framework/App/Filesystem/DirectoryResolver.php | 5 ++--- .../App/Test/Unit/Filesystem/DirectoryResolverTest.php | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php b/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php index 849cfa261ebe1..c5000314f1c70 100644 --- a/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php +++ b/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php @@ -27,10 +27,9 @@ class DirectoryResolver /** * @param DirectoryList $directoryList - * @param Filesystem|null $filesystem - * @throws \RuntimeException + * @param Filesystem $filesystem */ - public function __construct(DirectoryList $directoryList, Filesystem $filesystem = null) + public function __construct(DirectoryList $directoryList, Filesystem $filesystem) { $this->directoryList = $directoryList; $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class); diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Filesystem/DirectoryResolverTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Filesystem/DirectoryResolverTest.php index b74cf51fa9e2f..955765455df16 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Filesystem/DirectoryResolverTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Filesystem/DirectoryResolverTest.php @@ -52,7 +52,7 @@ protected function setUp() * @param bool $expectedResult * @return void */ - public function testValidatePath($path, $expectedResult) + public function testValidatePath(string $path, bool $expectedResult): void { $rootPath = '/path/root'; $directoryConfig = 'directory_config'; From 47bb4aaba8673096a61a48aa9f25c7a5b4b802b3 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 18 May 2018 14:58:34 +0300 Subject: [PATCH 086/333] MAGETWO-90818: Files and folders symlinked in pub/media cannot be deleted from media gallery browser --- .../Magento/Framework/App/Filesystem/DirectoryResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php b/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php index c5000314f1c70..b63deb6b244df 100644 --- a/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php +++ b/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php @@ -32,7 +32,7 @@ class DirectoryResolver public function __construct(DirectoryList $directoryList, Filesystem $filesystem) { $this->directoryList = $directoryList; - $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class); + $this->filesystem = $filesystem; } /** From b090d9cf4b3de4365ba2d75d5ce36f9e5f6c9db4 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 18 May 2018 15:07:57 +0300 Subject: [PATCH 087/333] MAGETWO-90818: Files and folders symlinked in pub/media cannot be deleted from media gallery browser --- .../Magento/Framework/App/Filesystem/DirectoryResolver.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php b/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php index b63deb6b244df..5ad3d888ffb57 100644 --- a/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php +++ b/lib/internal/Magento/Framework/App/Filesystem/DirectoryResolver.php @@ -8,7 +8,6 @@ namespace Magento\Framework\App\Filesystem; use Magento\Framework\Filesystem; -use Magento\Framework\App\ObjectManager; /** * Magento directories resolver. From 04afef04c2ba4dca5b6c3e5acdd7c31d7d496af3 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 18 May 2018 16:18:04 +0300 Subject: [PATCH 088/333] MAGETWO-90804: "Hide from Product Page" option does not work for child of configurable product --- app/code/Magento/Catalog/Model/Product.php | 2 + .../Plugin/ProductIdentitiesExtender.php | 58 +++++ .../Model/Product/Cache/Tag/Configurable.php | 51 ---- .../Product/Type/Configurable.php | 6 +- .../Plugin/ProductIdentitiesExtenderTest.php | 77 ++++++ .../Product/Cache/Tag/ConfigurableTest.php | 66 ----- .../Magento/ConfigurableProduct/etc/di.xml | 10 +- app/code/Magento/Swatches/Helper/Data.php | 75 +++--- .../Swatches/Test/Unit/Helper/DataTest.php | 228 +++++++----------- .../ConfigurableProduct/Model/ProductTest.php | 28 +++ 10 files changed, 297 insertions(+), 304 deletions(-) create mode 100644 app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php delete mode 100644 app/code/Magento/ConfigurableProduct/Model/Product/Cache/Tag/Configurable.php create mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductIdentitiesExtenderTest.php delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ProductTest.php diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index e58c9aab77665..b2b01bffe8691 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -2108,6 +2108,8 @@ public function reset() /** * Get cache tags associated with object id * + * @deprecated + * @see \Magento\Catalog\Model\Product::getIdentities * @return string[] */ public function getCacheIdTags() diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php new file mode 100644 index 0000000000000..68c574194817c --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model\Plugin; + +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; + +/** + * Extender of product identities for child of configurable products + */ +class ProductIdentitiesExtender +{ + /** + * @var Configurable + */ + private $configurableType; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @param Configurable $configurableType + * @param ProductRepositoryInterface $productRepository + */ + public function __construct(Configurable $configurableType, ProductRepositoryInterface $productRepository) + { + $this->configurableType = $configurableType; + $this->productRepository = $productRepository; + } + + /** + * Add parent identities to product identities + * + * @param Product $subject + * @param array $identities + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetIdentities(Product $subject, array $identities): array + { + $identities = (array) $identities; + + foreach ($this->configurableType->getParentIdsByChild($subject->getId()) as $parentId) { + $parentProduct = $this->productRepository->getById($parentId); + $identities = array_merge($identities, (array) $parentProduct->getIdentities()); + } + + return array_unique($identities); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Cache/Tag/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Cache/Tag/Configurable.php deleted file mode 100644 index ac42e320f3ad9..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Cache/Tag/Configurable.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\ConfigurableProduct\Model\Product\Cache\Tag; - -use Magento\Framework\App\Cache\Tag\StrategyInterface; - -/** - * Add parent invalidation tags - */ -class Configurable implements StrategyInterface -{ - /** - * Configurable product type resource - * - * @var \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable - */ - private $catalogProductTypeConfigurable; - - /** - * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable - */ - public function __construct( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable - ) { - $this->catalogProductTypeConfigurable = $catalogProductTypeConfigurable; - } - - /** - * {@inheritdoc} - */ - public function getTags($object) - { - if (!is_object($object)) { - throw new \InvalidArgumentException('Provided argument is not an object'); - } - - if (!($object instanceof \Magento\Catalog\Model\Product)) { - throw new \InvalidArgumentException('Provided argument must be a product'); - } - - $result = $object->getIdentities(); - - foreach ($this->catalogProductTypeConfigurable->getParentIdsByChild($object->getId()) as $parentId) { - $result[] = \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $parentId; - } - return $result; - } -} diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php index 95afba984d57d..ccff85dd9717f 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php @@ -189,7 +189,6 @@ public function getChildrenIds($parentId, $required = true) */ public function getParentIdsByChild($childId) { - $parentIds = []; $select = $this->getConnection() ->select() ->from(['l' => $this->getMainTable()], []) @@ -198,10 +197,7 @@ public function getParentIdsByChild($childId) 'e.' . $this->optionProvider->getProductEntityLinkField() . ' = l.parent_id', ['e.entity_id'] )->where('l.product_id IN(?)', $childId); - - foreach ($this->getConnection()->fetchAll($select) as $row) { - $parentIds[] = $row['entity_id']; - } + $parentIds = $this->getConnection()->fetchCol($select); return $parentIds; } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductIdentitiesExtenderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductIdentitiesExtenderTest.php new file mode 100644 index 0000000000000..d29f163ee1129 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductIdentitiesExtenderTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin; + +use Magento\ConfigurableProduct\Model\Plugin\ProductIdentitiesExtender; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; + +/** + * Class ProductIdentitiesExtenderTest + */ +class ProductIdentitiesExtenderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Configurable + */ + private $configurableTypeMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ProductRepositoryInterface + */ + private $productRepositoryMock; + + /** + * @var ProductIdentitiesExtender + */ + private $plugin; + + protected function setUp() + { + $this->configurableTypeMock = $this->getMockBuilder(Configurable::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productRepositoryMock = $this->getMockBuilder(ProductRepositoryInterface::class) + ->getMock(); + + $this->plugin = new ProductIdentitiesExtender($this->configurableTypeMock, $this->productRepositoryMock); + } + + public function testAfterGetIdentities() + { + $productId = 1; + $productIdentity = 'cache_tag_1'; + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $parentProductId = 2; + $parentProductIdentity = 'cache_tag_2'; + $parentProductMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $productMock->expects($this->once()) + ->method('getId') + ->willReturn($productId); + $this->configurableTypeMock->expects($this->once()) + ->method('getParentIdsByChild') + ->with($productId) + ->willReturn([$parentProductId]); + $this->productRepositoryMock->expects($this->once()) + ->method('getById') + ->with($parentProductId) + ->willReturn($parentProductMock); + $parentProductMock->expects($this->once()) + ->method('getIdentities') + ->willReturn([$parentProductIdentity]); + + $productIdentities = $this->plugin->afterGetIdentities($productMock, [$productIdentity]); + $this->assertEquals([$productIdentity, $parentProductIdentity], $productIdentities); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php deleted file mode 100644 index a3f1435f84d2f..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Cache/Tag/ConfigurableTest.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\ConfigurableProduct\Test\Unit\Model\Product\Cache\Tag; - -use Magento\ConfigurableProduct\Model\Product\Cache\Tag\Configurable; - -class ConfigurableTest extends \PHPUnit\Framework\TestCase -{ - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|Configurable - */ - private $typeResource; - - /** - * @var Configurable - */ - private $model; - - protected function setUp() - { - $this->typeResource = $this->createMock( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable::class - ); - - $this->model = new Configurable($this->typeResource); - } - - public function testGetWithScalar() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Provided argument is not an object'); - $this->model->getTags('scalar'); - } - - public function testGetTagsWithObject() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Provided argument must be a product'); - $this->model->getTags(new \stdClass()); - } - - public function testGetTagsWithVariation() - { - $product = $this->createMock(\Magento\Catalog\Model\Product::class); - - $identities = ['id1', 'id2']; - - $product->expects($this->once()) - ->method('getIdentities') - ->willReturn($identities); - - $parentId = 4; - $this->typeResource->expects($this->once()) - ->method('getParentIdsByChild') - ->willReturn([$parentId]); - - $expected = array_merge($identities, [\Magento\Catalog\Model\Product::CACHE_TAG . '_' . $parentId]); - - $this->assertEquals($expected, $this->model->getTags($product)); - } -} diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index 3f04081eaf645..15dbc53a5447a 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -168,13 +168,6 @@ </argument> </arguments> </type> - <type name="Magento\Framework\App\Cache\Tag\Strategy\Factory"> - <arguments> - <argument name="customStrategies" xsi:type="array"> - <item name="Magento\Catalog\Api\Data\ProductInterface" xsi:type="object">\Magento\ConfigurableProduct\Model\Product\Cache\Tag\Configurable</item> - </argument> - </arguments> - </type> <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator"> <arguments> <argument name="estimators" xsi:type="array"> @@ -209,4 +202,7 @@ <type name="Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver"> <plugin name="configurable" type="Magento\ConfigurableProduct\Plugin\Catalog\Model\Product\Pricing\Renderer\SalableResolver" /> </type> + <type name="Magento\Catalog\Model\Product"> + <plugin name="product_identities_extender" type="Magento\ConfigurableProduct\Model\Plugin\ProductIdentitiesExtender" /> + </type> </config> diff --git a/app/code/Magento/Swatches/Helper/Data.php b/app/code/Magento/Swatches/Helper/Data.php index 05daf3ddf584a..8a89f0f01db79 100644 --- a/app/code/Magento/Swatches/Helper/Data.php +++ b/app/code/Magento/Swatches/Helper/Data.php @@ -158,6 +158,28 @@ public function assembleAdditionalDataEavAttribute(Attribute $attribute) return $this; } + /** + * Check is media attribute available + * + * @param ModelProduct $product + * @param string $attributeCode + * @return bool + */ + private function isMediaAvailable(ModelProduct $product, string $attributeCode): bool + { + $isAvailable = false; + + $mediaGallery = $product->getMediaGalleryEntries(); + foreach ($mediaGallery as $mediaEntry) { + if (in_array($attributeCode, $mediaEntry->getTypes(), true)) { + $isAvailable = !$mediaEntry->isDisabled(); + break; + } + } + + return $isAvailable; + } + /** * @param string $attributeCode swatch_image|image * @param ModelProduct $configurableProduct @@ -170,8 +192,8 @@ private function loadFirstVariation($attributeCode, ModelProduct $configurablePr $usedProducts = $configurableProduct->getTypeInstance()->getUsedProducts($configurableProduct); foreach ($usedProducts as $simpleProduct) { - if (!in_array($simpleProduct->getData($attributeCode), [null, self::EMPTY_IMAGE_VALUE], true) - && !array_diff_assoc($requiredAttributes, $simpleProduct->getData()) + if (!array_diff_assoc($requiredAttributes, $simpleProduct->getData()) + && $this->isMediaAvailable($simpleProduct, $attributeCode) ) { return $simpleProduct; } @@ -290,47 +312,34 @@ private function addFilterByParent(ProductCollection $productCollection, $parent */ public function getProductMediaGallery(ModelProduct $product) { - if (!in_array($product->getData('image'), [null, self::EMPTY_IMAGE_VALUE], true)) { - $baseImage = $product->getData('image'); - } else { - $productMediaAttributes = array_filter($product->getMediaAttributeValues(), function ($value) { - return $value !== self::EMPTY_IMAGE_VALUE && $value !== null; - }); - foreach ($productMediaAttributes as $attributeCode => $value) { - if ($attributeCode !== 'swatch_image') { - $baseImage = (string)$value; - break; - } + $baseImage = null; + $gallery = []; + + $mediaGallery = $product->getMediaGalleryEntries(); + foreach ($mediaGallery as $mediaEntry) { + if ($mediaEntry->isDisabled()) { + continue; } + + if (in_array('image', $mediaEntry->getTypes(), true)) { + $baseImage = $mediaEntry->getFile(); + } elseif (!$baseImage) { + $baseImage = $mediaEntry->getFile(); + } + + $gallery[$mediaEntry->getId()] = $this->getAllSizeImages($mediaEntry->getFile()); } - if (empty($baseImage)) { + if (!$baseImage) { return []; } $resultGallery = $this->getAllSizeImages($baseImage); - $resultGallery['gallery'] = $this->getGalleryImages($product); + $resultGallery['gallery'] = $gallery; return $resultGallery; } - /** - * @param ModelProduct $product - * @return array - */ - private function getGalleryImages(ModelProduct $product) - { - //TODO: remove after fix MAGETWO-48040 - $product = $this->productRepository->getById($product->getId()); - - $result = []; - $mediaGallery = $product->getMediaGalleryImages(); - foreach ($mediaGallery as $media) { - $result[$media->getData('value_id')] = $this->getAllSizeImages($media->getData('file')); - } - return $result; - } - /** * @param string $imageFile * @return array @@ -497,7 +506,7 @@ private function addFallbackOptions(array $fallbackValues, array $swatches) */ public function isProductHasSwatch(Product $product) { - $swatchAttributes = $this->getSwatchAttributes($product); + $swatchAttributes = $this->getSwatchAttributes($product) ?: []; return count($swatchAttributes) > 0; } diff --git a/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php b/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php index 3029094c9ef4a..96e84dc6eecd0 100644 --- a/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php @@ -3,13 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Swatches\Test\Unit\Helper; +use Magento\Catalog\Model\Product\Image\UrlBuilder; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Framework\EntityManager\MetadataPool; use Magento\Swatches\Model\ResourceModel\Swatch\Collection; use Magento\Swatches\Model\SwatchAttributesProvider; -use Magento\Swatches\Model\SwatchAttributeType; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -61,9 +62,9 @@ class DataTest extends \PHPUnit\Framework\TestCase private $swatchAttributesProvider; /** - * @var SwatchAttributeType|\PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Model\Product\Image\UrlBuilder */ - private $swatchTypeCheckerMock; + private $imageUrlBuilderMock; protected function setUp() { @@ -122,9 +123,9 @@ protected function setUp() $this->swatchAttributesProvider = $this->getMockBuilder(SwatchAttributesProvider::class) ->disableOriginalConstructor() ->getMock(); - $this->swatchTypeCheckerMock = $this->getMockBuilder(SwatchAttributeType::class) + $this->imageUrlBuilderMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Image\UrlBuilder::class) ->disableOriginalConstructor() - ->setMethods(['isVisualSwatch', 'isTextSwatch', 'isSwatchAttribute']) + ->setMethods(['getUrl']) ->getMock(); $this->swatchHelperObject = $this->objectManager->getObject( @@ -135,10 +136,9 @@ protected function setUp() 'productRepository' => $this->productRepoMock, 'storeManager' => $this->storeManagerMock, 'swatchCollectionFactory' => $this->swatchCollectionFactoryMock, - 'imageHelper' => $this->imageHelperMock, + 'imageUrlBuilder' => $this->imageUrlBuilderMock, 'serializer' => $serializer, 'swatchAttributesProvider' => $this->swatchAttributesProvider, - 'swatchTypeChecker' => $this->swatchTypeCheckerMock, ] ); $this->objectManager->setBackwardCompatibleProperty( @@ -232,7 +232,7 @@ public function dataForAssembleEavAttribute() public function testLoadFirstVariationWithSwatchImage($imageTypes, $expected, $requiredAttributes) { $this->getSwatchAttributes($this->productMock); - $this->getUsedProducts($imageTypes + $requiredAttributes); + $this->getUsedProducts($imageTypes + $requiredAttributes, $imageTypes); $result = $this->swatchHelperObject->loadFirstVariationWithSwatchImage($this->productMock, $requiredAttributes); @@ -295,7 +295,7 @@ public function testLoadVariationByFallback($product) public function testLoadFirstVariationWithImage($imageTypes, $expected, $requiredAttributes) { $this->getSwatchAttributes($this->productMock); - $this->getUsedProducts($imageTypes + $requiredAttributes); + $this->getUsedProducts($imageTypes + $requiredAttributes, $imageTypes); $result = $this->swatchHelperObject->loadFirstVariationWithImage($this->productMock, $requiredAttributes); @@ -333,23 +333,13 @@ public function dataForVariationWithImage() public function testLoadFirstVariationWithImageNoProduct() { - $this->swatchAttributesProvider - ->method('provide') - ->with($this->productMock) - ->willReturn([]); $result = $this->swatchHelperObject->loadVariationByFallback($this->productMock, ['color' => 31]); - $this->assertFalse($result); } public function testLoadVariationByFallbackWithoutProduct() { - $this->swatchAttributesProvider - ->method('provide') - ->with($this->productMock) - ->willReturn([]); $result = $this->swatchHelperObject->loadFirstVariationWithImage($this->productMock, ['color' => 31]); - $this->assertFalse($result); } @@ -358,44 +348,50 @@ public function testLoadVariationByFallbackWithoutProduct() */ public function testGetProductMediaGallery($mediaGallery, $image) { - $this->productMock->expects($this->once())->method('getMediaAttributeValues')->willReturn($mediaGallery); - $this->productMock->expects($this->any())->method('getId')->willReturn(95); - - $this->imageHelperMock->expects($this->any()) - ->method('init') - ->willReturnMap([ - [$this->productMock, 'product_page_image_large', [], $this->imageHelperMock], - [$this->productMock, 'product_page_image_medium', [], $this->imageHelperMock], - [$this->productMock, 'product_page_image_small', [], $this->imageHelperMock], - ]); - - $this->imageHelperMock->expects($this->any()) - ->method('setImageFile') - ->with($image) - ->willReturnSelf(); - $this->imageHelperMock->expects($this->any()) - ->method('getUrl') - ->willReturn('http://full_path_to_image/magento1.png'); - - $this->productRepoMock->expects($this->any()) - ->method('getById') - ->with(95) - ->willReturn($this->productMock); - - $mediaObject = $this->createMock(\Magento\Framework\DataObject::class); - $iterator = new \ArrayIterator([$mediaObject]); - $mediaCollectionMock = $this->createMock(\Magento\Framework\Data\Collection::class); - $mediaCollectionMock->expects($this->any())->method('getIterator')->willReturn($iterator); - $mediaObject->method('getData')->withConsecutive( - ['value_id'], - ['file'] - )->willReturnOnConsecutiveCalls( - 0, - $image - ); - $this->productMock->method('getMediaGalleryImages')->willReturn($mediaCollectionMock); + $mediaGalleryEntries = []; + $id = 0; + $mediaUrls = []; + foreach ($mediaGallery as $mediaType => $mediaFile) { + $mediaGalleryEntryMock = $this->getMockBuilder( + \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface::class + )->getMock(); + $mediaGalleryEntryMock->expects($this->atLeastOnce()) + ->method('isDisabled') + ->willReturn(false); + $mediaGalleryEntryMock->expects($this->atLeastOnce()) + ->method('getTypes') + ->willReturn([$mediaType]); + $mediaGalleryEntryMock->expects($this->atLeastOnce()) + ->method('getFile') + ->willReturn($mediaFile); + $mediaGalleryEntryMock->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn(++$id); + + $mediaGalleryEntries[] = $mediaGalleryEntryMock; + $mediaUrls[] = [$mediaFile, 'product_swatch_image_large', 'http://full_path_to_image' . $mediaFile]; + $mediaUrls[] = [$mediaFile, 'product_swatch_image_medium' ,'http://full_path_to_image' . $mediaFile]; + $mediaUrls[] = [$mediaFile, 'product_swatch_image_small','http://full_path_to_image' . $mediaFile]; + } + + $this->productMock->expects($this->once()) + ->method('getMediaGalleryEntries') + ->willReturn($mediaGalleryEntries); + + if ($mediaGallery) { + $this->imageUrlBuilderMock->expects($this->any()) + ->method('getUrl') + ->willReturnMap($mediaUrls); + } - $this->swatchHelperObject->getProductMediaGallery($this->productMock); + $productMediaGallery = $this->swatchHelperObject->getProductMediaGallery($this->productMock); + if ($mediaGallery) { + $this->assertContains($image, $productMediaGallery['large']); + $this->assertContains($image, $productMediaGallery['medium']); + $this->assertContains($image, $productMediaGallery['small']); + } else { + $this->assertEmpty($productMediaGallery); + } } public function dataForMediaGallery() @@ -408,7 +404,7 @@ public function dataForMediaGallery() 'thumbnail' => '/m/a/magento3.png', 'swatch_image' => '/m/a/magento4.png', ], - '/m/a/magento1.png' + '/m/a/magento1.png', ], [ [ @@ -416,7 +412,7 @@ public function dataForMediaGallery() 'thumbnail' => '/m/a/magento5.png', 'swatch_image' => '/m/a/magento6.png', ], - '/m/a/magento4.png' + '/m/a/magento4.png', ], [ [], @@ -435,22 +431,45 @@ protected function getSwatchAttributes() ->willReturn($returnFromProvideMethod); } - protected function getUsedProducts(array $attributes) + protected function getUsedProducts(array $attributes, array $imageTypes) { $this->productMock ->expects($this->atLeastOnce()) ->method('getTypeInstance') ->willReturn($this->configurableMock); - $product1 = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['hasData']); - $product1->setData($attributes); - - $product2 = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['hasData']); - $product2->setData($attributes); - - $simpleProducts = [$product2, $product1]; + $simpleProducts = []; + for ($i = 0; $i < 2; $i++) { + $simpleProduct = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['hasData', 'getMediaGalleryEntries']) + ->getMock(); + $simpleProduct->setData($attributes); + + $mediaGalleryEntries = []; + foreach (array_keys($imageTypes) as $mediaType) { + $mediaGalleryEntryMock = $this->getMockBuilder( + \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface::class + )->getMock(); + $mediaGalleryEntryMock->expects($this->any()) + ->method('isDisabled') + ->willReturn(false); + $mediaGalleryEntryMock->expects($this->any()) + ->method('getTypes') + ->willReturn([$mediaType]); + + $mediaGalleryEntries[] = $mediaGalleryEntryMock; + } + $simpleProduct->expects($this->any()) + ->method('getMediaGalleryEntries') + ->willReturn($mediaGalleryEntries); + + $simpleProducts[] = $simpleProduct; + } - $this->configurableMock->expects($this->once())->method('getUsedProducts')->with($this->productMock) + $this->configurableMock->expects($this->once()) + ->method('getUsedProducts') + ->with($this->productMock) ->willReturn($simpleProducts); } @@ -761,79 +780,4 @@ public function testIsProductHasSwatch() $result = $this->swatchHelperObject->isProductHasSwatch($this->productMock); $this->assertEquals(true, $result); } - - /** - * @param bool $boolResult - * @return void - * @dataProvider dataIsSwatch - */ - public function testIsVisualSwatch(bool $boolResult) : void - { - $this->swatchTypeCheckerMock - ->expects($this->once()) - ->method('isVisualSwatch') - ->with($this->attributeMock) - ->willReturn($boolResult); - $result = $this->swatchHelperObject->isVisualSwatch($this->attributeMock); - - if ($boolResult) { - $this->assertTrue($result); - } else { - $this->assertFalse($result); - } - } - - /** - * @return array - */ - public function dataIsSwatch() : array - { - return [ - [true], - [false], - ]; - } - - /** - * @param bool $boolResult - * @return void - * @dataProvider dataIsSwatch - */ - public function testIsTextSwatch(bool $boolResult) : void - { - $this->swatchTypeCheckerMock - ->expects($this->once()) - ->method('isTextSwatch') - ->with($this->attributeMock) - ->willReturn($boolResult); - - $result = $this->swatchHelperObject->isTextSwatch($this->attributeMock); - - if ($boolResult) { - $this->assertTrue($result); - } else { - $this->assertFalse($result); - } - } - - /** - * @param bool $boolResult - * @return void - * @dataProvider dataIsSwatch - */ - public function testIsSwatchAttribute(bool $boolResult) : void - { - $this->swatchTypeCheckerMock - ->expects($this->once()) - ->method('isSwatchAttribute') - ->with($this->attributeMock) - ->willReturn($boolResult); - - $result = $this->swatchHelperObject->isSwatchAttribute($this->attributeMock); - if ($boolResult) { - $this->assertTrue($result); - } else { - $this->assertFalse($result); - } - } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ProductTest.php new file mode 100644 index 0000000000000..223c9fbe708e2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/ProductTest.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Api\ProductRepositoryInterface; + +class ProductTest extends \PHPUnit\Framework\TestCase +{ + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ + public function testGetIdentities() + { + $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + $confProduct = $productRepository->get('configurable'); + $simple10Product = $productRepository->get('simple_10'); + $simple20Product = $productRepository->get('simple_20'); + + $this->assertEmpty(array_diff($confProduct->getIdentities(), $simple10Product->getIdentities())); + $this->assertEmpty(array_diff($confProduct->getIdentities(), $simple20Product->getIdentities())); + } +} From d63c3d4b5d117a3c6741aebf6c009453f4502ecb Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Fri, 18 May 2018 09:17:42 -0500 Subject: [PATCH 089/333] MAGETWO-90358: Auto-generate ExtensionAttributes object --- .../Magento/Customer/Api/AddressRepositoryTest.php | 8 +++++--- .../Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php | 3 +-- .../Model/ResourceModel/AddressRepositoryTest.php | 8 +++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Api/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Api/AddressRepositoryTest.php index 308824db23b57..cf6e82639767e 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Api/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Api/AddressRepositoryTest.php @@ -161,9 +161,11 @@ public function testSaveNewAddress() $expectedNewAddress->setId($savedAddress->getId()); $expectedNewAddress->setRegion($this->_expectedAddresses[1]->getRegion()); - // Call getters on extension attributes to initialize them - $savedAddress->getExtensionAttributes(); - $savedAddress->getRegion()->getExtensionAttributes(); + $this->assertEquals($expectedNewAddress->getExtensionAttributes(), $savedAddress->getExtensionAttributes()); + $this->assertEquals( + $expectedNewAddress->getRegion()->getExtensionAttributes(), + $savedAddress->getRegion()->getExtensionAttributes() + ); $this->assertEquals($expectedNewAddress, $savedAddress); } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php index c0c2dbb8554b3..8d82ad94dfab4 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/PersonalInfoTest.php @@ -121,8 +121,7 @@ public function testGetCustomerEmpty() { $expectedCustomer = $this->createCustomerAndAddToBackendSession(); $actualCustomer = $this->_block->getCustomer(); - // Trigger extension attributes generation - $actualCustomer->getExtensionAttributes(); + $this->assertEquals($expectedCustomer->getExtensionAttributes(), $actualCustomer->getExtensionAttributes()); $this->assertEquals($expectedCustomer, $actualCustomer); } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php index 67c43c19c5ba4..4257b5a928505 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php @@ -167,9 +167,11 @@ public function testSaveNewAddress() $expectedNewAddress->setId($savedAddress->getId()); $expectedNewAddress->setRegion($this->_expectedAddresses[1]->getRegion()); - // Trigger extension attributes object generation - $savedAddress->getExtensionAttributes(); - $savedAddress->getRegion()->getExtensionAttributes(); + $this->assertEquals($expectedNewAddress->getExtensionAttributes(), $savedAddress->getExtensionAttributes()); + $this->assertEquals( + $expectedNewAddress->getRegion()->getExtensionAttributes(), + $savedAddress->getRegion()->getExtensionAttributes() + ); $this->assertEquals($expectedNewAddress, $savedAddress); } From 6c799ecbb82c9ef66a5db5e3006beda99fc0597c Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Fri, 18 May 2018 09:57:22 -0500 Subject: [PATCH 090/333] MAGETWO-91893: Verify the product attribute is not visible on storefront if it is empty for the Product - added test --- .../Catalog/Data/ProductAttributeData.xml | 2 +- .../Catalog/Data/ProductData.xml | 1 + ...torefrontProductWithEmptyAttributeTest.xml | 61 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductWithEmptyAttributeTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductAttributeData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductAttributeData.xml index 0adf1e91ae853..946a3f614c1e1 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductAttributeData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductAttributeData.xml @@ -32,7 +32,7 @@ <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> <entity name="productAttributeWithTwoOptions" type="ProductAttribute"> - <data key="attribute_code" unique="suffix">attribute</data> + <data key="attribute_code" unique="suffix">testattribute</data> <data key="frontend_input">select</data> <data key="scope">global</data> <data key="is_required">false</data> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml index 70e15717142d9..cc6a32e7223f9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml @@ -51,6 +51,7 @@ <data key="visibility">4</data> <data key="status">1</data> <data key="quantity">1000</data> + <data key="urlKey" unique="suffix">simpleproduct</data> <data key="weight">1</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductWithEmptyAttributeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductWithEmptyAttributeTest.xml new file mode 100644 index 0000000000000..c80025c299885 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductWithEmptyAttributeTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontProductWithEmptyAttributeTest"> + <annotations> + <title value="Product attribute is not visible on storefront if it is empty"/> + <description value="Product attribute should not be visible on storefront if it is empty"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-91893"/> + <group value="product"/> + </annotations> + <before> + <!--<actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/>--> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <!--<createData entity="productAttributeWithTwoOptions" stepKey="createProductAttribute"/>--> + </before> + <after> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteSimpleProduct"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <!--<deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/>--> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="navigateToProductAttribute"/> + <waitForPageLoad stepKey="wait1"/> + <!--<click selector="#add" stepKey="addNewAttribute"/>--> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="testattribute" stepKey="fillAttributeLabel"/> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="Dropdown" stepKey="selectCatalogAttributeInputType"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> + <selectOption selector="#is_visible_on_front" userInput="Yes" stepKey="isVisibleOnStoreFront"/> + <selectOption selector="#used_in_product_listing" userInput="Yes" stepKey="usedInProductListing"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> + <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/> + <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> + <dragAndDrop selector1="{{UnassignedAttributes.ProductAttributeName('testattribute')}}" selector2="{{Group.FolderName('Product Details')}}" stepKey="moveProductAttributeToGroup"/> + <click selector="{{AttributeSetSection.Save}}" stepKey="saveAttributeSet"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear" /> + <seeElement selector=".message-success" stepKey="assertSuccess"/> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="SimpleProduct"/> + </actionGroup> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> + <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.urlKey)}}" stepKey="goProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForProductPageToLoad"/> + <!--<see selector="#product-attribute-specs-table tr th:nth-of-type(1)" stepKey="seeAttribute"/>--> + <!--<scrollTo selector=".additional-attributes tbody td:nth-of-type(1)" stepKey="seeAttribute1"/>--> + <dontSeeElement selector="//table[@id='product-attribute-specs-table']/tbody/tr/th[contains(text(),'testattribute')]" stepKey="seeAttribute2"/> + </test> + </tests> From b649fee9befaf18263ee889934d8d6c7cdb01bcd Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Fri, 18 May 2018 09:57:41 -0500 Subject: [PATCH 091/333] MC-95: Admin should be able to edit configuration to add a value to an existing attribute --- .../Data/ProductConfigurableAttributeData.xml | 4 ++ .../AdminConfigurableProductUpdateTest.xml | 53 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ProductConfigurableAttributeData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ProductConfigurableAttributeData.xml index da02b259956c4..5a1c0139d56ca 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ProductConfigurableAttributeData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ProductConfigurableAttributeData.xml @@ -25,4 +25,8 @@ <data key="name" unique="suffix">Blue</data> <data key="price">3.00</data> </entity> + <entity name="colorProductAttribute4" type="product_attribute"> + <data key="name" unique="suffix">Orange</data> + <data key="price">99.99</data> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml index 646bf9f65bbe5..4aca40b761fd1 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml @@ -246,4 +246,57 @@ <dontSee selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option1" stepKey="dontSeeOption1InStorefront"/> <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="option2" stepKey="seeOption2Again"/> </test> + + <test name="AdminConfigurableProductAddConfigurationTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Edit a configurable product in admin"/> + <title value="Admin should be able to edit configuration to add a value to an existing attribute"/> + <description value="Admin should be able to edit configuration to add a value to an existing attribute"/> + <testCaseId value="MC-95"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="logout"/> + </after> + + <!-- Create a configurable product via the UI --> + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- + After saving, we are still on the product edit page. There is no need to reload or go to this page + again, because a round trip to the server has already happened. + --> + + <!-- Add a configuration option to the configurable product --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickEditConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> + <fillField userInput="{{colorProductAttribute4.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="42" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!-- Verify that the added option is present in the storefront --> + <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeInDropDown1"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute2.name}}" stepKey="seeInDropDown2"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute3.name}}" stepKey="seeInDropDown3"/> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" userInput="{{colorProductAttribute4.name}}" stepKey="seeInDropDown4"/> + </test> </tests> From 17688a1bd0dff346b8cd8be998a41657fa346586 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Fri, 18 May 2018 10:13:50 -0500 Subject: [PATCH 092/333] MAGETWO-91240: Automate MFTF - Added actionGroup to config Out Of Stock product --- .../Catalog/Data/ProductData.xml | 13 +++++++++ .../DisplayOutOfStockProductActionGroup.xml | 27 +++++++++++++++++++ .../ActionGroup/SwitcherActionGroup.xml | 9 +++---- .../Config/Page/AdminConfigPage.xml | 3 +++ .../Config/Section/InventorySection.xml | 13 +++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/InventorySection.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml index 70e15717142d9..d65328a2fb626 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml @@ -78,6 +78,19 @@ <data key="urlKey" unique="suffix">simple</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> </entity> + <entity name="SimpleProduct4" type="product"> + <data key="sku" unique="suffix">SimpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">0</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="NewSimpleProduct" type="product"> <data key="price">321.00</data> </entity> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml new file mode 100644 index 0000000000000..3e466427d661b --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="displayOutOfStockProduct"> + <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> + <waitForPageLoad stepKey="waitForConfigPageToLoad"/> + <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> + <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForSwitcherDropdown" /> + <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="Yes" stepKey="switchToYes" /> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> + </actionGroup> + <actionGroup name="dontDisplayOutOfStockProduct"> + <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> + <waitForPageLoad stepKey="waitForConfigPageToLoad"/> + <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/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/SwitcherActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/SwitcherActionGroup.xml index f127b14931c98..66c4a2b56cb15 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/SwitcherActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/SwitcherActionGroup.xml @@ -11,12 +11,9 @@ <actionGroup name="SwitchToVersion4ActionGroup"> <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> <waitForPageLoad stepKey="waitForConfigPageToLoad"/> - <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{ContentManagementSection.SwitcherSystemValue}}" stepKey="waitForCheckbox" /> - <uncheckOption selector="{{ContentManagementSection.SwitcherSystemValue}}" stepKey="uncheckUseSystemValue"/> - <waitForElementVisible selector="{{ContentManagementSection.Switcher}}" stepKey="waitForSwitcherDropdown" /> - <selectOption selector="{{ContentManagementSection.Switcher}}" userInput="TinyMCE 4" stepKey="switchToVersion4" /> - <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> + <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> + <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForDropdown" /> + <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="Yes" stepKey="switchToYes" /> <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> </actionGroup> </actionGroups> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Page/AdminConfigPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Page/AdminConfigPage.xml index 9ae1ecab1c26e..cf1fe307c4c58 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Page/AdminConfigPage.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Page/AdminConfigPage.xml @@ -12,4 +12,7 @@ <page name="AdminContentManagementPage" url="admin/system_config/edit/section/cms/" area="admin" module="Magento_Config"> <section name="ContentManagementSection"/> </page> + <page name="InventoryConfigurationPage" url="admin/system_config/edit/section/cataloginventory/" area="admin" module="Magento_Config"> + <section name="InventorySection"/> + </page> </pages> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/InventorySection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/InventorySection.xml new file mode 100644 index 0000000000000..45fea63e88e75 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/InventorySection.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="InventoryConfigSection"> + <element name="DisplayOutOfStockSystemValue" type="button" selector="#cataloginventory_options_show_out_of_stock_inherit"/> + <element name="DisplayOutOfStockDropdown" type="button" selector="#cataloginventory_options_show_out_of_stock"/> + </section> +</sections> From 527fbbb9957dd99e5a5ebc86d73c84853e5c7b2a Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Fri, 18 May 2018 10:28:40 -0500 Subject: [PATCH 093/333] MAGETWO-91409: Rush theme not available on Themes Grid with clean 2.3 installation - added functional test --- .../FunctionalTest/Theme/Page/ThemesPage.xml | 14 +++++++++ .../Theme/Section/AdminThemeSection.xml | 21 +++++++++++++ .../FunctionalTest/Theme/Test/ThemeTest.xml | 31 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Page/ThemesPage.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Section/AdminThemeSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Test/ThemeTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Page/ThemesPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Page/ThemesPage.xml new file mode 100644 index 0000000000000..358d88875d02f --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Page/ThemesPage.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="ThemesPageIndex" url="admin/system_design_theme/" area="admin" module="Magento_Theme"> + <section name="AdminThemeSection"/> + </page> +</pages> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Section/AdminThemeSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Section/AdminThemeSection.xml new file mode 100644 index 0000000000000..281cc290b4f39 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Section/AdminThemeSection.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminThemeSection"> + <!--All rows in a specific Column e.g. {{Section.rowsInColumn('columnName')}}--> + <element name="rowsInColumn" type="text" selector="//tr/td[contains(@class, '{{column}}')]" parameterized="true"/> + <!--selector for Theme Title column since it needs to be handled separately--> + <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="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 diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Test/ThemeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Test/ThemeTest.xml new file mode 100644 index 0000000000000..8764750d473c3 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Theme/Test/ThemeTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="ThemeTest"> + <annotations> + <features value="Theme Test"/> + <title value="Magento Rush theme should not be available in Themes grid"/> + <description value="Magento Rush theme should not be available in Themes grid"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-91409"/> + <group value="theme"/> + </annotations> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <!--Login to Admin Area--> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + <!--Navigate to Themes page--> + <amOnPage url="{{ThemesPageIndex.url}}" stepKey="navigateToThemesIndexPage" /> + <waitForPageLoad stepKey="wait1"/> + <dontSee selector="{{AdminThemeSection.gridCell('Magento Rush')}}" stepKey="dontSeeRushThemeInTitleColumn"/> + <seeNumberOfElements selector="{{AdminThemeSection.rowsInColumn('theme_path')}}" userInput="2" stepKey="see2RowsOnTheGrid"/> + </test> +</tests> From eb64a14bbb5c72ed1dc8c1368d8f4af4b59e2f62 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Fri, 18 May 2018 11:36:01 -0500 Subject: [PATCH 094/333] MC-113: Admin should be able to add default image for simple products. - Split adding and removing image into separte tests. --- .../AdminAddDefaultImageBundleProductTest.xml | 86 ++++++++++++ ...inRemoveDefaultImageBundleProductTest.xml} | 28 +--- .../AdminAddDefaultImageSimpleProductTest.xml | 59 +++++++++ ...AdminAddDefaultImageVirtualProductTest.xml | 59 +++++++++ ...inRemoveDefaultImageSimpleProductTest.xml} | 30 +---- ...nRemoveDefaultImageVirtualProductTest.xml} | 29 +---- .../AdminAddDefaultImageConfigurableTest.xml | 122 ++++++++++++++++++ ...minRemoveDefaultImageConfigurableTest.xml} | 29 +---- ...AddDefaultImageDownloadableProductTest.xml | 73 +++++++++++ ...veDefaultImageDownloadableProductTest.xml} | 28 +--- ...AdminAddDefaultImageGroupedProductTest.xml | 82 ++++++++++++ ...nRemoveDefaultImageGroupedProductTest.xml} | 29 +---- 12 files changed, 510 insertions(+), 144 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddDefaultImageBundleProductTest.xml rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/{AdminAddRemoveProductImageBundleProductTest.xml => AdminRemoveDefaultImageBundleProductTest.xml} (78%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddDefaultImageSimpleProductTest.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddDefaultImageVirtualProductTest.xml rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/{AdminAddRemoveProductImageSimpleProductTest.xml => AdminRemoveDefaultImageSimpleProductTest.xml} (65%) rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/{AdminAddRemoveProductImageVirtualProductTest.xml => AdminRemoveDefaultImageVirtualProductTest.xml} (65%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddDefaultImageConfigurableTest.xml rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/{AdminAddRemoveProductImageConfigurableTest.xml => AdminRemoveDefaultImageConfigurableTest.xml} (80%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddDefaultImageDownloadableProductTest.xml rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/{AdminAddRemoveProductImageDownloadableProductTest.xml => AdminRemoveDefaultImageDownloadableProductTest.xml} (72%) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddDefaultImageGroupedProductTest.xml rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/{AdminAddRemoveProductImageGroupedProductTest.xml => AdminRemoveDefaultImageGroupedProductTest.xml} (75%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddDefaultImageBundleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddDefaultImageBundleProductTest.xml new file mode 100644 index 0000000000000..064c57958f37b --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddDefaultImageBundleProductTest.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultImageBundleProductTest"> + <annotations> + <features value="Bundle"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default images for a Bundle Product"/> + <description value="Admin should be able to add default images for a Bundle Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-115"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Create a bundle product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle"/> + <waitForPageLoad stepKey="waitForProductPageLoadBundle"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillBundleProductNameAndSku"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Add two bundle items --> + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption3"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" stepKey="waitForBundleOptions"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle('0')}}" userInput="{{BundleProduct.optionTitle1}}" stepKey="fillOptionTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> + <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> + <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> + <actionGroup ref="filterProductGridBySku" 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"/> + + <!-- Add image to product --> + <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + + <!--Save product--> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!-- Assert product image in admin product form --> + <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Assert product image in storefront product page --> + <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <argument name="product" value="BundleProduct"/> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddRemoveProductImageBundleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminRemoveDefaultImageBundleProductTest.xml similarity index 78% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddRemoveProductImageBundleProductTest.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminRemoveDefaultImageBundleProductTest.xml index 39c2ec33b9b3a..3dec79cf1b054 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminAddRemoveProductImageBundleProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Test/AdminRemoveDefaultImageBundleProductTest.xml @@ -8,14 +8,14 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="AdminAddRemoveProductImageBundleProductTest"> + <test name="AdminRemoveDefaultImageBundleProductTest"> <annotations> <features value="Bundle"/> <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to add/remove images for a Bundle Product"/> - <description value="Admin should be able to add/remove images for a Bundle Product"/> + <title value="Admin should be able to remove default images from a Bundle Product"/> + <description value="Admin should be able to remove default images from a Bundle Product"/> <severity value="MAJOR"/> - <testCaseId value="MC-115"/> + <testCaseId value="MC-200"/> <group value="Bundle"/> </annotations> <before> @@ -69,27 +69,7 @@ <!--Save product--> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> - <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> - - <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - - <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> - <argument name="product" value="BundleProduct"/> - <argument name="image" value="MagentoLogo"/> - </actionGroup> - <!-- Remove image from product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageRemove"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> - <argument name="product" value="BundleProduct"/> - </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddDefaultImageSimpleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddDefaultImageSimpleProductTest.xml new file mode 100644 index 0000000000000..3b3693d0ff8a6 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddDefaultImageSimpleProductTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultImageSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default image for a Simple Product"/> + <description value="Admin should be able to add default images for a Simple Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-113"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!--Create product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateSimpleProduct"> + <argument name="product" value="SimpleProduct3"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillSimpleProductMain"> + <argument name="product" value="SimpleProduct3"/> + </actionGroup> + + <!-- Add image to product --> + <actionGroup ref="addProductImage" stepKey="addImageForProductSimple"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + + <!-- Assert product image in admin product form --> + <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="SimpleProduct3"/> + </actionGroup> + + <!-- Assert product image in storefront product page --> + <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <argument name="product" value="SimpleProduct3"/> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddDefaultImageVirtualProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddDefaultImageVirtualProductTest.xml new file mode 100644 index 0000000000000..292ba20f48bd3 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddDefaultImageVirtualProductTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddRemoveProductImageVirtualProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default images for a Virtual Product"/> + <description value="Admin should be able to add default images for a Virtual Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-103"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!--Create product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductMain"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + + <!-- Add image to product --> + <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!-- Assert product image in admin product form --> + <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="defaultVirtualProduct"/> + </actionGroup> + + <!-- Assert product image in storefront product page --> + <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <argument name="product" value="defaultVirtualProduct"/> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageSimpleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageSimpleProductTest.xml similarity index 65% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageSimpleProductTest.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageSimpleProductTest.xml index 23911e88002d1..36b2c5e9ffac5 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageSimpleProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageSimpleProductTest.xml @@ -8,15 +8,16 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="AdminAddRemoveProductImageSimpleProductTest"> + <test name="AdminRemoveDefaultImageSimpleProductTest"> <annotations> <features value="Catalog"/> <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to add/remove images for a Simple Product"/> - <description value="Admin should be able to add/remove images for a Simple Product"/> + <title value="Admin should be able to remove default images from a Simple Product"/> + <description value="Admin should be able to remove default images from a Simple Product"/> <severity value="MAJOR"/> - <testCaseId value="MC-113"/> + <testCaseId value="MC-195"/> <group value="Catalog"/> + <group value="ji"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -40,29 +41,10 @@ <actionGroup ref="addProductImage" stepKey="addImageForProductSimple"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> - - <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> - - <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> - <argument name="product" value="SimpleProduct3"/> - </actionGroup> - <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> - <argument name="product" value="SimpleProduct3"/> - <argument name="image" value="MagentoLogo"/> - </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> <!-- Remove image from product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageRemove"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> - <argument name="product" value="SimpleProduct3"/> - </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageVirtualProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageVirtualProductTest.xml similarity index 65% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageVirtualProductTest.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageVirtualProductTest.xml index d6f51108c3d66..e872d96b8fdff 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddRemoveProductImageVirtualProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageVirtualProductTest.xml @@ -8,14 +8,14 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="AdminAddRemoveProductImageVirtualProductTest"> + <test name="AdminRemoveDefaultImageVirtualProductTest"> <annotations> <features value="Catalog"/> <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to add/remove images for a Virtual Product"/> - <description value="Admin should be able to add/remove images for a Virtual Product"/> + <title value="Admin should be able to remove default images from a Virtual Product"/> + <description value="Admin should be able to remove default images from a Virtual Product"/> <severity value="MAJOR"/> - <testCaseId value="MC-103"/> + <testCaseId value="MC-197"/> <group value="Catalog"/> </annotations> <before> @@ -40,29 +40,10 @@ <actionGroup ref="addProductImage" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> - - <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> - - <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> - <argument name="product" value="defaultVirtualProduct"/> - </actionGroup> - <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> - <argument name="product" value="defaultVirtualProduct"/> - <argument name="image" value="MagentoLogo"/> - </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!-- Remove image from product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageRemove"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> - <argument name="product" value="defaultVirtualProduct"/> - </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddDefaultImageConfigurableTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddDefaultImageConfigurableTest.xml new file mode 100644 index 0000000000000..55d6029c917ce --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddDefaultImageConfigurableTest.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultImageConfigurableTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default images for a Configurable Product"/> + <description value="Admin should be able to add default images for a Configurable Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-101"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="categoryHandle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="BaseConfigurableProduct" stepKey="baseConfigProductHandle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="baseConfigProductHandle"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="baseConfigProductHandle"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="baseConfigProductHandle"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChild1"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChild2"/> + <deleteData createDataKey="baseConfigProductHandle" stepKey="deleteConfig"/> + <deleteData createDataKey="categoryHandle" stepKey="deleteCategory"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductAttribute"/> + </after> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGrid"> + <argument name="product" value="$$baseConfigProductHandle$$"/> + </actionGroup> + <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid1"/> + + <!-- Add image to product --> + <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!-- Assert product image in admin product form --> + <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="$$baseConfigProductHandle$$"/> + </actionGroup> + + <!-- Assert product image in storefront product page --> + <actionGroup ref="assertProductImageStorefrontProductPage2" stepKey="assertProductImageStorefrontProductPage"> + <argument name="product" value="$$baseConfigProductHandle$$"/> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddRemoveProductImageConfigurableTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminRemoveDefaultImageConfigurableTest.xml similarity index 80% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddRemoveProductImageConfigurableTest.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminRemoveDefaultImageConfigurableTest.xml index efaecc34df7a2..287cdce875d77 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminAddRemoveProductImageConfigurableTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminRemoveDefaultImageConfigurableTest.xml @@ -8,14 +8,14 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="AdminAddRemoveProductImageConfigurableTest"> + <test name="AdminRemoveDefaultImageConfigurableTest"> <annotations> <features value="ConfigurableProduct"/> <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to add/remove images for a Configurable Product"/> - <description value="Admin should be able to add/remove images for a Configurable Product"/> + <title value="Admin should be able to remove default images from a Configurable Product"/> + <description value="Admin should be able to remove default images from a Configurable Product"/> <severity value="MAJOR"/> - <testCaseId value="MC-101"/> + <testCaseId value="MC-196"/> <group value="ConfigurableProduct"/> </annotations> @@ -103,29 +103,10 @@ <actionGroup ref="addProductImage" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> - - <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> - - <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="AssertProductInStorefrontProductPage"> - <argument name="product" value="$$baseConfigProductHandle$$"/> - </actionGroup> - <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage2" stepKey="assertProductImageStorefrontProductPage"> - <argument name="product" value="$$baseConfigProductHandle$$"/> - <argument name="image" value="MagentoLogo"/> - </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!-- Remove image from product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageRemove"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> - <argument name="product" value="$$baseConfigProductHandle$$"/> - </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddDefaultImageDownloadableProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddDefaultImageDownloadableProductTest.xml new file mode 100644 index 0000000000000..e95e788957395 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddDefaultImageDownloadableProductTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultImageDownloadableProductTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default images for a Downloadable Product"/> + <description value="Admin should be able to add default images for a Downloadable Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-117"/> + <group value="Downloadable"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + + <!-- Add image to product --> + <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + + <!-- Add downloadable links --> + <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection"/> + <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> + <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"> + <argument name="link" value="downloadableLinkWithMaxDownloads"/> + </actionGroup> + <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableLink"> + <argument name="link" value="downloadableLink"/> + </actionGroup> + + <!--Save product--> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!-- Assert product image in admin product form --> + <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + + <!-- Assert product image in storefront product page --> + <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <argument name="product" value="DownloadableProduct"/> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddRemoveProductImageDownloadableProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminRemoveDefaultImageDownloadableProductTest.xml similarity index 72% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddRemoveProductImageDownloadableProductTest.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminRemoveDefaultImageDownloadableProductTest.xml index 7417786046f8d..43d73e45a943f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminAddRemoveProductImageDownloadableProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Test/AdminRemoveDefaultImageDownloadableProductTest.xml @@ -8,14 +8,14 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="AdminAddRemoveProductImageDownloadableProductTest"> + <test name="AdminRemoveDefaultImageDownloadableProductTest"> <annotations> <features value="Downloadable"/> <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to add/remove images for a Downloadable Product"/> - <description value="Admin should be able to add/remove images for a Downloadable Product"/> + <title value="Admin should be able to remove default images from a Downloadable Product"/> + <description value="Admin should be able to remove default images from a Downloadable Product"/> <severity value="MAJOR"/> - <testCaseId value="MC-117"/> + <testCaseId value="MC-201"/> <group value="Downloadable"/> </annotations> <before> @@ -56,27 +56,7 @@ <!--Save product--> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> - <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> - - <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> - <argument name="product" value="DownloadableProduct"/> - </actionGroup> - - <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> - <argument name="product" value="DownloadableProduct"/> - <argument name="image" value="MagentoLogo"/> - </actionGroup> - <!-- Remove image from product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageRemove"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> - <argument name="product" value="DownloadableProduct"/> - </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddDefaultImageGroupedProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddDefaultImageGroupedProductTest.xml new file mode 100644 index 0000000000000..2120045513e13 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddDefaultImageGroupedProductTest.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultImageGroupedProductTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to add default images for a Grouped Product"/> + <description value="Admin should be able to add default images for a Grouped Product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-106"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProductOne"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProductTwo"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Create product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <actionGroup ref="fillGroupedProductForm" stepKey="fillProductForm"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToGroupedSection"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductsSection"/> + <click selector="body" stepKey="clickBodyToCorrectFocusGrouped"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> + <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForGroupedProductModal"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterGroupedProducts"> + <argument name="sku" value="api-simple-product"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToGroupPanel.nThCheckbox('0')}}" stepKey="checkFilterResult1"/> + <checkOption selector="{{AdminAddProductsToGroupPanel.nThCheckbox('1')}}" stepKey="checkFilterResult2"/> + + <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> + + <!-- Add image to product --> + <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!-- Assert product image in admin product form --> + <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + + <!-- Assert product image in storefront product page --> + <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <argument name="product" value="GroupedProduct"/> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddRemoveProductImageGroupedProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminRemoveDefaultImageGroupedProductTest.xml similarity index 75% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddRemoveProductImageGroupedProductTest.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminRemoveDefaultImageGroupedProductTest.xml index f76f0cb7e3798..b1105369b2ff5 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminAddRemoveProductImageGroupedProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Test/AdminRemoveDefaultImageGroupedProductTest.xml @@ -8,14 +8,14 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="AdminAddRemoveProductImageGroupedProductTest"> + <test name="AdminRemoveDefaultImageGroupedProductTest"> <annotations> <features value="GroupedProduct"/> <stories value="Add/remove images and videos for all product types and category"/> - <title value="Admin should be able to add/remove images for a Grouped Product"/> - <description value="Admin should be able to add/remove images for a Grouped Product"/> + <title value="Admin should be able to remove default images from a Grouped Product"/> + <description value="Admin should be able to remove default images from a Grouped Product"/> <severity value="MAJOR"/> - <testCaseId value="MC-106"/> + <testCaseId value="MC-198"/> <group value="GroupedProduct"/> </annotations> <before> @@ -63,29 +63,10 @@ <actionGroup ref="addProductImage" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> - - <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> - - <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> - <argument name="product" value="GroupedProduct"/> - </actionGroup> - <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> - <argument name="product" value="GroupedProduct"/> - <argument name="image" value="MagentoLogo"/> - </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!-- Remove image from product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageRemove"/> - <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> - <argument name="product" value="GroupedProduct"/> - </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> <!-- Skip success message check when saving product because of bug MAGETWO-91177 --> From aad511131b1406666dbe3fab1ab09378632d2f3b Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Fri, 18 May 2018 11:45:57 -0500 Subject: [PATCH 095/333] MAGETWO-91893: Verify the product attribute is not visible on storefront if it is empty for the Product - added clean up steps and updated Data file --- .../Catalog/Data/ProductAttributeData.xml | 23 ++++++++++++++++++- ...torefrontProductWithEmptyAttributeTest.xml | 17 ++------------ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductAttributeData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductAttributeData.xml index 946a3f614c1e1..ab0eac4960dc6 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductAttributeData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductAttributeData.xml @@ -32,7 +32,7 @@ <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> <entity name="productAttributeWithTwoOptions" type="ProductAttribute"> - <data key="attribute_code" unique="suffix">testattribute</data> + <data key="attribute_code" unique="suffix">attribute</data> <data key="frontend_input">select</data> <data key="scope">global</data> <data key="is_required">false</data> @@ -73,4 +73,25 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> + <entity name="productAttributeWithDropdownTwoOptions" type="ProductAttribute"> + <data key="attribute_code">testattribute</data> + <data key="frontend_input">select</data> + <data key="scope">global</data> + <data key="is_required">false</data> + <data key="is_unique">false</data> + <data key="is_searchable">true</data> + <data key="is_visible">true</data> + <data key="is_visible_in_advanced_search">true</data> + <data key="is_visible_on_front">true</data> + <data key="is_filterable">true</data> + <data key="is_filterable_in_search">true</data> + <data key="used_in_product_listing">true</data> + <data key="is_used_for_promo_rules">true</data> + <data key="is_comparable">true</data> + <data key="is_used_in_grid">true</data> + <data key="is_visible_in_grid">true</data> + <data key="is_filterable_in_grid">true</data> + <data key="used_for_sort_by">true</data> + <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductWithEmptyAttributeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductWithEmptyAttributeTest.xml index c80025c299885..f858340bc09fa 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -17,29 +17,18 @@ <group value="product"/> </annotations> <before> - <!--<actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/>--> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> - <!--<createData entity="productAttributeWithTwoOptions" stepKey="createProductAttribute"/>--> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createProductAttribute"/> </before> <after> <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteSimpleProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> - <!--<deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/>--> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <amOnPage url="{{ProductAttributePage.url}}" stepKey="navigateToProductAttribute"/> - <waitForPageLoad stepKey="wait1"/> - <!--<click selector="#add" stepKey="addNewAttribute"/>--> - <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="testattribute" stepKey="fillAttributeLabel"/> - <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="Dropdown" stepKey="selectCatalogAttributeInputType"/> - <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> - <selectOption selector="#is_visible_on_front" userInput="Yes" stepKey="isVisibleOnStoreFront"/> - <selectOption selector="#used_in_product_listing" userInput="Yes" stepKey="usedInProductListing"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> - <waitForPageLoad stepKey="waitForSaveAttribute"/> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/> <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> @@ -54,8 +43,6 @@ <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.urlKey)}}" stepKey="goProductPageOnStorefront"/> <waitForPageLoad stepKey="waitForProductPageToLoad"/> - <!--<see selector="#product-attribute-specs-table tr th:nth-of-type(1)" stepKey="seeAttribute"/>--> - <!--<scrollTo selector=".additional-attributes tbody td:nth-of-type(1)" stepKey="seeAttribute1"/>--> <dontSeeElement selector="//table[@id='product-attribute-specs-table']/tbody/tr/th[contains(text(),'testattribute')]" stepKey="seeAttribute2"/> </test> </tests> From 040ab11d07238b4395987b6c88396b7dcb9968ee Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Fri, 18 May 2018 13:04:53 -0500 Subject: [PATCH 096/333] MAGETWO-91240: Automate MFTF - Added test to check Out Of Stock products --- .../FunctionalTest/Catalog/Data/ProductData.xml | 10 +++++----- .../DisplayOutOfStockProductActionGroup.xml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml index d65328a2fb626..5815dc0eeeed1 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml @@ -77,18 +77,18 @@ <data key="quantity">1000</data> <data key="urlKey" unique="suffix">simple</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="SimpleProduct4" type="product"> - <data key="sku" unique="suffix">SimpleProduct</data> + <data key="sku" unique="suffix">testSku</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name" unique="suffix">SimpleProduct</data> - <data key="price">123.00</data> <data key="visibility">4</data> + <data key="name" unique="suffix">OutOfStockProduct</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">testurlkey</data> <data key="status">1</data> <data key="quantity">0</data> - <data key="weight">1</data> - <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="NewSimpleProduct" type="product"> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml index 3e466427d661b..d990fcc2d935b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml @@ -16,7 +16,7 @@ <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="Yes" stepKey="switchToYes" /> <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> </actionGroup> - <actionGroup name="dontDisplayOutOfStockProduct"> + <actionGroup name="noDisplayOutOfStockProduct"> <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> <waitForPageLoad stepKey="waitForConfigPageToLoad"/> <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> From 1855062ceed5d8a1fb8822ab4fc8649d2d5e8080 Mon Sep 17 00:00:00 2001 From: Zbigniew Kuras <zbigniew.kuras@gate-software.com> Date: Fri, 18 May 2018 21:40:45 +0200 Subject: [PATCH 097/333] GraphQL - moved sort_fields to Products object. Some fixes --- .../Model/Resolver/Category/SortFields.php | 23 +++++++++------ .../CatalogGraphQl/etc/schema.graphqls | 11 ++++++++ app/code/Magento/GraphQl/etc/schema.graphqls | 11 -------- .../GraphQl/Catalog/ProductSearchTest.php | 28 +++++++++---------- 4 files changed, 40 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFields.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFields.php index 2b482f51df616..ca68b29910118 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFields.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/SortFields.php @@ -32,21 +32,28 @@ class SortFields implements ResolverInterface * @var \Magento\Store\Model\StoreManagerInterface */ private $storeManager; + + /** + * @var \Magento\Catalog\Model\Category\Attribute\Source\Sortby + */ + private $sortbyAttributeSource; /** * @param ValueFactory $valueFactory * @param \Magento\Catalog\Model\Config $catalogConfig * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @oaram \Magento\Catalog\Model\Category\Attribute\Source\Sortby $sortbyAttributeSource */ public function __construct( ValueFactory $valueFactory, \Magento\Catalog\Model\Config $catalogConfig, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Catalog\Model\Category\Attribute\Source\Sortby $ss + \Magento\Catalog\Model\Category\Attribute\Source\Sortby $sortbyAttributeSource ) { $this->valueFactory = $valueFactory; $this->catalogConfig = $catalogConfig; $this->storeManager = $storeManager; + $this->sortbyAttributeSource = $sortbyAttributeSource; } /** @@ -54,13 +61,13 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) : Value { - $sortFieldsOptions = [ - ['value' => 'position', 'label' => 'Position'] - ]; - foreach ($this->catalogConfig->getAttributesUsedForSortBy() as $attribute) { - $sortFieldsOptions[] = ['value' => $attribute->getAttributeCode(), 'label' => $attribute->getStoreLabel()]; - } - + $sortFieldsOptions = $this->sortbyAttributeSource->getAllOptions(); + array_walk( + $sortFieldsOptions, + function (&$option) { + $option['label'] = (string)$option['label']; + } + ); $data = [ 'default' => $this->catalogConfig->getProductListDefaultSortBy($this->storeManager->getStore()->getId()), 'options' => $sortFieldsOptions, diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index ca1ff78654319..9a9e114a06d17 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -402,6 +402,7 @@ type Products @doc(description: "The Products object is the top-level object ret page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query") total_count: Int @doc(description: "The number of products returned") filters: [LayerFilter] @doc(description: "Layered navigation filters array") + sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFields") } input ProductFilterInput @doc(description: "ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { @@ -521,3 +522,13 @@ interface LayerFilterItemInterface @typeResolver(class: "Magento\\CatalogGraphQl type LayerFilterItem implements LayerFilterItemInterface { } + +type SortField { + value: String @doc(description: "Attribute code of sort field") + label: String @doc(description: "Label of sort field") +} + +type SortFields @doc(description: "SortFields contains a default value for sort fields and all available sort fields") { + default: String @doc(description: "Default value of sort fields") + options: [SortField] @doc(description: "Available sort fields") +} diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index 5446e99c4c314..37ca2d8d7b378 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -25,20 +25,9 @@ input FilterTypeInput @doc(description: "FilterTypeInput specifies which action type SearchResultPageInfo @doc(description: "SearchResultPageInfo provides navigation for the query response") { page_size: Int @doc(description: "Specifies the maximum number of items to return") current_page: Int @doc(description: "Specifies which page of results to return") - sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFields") } enum SortEnum @doc(description: "This enumeration indicates whether to return results in ascending or descending order") { ASC DESC -} - -type SortField { - value: String @doc(description: "Attribute code of sort field") - label: String @doc(description: "Label of sort field") -} - -type SortFields @doc(description: "SortFields contains a default value for sort fields and all available sort fields") { - default: String @doc(description: "Default value of sort fields") - options: [SortField] @doc(description: "Available sort fields") } \ No newline at end of file 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 49371973f1001..65e044a5f005b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -511,14 +511,14 @@ public function testQueryProductsInCurrentPageSortedByPriceASC() { page_size current_page - sort_fields + } + sort_fields + { + default + options { - default - options - { - value - label - } + value + label } } } @@ -539,13 +539,13 @@ public function testQueryProductsInCurrentPageSortedByPriceASC() $this->assertProductItems($filteredChildProducts, $response); $this->assertEquals(4, $response['products']['page_info']['page_size']); $this->assertEquals(1, $response['products']['page_info']['current_page']); - $this->assertArrayHasKey('sort_fields', $response['products']['page_info']); - $this->assertArrayHasKey('options', $response['products']['page_info']['sort_fields']); - $this->assertArrayHasKey('default', $response['products']['page_info']['sort_fields']); - $this->assertEquals('position', $response['products']['page_info']['sort_fields']['default']); - $this->assertArrayHasKey('value', $response['products']['page_info']['sort_fields']['options'][0]); - $this->assertArrayHasKey('label', $response['products']['page_info']['sort_fields']['options'][0]); - $this->assertEquals(['value'=>'position', 'label' => 'Position'], $response['products']['page_info']['sort_fields']['options'][0]); + $this->assertArrayHasKey('sort_fields', $response['products']); + $this->assertArrayHasKey('options', $response['products']['sort_fields']); + $this->assertArrayHasKey('default', $response['products']['sort_fields']); + $this->assertEquals('position', $response['products']['sort_fields']['default']); + $this->assertArrayHasKey('value', $response['products']['sort_fields']['options'][0]); + $this->assertArrayHasKey('label', $response['products']['sort_fields']['options'][0]); + $this->assertEquals('position', $response['products']['sort_fields']['options'][0]['value']); } /** From dacf29473e55edee25e19fba8e82386f97dff1e5 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Fri, 18 May 2018 14:59:54 -0500 Subject: [PATCH 098/333] MAGETWO-91240: Automate MFTF --- .../Page/InventoryConfigurationPage.xml | 12 ++++++++++++ .../Section/InventorySection.xml | 0 2 files changed, 12 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Page/InventoryConfigurationPage.xml rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/{Config => CatalogInventory}/Section/InventorySection.xml (100%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Page/InventoryConfigurationPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Page/InventoryConfigurationPage.xml new file mode 100644 index 0000000000000..d138aadb144d8 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Page/InventoryConfigurationPage.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="InventoryConfigurationPage" url="admin/system_config/edit/section/cataloginventory/" area="admin" module="Magento_Config"> + <section name="InventorySection"/> + </page> +</pages> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/InventorySection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Section/InventorySection.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/InventorySection.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Section/InventorySection.xml From 58328f21e582d355e4dae56b4b76c8a8de3941c6 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 18 May 2018 15:13:37 -0500 Subject: [PATCH 099/333] MAGETWO-91440: Unassigned Attribute Values Render as N/A in Product Comparison block - fixing display issues when an attribute is empty for all products in the list to not show at all --- .../Block/Product/Compare/ListCompare.php | 19 +++ .../templates/product/compare/list.phtml | 108 +++++++++--------- 2 files changed, 74 insertions(+), 53 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php index c561913dee4d3..3b0b0831b4034 100644 --- a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php +++ b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php @@ -213,6 +213,25 @@ public function getProductAttributeValue($product, $attribute) return (string)$value == '' ? __('No') : $value; } + /** + * Check if any of the products has a value set for the attribute + * + * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute + * @return bool + */ + public function hasAttributeValueForProducts($attribute) + { + $index = 0; + foreach ($this->getItems() as $item) { + if ($index++ != 0) { + if ($item->hasData($attribute->getAttributeCode())) { + return true; + } + } + } + return false; + } + /** * Retrieve Print URL * 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 6a2dd1f27d4a9..fbc748bd615fa 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 @@ -8,8 +8,8 @@ /* @var $block \Magento\Catalog\Block\Product\Compare\ListCompare */ ?> -<?php $_total = $block->getItems()->getSize() ?> -<?php if ($_total): ?> +<?php $total = $block->getItems()->getSize() ?> +<?php if ($total): ?> <a href="#" class="action print" title="<?= /* @escapeNotVerified */ __('Print This Page') ?>"> <span><?= /* @escapeNotVerified */ __('Print This Page') ?></span> </a> @@ -24,14 +24,14 @@ <caption class="table-caption"><?= /* @escapeNotVerified */ __('Compare Products') ?></caption> <thead> <tr> - <?php $_i = 0 ?> - <?php foreach ($block->getItems() as $_item): ?> - <?php if ($_i++ == 0): ?> + <?php $index = 0 ?> + <?php foreach ($block->getItems() as $item): ?> + <?php if ($index++ == 0): ?> <th scope="row" class="cell label remove"><span><?= /* @escapeNotVerified */ __('Remove Product') ?></span></th> <?php endif; ?> <td class="cell remove product"> <?php $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare');?> - <a href="#" data-post='<?= /* @escapeNotVerified */ $compareHelper->getPostDataRemove($_item) ?>' + <a href="#" data-post='<?= /* @escapeNotVerified */ $compareHelper->getPostDataRemove($item) ?>' class="action delete" title="<?= /* @escapeNotVerified */ __('Remove Product') ?>"> <span><?= /* @escapeNotVerified */ __('Remove Product') ?></span> </a> @@ -41,35 +41,35 @@ </thead> <tbody> <tr> - <?php $_i = 0; ?> - <?php $_helper = $this->helper('Magento\Catalog\Helper\Output'); ?> - <?php /** @var $_item \Magento\Catalog\Model\Product */ ?> - <?php foreach ($block->getItems() as $_item): ?> - <?php if ($_i++ == 0): ?> + <?php $index = 0; ?> + <?php $helper = $this->helper('Magento\Catalog\Helper\Output'); ?> + <?php /** @var $item \Magento\Catalog\Model\Product */ ?> + <?php foreach ($block->getItems() as $item): ?> + <?php if ($index++ == 0): ?> <th scope="row" class="cell label product"><span><?= /* @escapeNotVerified */ __('Product') ?></span></th> <?php endif; ?> <td data-th="<?= $block->escapeHtml(__('Product')) ?>" class="cell product info"> - <a class="product-item-photo" href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>" title="<?= /* @escapeNotVerified */ $block->stripTags($_item->getName(), null, true) ?>"> - <?= $block->getImage($_item, 'product_comparison_list')->toHtml() ?> + <a class="product-item-photo" href="<?= /* @escapeNotVerified */ $block->getProductUrl($item) ?>" title="<?= /* @escapeNotVerified */ $block->stripTags($item->getName(), null, true) ?>"> + <?= $block->getImage($item, 'product_comparison_list')->toHtml() ?> </a> <strong class="product-item-name"> - <a href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>" title="<?= /* @escapeNotVerified */ $block->stripTags($_item->getName(), null, true) ?>"> - <?= /* @escapeNotVerified */ $_helper->productAttribute($_item, $_item->getName(), 'name') ?> + <a href="<?= /* @escapeNotVerified */ $block->getProductUrl($item) ?>" title="<?= /* @escapeNotVerified */ $block->stripTags($item->getName(), null, true) ?>"> + <?= /* @escapeNotVerified */ $helper->productAttribute($item, $item->getName(), 'name') ?> </a> </strong> - <?= $block->getReviewsSummaryHtml($_item, 'short') ?> - <?= /* @escapeNotVerified */ $block->getProductPrice($_item, '-compare-list-top') ?> + <?= $block->getReviewsSummaryHtml($item, 'short') ?> + <?= /* @escapeNotVerified */ $block->getProductPrice($item, '-compare-list-top') ?> <div class="product-item-actions"> <div class="actions-primary"> - <?php if ($_item->isSaleable()): ?> - <form data-role="tocart-form" action="<?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Product\Compare')->getAddToCartUrl($_item) ?>" method="post"> + <?php if ($item->isSaleable()): ?> + <form data-role="tocart-form" action="<?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Product\Compare')->getAddToCartUrl($item) ?>" method="post"> <?= $block->getBlockHtml('formkey') ?> <button type="submit" class="action tocart primary"> <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> </button> </form> <?php else: ?> - <?php if ($_item->getIsSalable()): ?> + <?php if ($item->getIsSalable()): ?> <div class="stock available"><span><?= /* @escapeNotVerified */ __('In stock') ?></span></div> <?php else: ?> <div class="stock unavailable"><span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></div> @@ -78,7 +78,7 @@ </div> <?php if ($this->helper('Magento\Wishlist\Helper\Data')->isAllow()) : ?> <div class="secondary-addto-links actions-secondary" data-role="add-to-links"> - <a href="#" data-post='<?= /* @escapeNotVerified */ $block->getAddToWishlistParams($_item) ?>' class="action towishlist" data-action="add-to-wishlist"> + <a href="#" data-post='<?= /* @escapeNotVerified */ $block->getAddToWishlistParams($item) ?>' class="action towishlist" data-action="add-to-wishlist"> <span><?= /* @escapeNotVerified */ __('Add to Wish List') ?></span> </a> </div> @@ -89,39 +89,41 @@ </tr> </tbody> <tbody> - <?php foreach ($block->getAttributes() as $_attribute): ?> - <tr> - <?php $_i = 0 ?> - <?php foreach ($block->getItems() as $_item): ?> - <?php if ($_i++ == 0): ?> - <th scope="row" class="cell label"> - <span class="attribute label"> - <?= $block->escapeHtml($_attribute->getStoreLabel() ? $_attribute->getStoreLabel() : __($_attribute->getFrontendLabel())) ?> - </span> - </th> - <?php endif; ?> - <td class="cell product attribute"> - <div class="attribute value"> - <?php switch ($_attribute->getAttributeCode()) { - case "price": ?> - <?php - /* @escapeNotVerified */ echo $block->getProductPrice( - $_item, - '-compare-list-' . $_attribute->getCode() - ) - ?> - <?php break; - case "small_image": ?> - <?php $block->getImage($_item, 'product_small_image')->toHtml(); ?> + <?php foreach ($block->getAttributes() as $attribute): ?> + <?php $index = 0; ?> + <?php if ($block->hasAttributeValueForProducts($attribute)): ?> + <tr> + <?php foreach ($block->getItems() as $item): ?> + <?php if ($index++ == 0): ?> + <th scope="row" class="cell label"> + <span class="attribute label"> + <?= $block->escapeHtml($attribute->getStoreLabel() ? $attribute->getStoreLabel() : __($attribute->getFrontendLabel())) ?> + </span> + </th> + <?php endif; ?> + <td class="cell product attribute"> + <div class="attribute value"> + <?php switch ($attribute->getAttributeCode()) { + case "price": ?> + <?php + /* @escapeNotVerified */ echo $block->getProductPrice( + $item, + '-compare-list-' . $attribute->getCode() + ) + ?> + <?php break; + case "small_image": ?> + <?php $block->getImage($item, 'product_small_image')->toHtml(); ?> + <?php break; + default: ?> + <?= /* @escapeNotVerified */ $index . '-'.$helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode()) ?> <?php break; - default: ?> - <?= /* @escapeNotVerified */ $_helper->productAttribute($_item, $block->getProductAttributeValue($_item, $_attribute), $_attribute->getAttributeCode()) ?> - <?php break; - } ?> - </div> - </td> - <?php endforeach; ?> - </tr> + } ?> + </div> + </td> + <?php endforeach; ?> + </tr> + <?php endif; ?> <?php endforeach; ?> </tbody> </table> From c783e1eab4268c169897c7d181e0e42e24418024 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Fri, 18 May 2018 16:04:17 -0500 Subject: [PATCH 100/333] MAGETWO-91240: Automate MFTF --- .../Magento/FunctionalTest/Config/Page/AdminConfigPage.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Page/AdminConfigPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Page/AdminConfigPage.xml index cf1fe307c4c58..9ae1ecab1c26e 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Page/AdminConfigPage.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Page/AdminConfigPage.xml @@ -12,7 +12,4 @@ <page name="AdminContentManagementPage" url="admin/system_config/edit/section/cms/" area="admin" module="Magento_Config"> <section name="ContentManagementSection"/> </page> - <page name="InventoryConfigurationPage" url="admin/system_config/edit/section/cataloginventory/" area="admin" module="Magento_Config"> - <section name="InventorySection"/> - </page> </pages> From 31eaf6471c8c720b4a01867caf148c81105e3c6c Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Fri, 18 May 2018 16:16:36 -0500 Subject: [PATCH 101/333] MAGETWO-91240: Automate MFTF --- .../ActionGroup/DisplayOutOfStockProductActionGroup.xml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dev/tests/acceptance/tests/functional/Magento/FunctionalTest/{Config => CatalogInventory}/ActionGroup/DisplayOutOfStockProductActionGroup.xml (100%) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/ActionGroup/DisplayOutOfStockProductActionGroup.xml similarity index 100% rename from dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/DisplayOutOfStockProductActionGroup.xml rename to dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/ActionGroup/DisplayOutOfStockProductActionGroup.xml From cad44a3db14797f7fd2978e1c2a252e785fc0ec1 Mon Sep 17 00:00:00 2001 From: Saravanan <saravananv14@live.com> Date: Sat, 19 May 2018 15:26:18 +0530 Subject: [PATCH 102/333] 15210-Fixed product tier pricing pagination issue in admin --- .../js/components/dynamic-rows-tier-price.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js index b5c0e7a95d401..bafb8672e5e80 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js @@ -28,6 +28,32 @@ define([ }); this.labels(labels); + }, + + /** + * Change page + * + * @param {Number} page - current page + */ + changePage: function (page) { + this.clear(); /* Clear the children when directly edit the text field */ + if (page === 1 && !this.recordData().length) { + return false; + } + + if (~~page > this.pages()) { + this.currentPage(this.pages()); + + return false; + } else if (~~page < 1) { + this.currentPage(1); + + return false; + } + + this.initChildren(); + + return true; } }); }); From 143022ff15824a14687468c752aa52949d67179b Mon Sep 17 00:00:00 2001 From: vitaliyboyko <vitaliyboyko@i.ua> Date: Sat, 19 May 2018 13:38:31 +0300 Subject: [PATCH 103/333] Fixed typo in MagentoUi abstract.js --- app/code/Magento/Ui/view/base/web/js/form/element/abstract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index b6979121a1891..a6fe0159fc785 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -220,7 +220,7 @@ define([ }, /** - * Sets 'value' as 'hidden' propertie's value, triggers 'toggle' event, + * Sets 'value' as 'hidden' properties value, triggers 'toggle' event, * sets instance's hidden identifier in params storage based on * 'value'. * From df6c7b47f6366a70a34cc0e3cff0f0224fd5a246 Mon Sep 17 00:00:00 2001 From: David Manners <dmanners87@gmail.com> Date: Sat, 19 May 2018 15:48:39 +0000 Subject: [PATCH 104/333] ADHOC: update the contribution templates to include a bit of manners and clarification. --- .github/ISSUE_TEMPLATE.md | 27 +++++++++++++++++++-------- .github/PULL_REQUEST_TEMPLATE.md | 25 +++++++++++++++++++++---- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 3ac68076d4353..f6c8b354f42cc 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,24 +1,35 @@ -<!--- Provide a general summary of the issue in the Title above --> -<!--- Before adding new issues, please, check this article https://github.com/magento/magento2/wiki/Issue-reporting-guidelines--> +<!--- + Thank you for contributing to Magento. + To help us process this issue we recommend that you add the following information: + - Summary of the issue, + - Information on your environment, + - Steps to reproduce, + - Expected and actual results, + + Please also have a look at our guidelines article before adding a new issue https://github.com/magento/magento2/wiki/Issue-reporting-guidelines +--> ### Preconditions -<!--- Provide a more detailed information of environment you use --> -<!--- Magento version, tag, HEAD, etc., PHP & MySQL version, etc.. --> +<!--- + Please provide as detailed information about your environment as possible. + For example Magento version, tag, HEAD, PHP & MySQL version, etc.. +--> 1. 2. ### Steps to reproduce -<!--- Provide a set of unambiguous steps to reproduce this bug include code, if relevant --> +<!--- + It is important to provide a set of clear steps to reproduce this bug. + If relevant please include code samples +--> 1. 2. 3. ### Expected result <!--- Tell us what should happen --> -1. +1. [Screenshot, logs] ### Actual result <!--- Tell us what happens instead --> 1. [Screenshot, logs] - -<!--- (This may be platform independent comment) --> diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d1f01ba9f2640..5b0b9d74e453b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,32 @@ -<!--- Provide a general summary of the Pull Request in the Title above --> +<!--- + Thank you for contributing to Magento. + To help us process this pull request we recommend that you add the following information: + - Summary of the pull request, + - Issue(s) related to the changes made, + - Manual testing scenarios, +--> + +<!--- Please provide a general summary of the Pull Request in the Title above --> ### Description -<!--- Provide a description of the changes proposed in the pull request --> +<!--- + Please provide a description of the changes proposed in the pull request. + Letting us know what has changed and why it needed changing will help us validate this pull request. +--> ### Fixed Issues (if relevant) -<!--- Provide a list of fixed issues in the format magento/magento2#<issue_number>, if relevant --> +<!--- + If relevant, please provide a list of fixed issues in the format magento/magento2#<issue_number>. + There could be 1 or more issues linked here and it will help us find some more information about the reasoning behind this change. +--> 1. magento/magento2#<issue_number>: Issue title 2. ... ### Manual testing scenarios -<!--- Provide a set of unambiguous steps to test the proposed code change --> +<!--- + Please provide a set of unambiguous steps to test the proposed code change. + Giving us manual testing scenarios will help with the processing and validation process. +--> 1. ... 2. ... From 560aa38603ac0cd7762ff7d791c598f734eb5435 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Sun, 20 May 2018 06:31:23 +0530 Subject: [PATCH 105/333] Fixed coding related issue --- .../view/adminhtml/web/js/components/dynamic-rows-tier-price.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js index bafb8672e5e80..430b3882d29db 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js @@ -37,6 +37,7 @@ define([ */ changePage: function (page) { this.clear(); /* Clear the children when directly edit the text field */ + if (page === 1 && !this.recordData().length) { return false; } From c9215e5d1c58430fa61de79dbdcdcc824d52d4d3 Mon Sep 17 00:00:00 2001 From: Igor Vitol <igorvitold@gmail.com> Date: Tue, 8 May 2018 14:01:47 +0300 Subject: [PATCH 106/333] Add 'const' type support to layout arguments --- app/code/Magento/Ui/etc/di.xml | 1 + .../Magento/Framework/View/Layout/etc/layout_merged.xsd | 6 ++++++ .../Framework/View/Layout/etc/page_configuration.xsd | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/app/code/Magento/Ui/etc/di.xml b/app/code/Magento/Ui/etc/di.xml index 8cf0e1e12e781..c029e18addf73 100644 --- a/app/code/Magento/Ui/etc/di.xml +++ b/app/code/Magento/Ui/etc/di.xml @@ -252,6 +252,7 @@ <type name="Magento\Framework\Data\Argument\Interpreter\Composite"> <arguments> <argument name="interpreters" xsi:type="array"> + <item name="const" xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Constant</item> <item name="object" xsi:type="object">configurableObjectArgumentInterpreterProxy</item> <item name="configurableObject" xsi:type="object">configurableObjectArgumentInterpreterProxy</item> <item name="array" xsi:type="object">arrayArgumentInterpreterProxy</item> diff --git a/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd b/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd index f830f41b53255..e50b8a9cf2808 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/layout_merged.xsd @@ -62,4 +62,10 @@ </xs:extension> </xs:complexContent> </xs:complexType> + + <xs:complexType name="const"> + <xs:complexContent> + <xs:extension base="argumentType" /> + </xs:complexContent> + </xs:complexType> </xs:schema> diff --git a/lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd b/lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd index 397b71fe70825..09ad246b3a64d 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd @@ -32,4 +32,10 @@ <xs:field xpath="@name"/> </xs:unique> </xs:element> + + <xs:complexType name="const"> + <xs:complexContent> + <xs:extension base="argumentType" /> + </xs:complexContent> + </xs:complexType> </xs:schema> From 6c04a04434fc7fc90dadd0fd1f687508d33cb48d Mon Sep 17 00:00:00 2001 From: austris argalis <austris.argalis@gmail.com> Date: Sun, 20 May 2018 20:30:22 +0300 Subject: [PATCH 107/333] Correct failing test, use urlModel no more --- .../testsuite/Magento/GraphQl/Catalog/ProductViewTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 54d363ea6c1e6..aaf22c8b373fe 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -273,8 +273,9 @@ public function testQueryAllFieldsSimpleProduct() 'Filter category', $responseObject->getData('products/items/0/categories/2/name') ); + $storeManager = ObjectManager::getInstance()->get(\Magento\Store\Model\StoreManagerInterface::class); self::assertEquals( - $product->getUrlModel()->getUrl($product, ['_ignore_category' => true, '_nosid' => true]), + $storeManager->getStore()->getBaseUrl() . 'simple-product.html', $responseObject->getData('products/items/0/canonical_url') ); } From b1abc209be07ff7c983bb822cf0147198a133078 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 20 May 2018 23:00:40 +0300 Subject: [PATCH 108/333] [Forwardport] Removed non-existing argument --- app/code/Magento/Translation/Block/Html/Head/Config.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Translation/Block/Html/Head/Config.php b/app/code/Magento/Translation/Block/Html/Head/Config.php index 20a67409212e1..422564abfbc7b 100644 --- a/app/code/Magento/Translation/Block/Html/Head/Config.php +++ b/app/code/Magento/Translation/Block/Html/Head/Config.php @@ -34,7 +34,6 @@ class Config extends \Magento\Framework\View\Element\AbstractBlock /** * @param \Magento\Framework\View\Element\Context $context - * @param RequireJsConfig $config * @param \Magento\Framework\View\Page\Config $pageConfig * @param \Magento\Translation\Model\FileManager $fileManager * @param Inline $inline From 05456c7af60086aa9263d18d7591ee77cd6758b1 Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Fri, 18 May 2018 14:30:35 +0530 Subject: [PATCH 109/333] Fixed typo mistake in function comment --- app/code/Magento/Theme/Model/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/Model/Config.php b/app/code/Magento/Theme/Model/Config.php index 57efe43db3d7c..bbf9677afdcb2 100644 --- a/app/code/Magento/Theme/Model/Config.php +++ b/app/code/Magento/Theme/Model/Config.php @@ -124,7 +124,7 @@ protected function _getAssignedScopesCollection($scope, $configPath) } /** - * Unassign given theme from stores that were unchecked + * Unassigned given theme from stores that were unchecked * * @param string $themeId * @param array $stores From 39c83647d3f3e1ceb111c330e1e8bbfffb6e4d7b Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Fri, 18 May 2018 16:21:11 +0530 Subject: [PATCH 110/333] Removed extra added space in function comment and fixed typo mistakes. --- app/code/Magento/Paypal/Model/Api/Nvp.php | 6 +++--- .../blank/Magento_ProductVideo/web/css/source/_module.less | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Api/Nvp.php b/app/code/Magento/Paypal/Model/Api/Nvp.php index 6933c613ef748..25883590350f4 100644 --- a/app/code/Magento/Paypal/Model/Api/Nvp.php +++ b/app/code/Magento/Paypal/Model/Api/Nvp.php @@ -1025,7 +1025,7 @@ public function callGetPalDetails() } /** - * Set Customer BillingA greement call + * Set Customer BillingAgreement call * * @return void * @link https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_SetCustomerBillingAgreement @@ -1425,7 +1425,7 @@ protected function _deformatNVP($nvpstr) $nvpstr = strpos($nvpstr, "\r\n\r\n") !== false ? substr($nvpstr, strpos($nvpstr, "\r\n\r\n") + 4) : $nvpstr; while (strlen($nvpstr)) { - //postion of Key + //position of Key $keypos = strpos($nvpstr, '='); //position of value $valuepos = strpos($nvpstr, '&') ? strpos($nvpstr, '&') : strlen($nvpstr); @@ -1433,7 +1433,7 @@ protected function _deformatNVP($nvpstr) /*getting the Key and Value values and storing in a Associative Array*/ $keyval = substr($nvpstr, $intial, $keypos); $valval = substr($nvpstr, $keypos + 1, $valuepos - $keypos - 1); - //decoding the respose + //decoding the response $nvpArray[urldecode($keyval)] = urldecode($valval); $nvpstr = substr($nvpstr, $valuepos + 1, strlen($nvpstr)); } diff --git a/app/design/frontend/Magento/blank/Magento_ProductVideo/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_ProductVideo/web/css/source/_module.less index 023c531545963..3dce80ff4d605 100644 --- a/app/design/frontend/Magento/blank/Magento_ProductVideo/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_ProductVideo/web/css/source/_module.less @@ -97,8 +97,8 @@ // @TODO UI: check possibility to use .media-width() mixin @media only screen -and (min-device-width: 320px) -and (max-device-width: 780px) +and (min-device-width: @screen__xxs) +and (max-device-width: @screen__m) and (orientation: landscape) { .product-video { height: 100%; From 7f29a8fc98fb4c40568612aca7ba6a4b1761eeb1 Mon Sep 17 00:00:00 2001 From: Namrata <37930646+NamrataChangani@users.noreply.github.com> Date: Fri, 18 May 2018 16:34:59 +0530 Subject: [PATCH 111/333] Revert back file changes --- .../blank/Magento_ProductVideo/web/css/source/_module.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_ProductVideo/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_ProductVideo/web/css/source/_module.less index 3dce80ff4d605..023c531545963 100644 --- a/app/design/frontend/Magento/blank/Magento_ProductVideo/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_ProductVideo/web/css/source/_module.less @@ -97,8 +97,8 @@ // @TODO UI: check possibility to use .media-width() mixin @media only screen -and (min-device-width: @screen__xxs) -and (max-device-width: @screen__m) +and (min-device-width: 320px) +and (max-device-width: 780px) and (orientation: landscape) { .product-video { height: 100%; From 24f6d2ef420dcd814ee1a9268d077ec495cceb2e Mon Sep 17 00:00:00 2001 From: NamrataChangani <namratavora301@gmail.com> Date: Fri, 18 May 2018 17:26:18 +0530 Subject: [PATCH 112/333] Revert back file changes. --- app/code/Magento/Theme/Model/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/Model/Config.php b/app/code/Magento/Theme/Model/Config.php index bbf9677afdcb2..57efe43db3d7c 100644 --- a/app/code/Magento/Theme/Model/Config.php +++ b/app/code/Magento/Theme/Model/Config.php @@ -124,7 +124,7 @@ protected function _getAssignedScopesCollection($scope, $configPath) } /** - * Unassigned given theme from stores that were unchecked + * Unassign given theme from stores that were unchecked * * @param string $themeId * @param array $stores From 44e3fab7af4c7a858b797fe75a12413d6195460d Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Mon, 21 May 2018 09:30:46 +0300 Subject: [PATCH 113/333] MAGETWO-91934: Unlock Locales Editing when SCD on Demand Mode is Enabled --- .../ConcealInProductionConfigList.php | 7 +- .../ElementVisibility/ConcealInProduction.php | 134 ++++++++++++ .../ElementVisibility/ConcealScdField.php | 149 ++++++++++++++ .../ConcealInProductionConfigListTest.php | 13 ++ .../ConcealInProductionTest.php | 110 ++++++++++ .../ElementVisibility/ConcealScdFieldTest.php | 163 +++++++++++++++ .../Framework/Locale/Deployed/Options.php | 16 +- .../Locale/Test/Unit/Deployed/OptionsTest.php | 190 +++++++++++++----- 8 files changed, 727 insertions(+), 55 deletions(-) create mode 100755 app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php create mode 100755 app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php diff --git a/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php b/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php index 92bc61b3d65e5..367b0837b8e19 100644 --- a/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php +++ b/app/code/Magento/Config/Model/Config/Structure/ConcealInProductionConfigList.php @@ -11,7 +11,8 @@ * Defines status of visibility of form elements on Stores > Settings > Configuration page * in Admin Panel in Production mode. * @api - * @since 100.2.0 + * @deprecated class location was changed + * @see \Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction */ class ConcealInProductionConfigList implements ElementVisibilityInterface { @@ -76,7 +77,7 @@ public function __construct(State $state, array $configs = [], array $exemptions /** * @inheritdoc - * @since 100.2.0 + * @deprecated */ public function isHidden($path) { @@ -99,7 +100,7 @@ public function isHidden($path) /** * @inheritdoc - * @since 100.2.0 + * @deprecated */ public function isDisabled($path) { diff --git a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php new file mode 100755 index 0000000000000..4879dec385407 --- /dev/null +++ b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Config\Model\Config\Structure\ElementVisibility; + +use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; +use Magento\Framework\App\State; + +/** + * Defines status of visibility of form elements on Stores > Settings > Configuration page + * in Admin Panel in Production mode. + * @api + */ +class ConcealInProduction implements ElementVisibilityInterface +{ + /** + * The list of form element paths with concrete visibility status. + * + * E.g. + * + * ```php + * [ + * 'general/locale/code' => ElementVisibilityInterface::DISABLED, + * 'general/country' => ElementVisibilityInterface::HIDDEN, + * ]; + * ``` + * + * It means that: + * - field Locale (in group Locale Options in section General) will be disabled + * - group Country Options (in section General) will be hidden + * + * @var array + */ + private $configs = []; + + /** + * The object that has information about the state of the system. + * + * @var State + */ + private $state; + + /** + * + * The list of form element paths which ignore visibility status. + * + * E.g. + * + * ```php + * [ + * 'general/country/default' => '', + * ]; + * ``` + * + * It means that: + * - field 'default' in group Country Options (in section General) will be showed, even if all group(section) + * will be hidden. + * + * @var array + */ + private $exemptions = []; + + /** + * @param State $state The object that has information about the state of the system + * @param array $configs The list of form element paths with concrete visibility status. + * @param array $exemptions The list of form element paths which ignore visibility status. + */ + public function __construct(State $state, array $configs = [], array $exemptions = []) + { + $this->state = $state; + $this->configs = $configs; + $this->exemptions = $exemptions; + } + + /** + * @inheritdoc + * @since 100.2.0 + */ + public function isHidden($path) + { + $result = false; + $path = $this->normalizePath($path); + if ($this->state->getMode() === State::MODE_PRODUCTION + && preg_match('/(?<group>(?<section>.*?)\/.*?)\/.*?/', $path, $match)) { + $group = $match['group']; + $section = $match['section']; + $exemptions = array_keys($this->exemptions); + foreach ($this->configs as $configPath => $value) { + if ($value === static::HIDDEN && strpos($path, $configPath) !==false) { + $result = empty(array_intersect([$section, $group, $path], $exemptions)); + } + } + } + + return $result; + } + + /** + * @inheritdoc + * @since 100.2.0 + */ + public function isDisabled($path) + { + $path = $this->normalizePath($path); + if ($this->state->getMode() === State::MODE_PRODUCTION) { + while (true) { + if (!empty($this->configs[$path])) { + return $this->configs[$path] === static::DISABLED; + } + + $position = strripos($path, '/'); + if ($position === false) { + break; + } + $path = substr($path, 0, $position); + } + } + + return false; + } + + /** + * Returns normalized path. + * + * @param string $path The path to be normalized + * @return string The normalized path + */ + private function normalizePath($path) + { + return trim($path, '/'); + } +} diff --git a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php new file mode 100755 index 0000000000000..1335921520c38 --- /dev/null +++ b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php @@ -0,0 +1,149 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Config\Model\Config\Structure\ElementVisibility; + +use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\State; +use Magento\Framework\Config\ConfigOptionsListConstants as Constants; + +/** + * Defines status of visibility of form elements on Stores > Settings > Configuration page + * in Admin Panel in Production mode. + * @api + */ +class ConcealScdField implements ElementVisibilityInterface +{ + /** + * The list of form element paths with concrete visibility status. + * + * E.g. + * + * ```php + * [ + * 'general/locale/code' => ElementVisibilityInterface::DISABLED, + * 'general/country' => ElementVisibilityInterface::HIDDEN, + * ]; + * ``` + * + * It means that: + * - field Locale (in group Locale Options in section General) will be disabled + * - group Country Options (in section General) will be hidden + * + * @var array + */ + private $configs = []; + + /** + * The object that has information about the state of the system. + * + * @var State + */ + private $state; + + /** + * + * The list of form element paths which ignore visibility status. + * + * E.g. + * + * ```php + * [ + * 'general/country/default' => '', + * ]; + * ``` + * + * It means that: + * - field 'default' in group Country Options (in section General) will be showed, even if all group(section) + * will be hidden. + * + * @var array + */ + private $exemptions = []; + + /** + * @var DeploymentConfig + */ + private $deploymentConfig; + + /** + * @param State $state The object that has information about the state of the system + * @param DeploymentConfig $deploymentConfig Deployment configuration reader + * @param array $configs The list of form element paths with concrete visibility status. + * @param array $exemptions The list of form element paths which ignore visibility status. + */ + public function __construct( + State $state, + DeploymentConfig $deploymentConfig, + array $configs = [], + array $exemptions = [] + ) { + $this->state = $state; + $this->deploymentConfig = $deploymentConfig; + $this->configs = $configs; + $this->exemptions = $exemptions; + } + + /** + * @inheritdoc + */ + public function isHidden($path) + { + $path = $this->normalizePath($path); + if ($this->state->getMode() === State::MODE_PRODUCTION + && !$this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) + && preg_match('/(?<group>(?<section>.*?)\/.*?)\/.*?/', $path, $match)) { + $group = $match['group']; + $section = $match['section']; + $exemptions = array_keys($this->exemptions); + $checkedItems = []; + foreach ([$path, $group, $section] as $itemPath) { + $checkedItems[] = $itemPath; + if (!empty($this->configs[$itemPath])) { + return $this->configs[$itemPath] === static::HIDDEN + && empty(array_intersect($checkedItems, $exemptions)); + } + } + } + + return false; + } + + /** + * @inheritdoc + */ + public function isDisabled($path) + { + $path = $this->normalizePath($path); + if ($this->state->getMode() === State::MODE_PRODUCTION + && !$this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)) { + while ($path) { + if (!empty($this->configs[$path])) { + return $this->configs[$path] === static::DISABLED; + } + + $position = strripos($path, '/'); + if ($position === false) { + break; + } + $path = $position !== false ? substr($path, 0, $position) : null; + } + } + + return false; + } + + /** + * Returns normalized path. + * + * @param string $path The path to be normalized + * @return string The normalized path + */ + private function normalizePath($path) + { + return trim($path, '/'); + } +} diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php index fa78d5dde652c..bfb04bb413d92 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ConcealInProductionConfigListTest.php @@ -8,6 +8,11 @@ use Magento\Config\Model\Config\Structure\ConcealInProductionConfigList; use Magento\Framework\App\State; +/** + * @deprecated Original class has changed the location + * @see \Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction + * @see \Magento\Config\Test\Unit\Model\Config\Structure\ElementVisibility\ConcealInProductionTest + */ class ConcealInProductionConfigListTest extends \PHPUnit\Framework\TestCase { /** @@ -47,6 +52,8 @@ protected function setUp() * @param string $mageMode * @param bool $expectedResult * @dataProvider disabledDataProvider + * + * @deprecated */ public function testIsDisabled($path, $mageMode, $expectedResult) { @@ -58,6 +65,8 @@ public function testIsDisabled($path, $mageMode, $expectedResult) /** * @return array + * + * @deprecated */ public function disabledDataProvider() { @@ -82,6 +91,8 @@ public function disabledDataProvider() * @param string $mageMode * @param bool $expectedResult * @dataProvider hiddenDataProvider + * + * @deprecated */ public function testIsHidden($path, $mageMode, $expectedResult) { @@ -93,6 +104,8 @@ public function testIsHidden($path, $mageMode, $expectedResult) /** * @return array + * + * @deprecated */ public function hiddenDataProvider() { diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php new file mode 100644 index 0000000000000..e3232f3efd2f8 --- /dev/null +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Config\Test\Unit\Model\Config\Structure\ElementVisibility; + +use Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction; +use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; +use Magento\Framework\App\State; + +class ConcealInProductionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var State|\PHPUnit_Framework_MockObject_MockObject + */ + private $stateMock; + + /** + * @var ConcealInProduction + */ + private $model; + + protected function setUp() + { + $this->stateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->getMock(); + + $configs = [ + 'first/path' => ElementVisibilityInterface::DISABLED, + 'second/path' => ElementVisibilityInterface::HIDDEN, + 'third' => ElementVisibilityInterface::DISABLED, + 'third/path' => 'no', + 'third/path/field' => ElementVisibilityInterface::DISABLED, + 'first/path/field' => 'no', + 'fourth' => ElementVisibilityInterface::HIDDEN, + ]; + $exemptions = [ + 'fourth/path/value' => '', + ]; + + $this->model = new ConcealInProduction($this->stateMock, $configs, $exemptions); + } + + /** + * @param string $path + * @param string $mageMode + * @param bool $expectedResult + * @dataProvider disabledDataProvider + */ + public function testIsDisabled($path, $mageMode, $expectedResult) + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn($mageMode); + $this->assertSame($expectedResult, $this->model->isDisabled($path)); + } + + /** + * @return array + */ + public function disabledDataProvider() + { + return [ + ['first/path', State::MODE_PRODUCTION, true], + ['first/path/field', State::MODE_PRODUCTION, false], + ['first/path/field2', State::MODE_PRODUCTION, true], + ['first/path', State::MODE_DEFAULT, false], + ['some/path', State::MODE_PRODUCTION, false], + ['second/path', State::MODE_PRODUCTION, false], + ['third', State::MODE_PRODUCTION, true], + ['third/path2', State::MODE_PRODUCTION, true], + ['third/path2/field', State::MODE_PRODUCTION, true], + ['third/path', State::MODE_PRODUCTION, false], + ['third/path/field', State::MODE_PRODUCTION, true], + ['third/path/field2', State::MODE_PRODUCTION, false], + ]; + } + + /** + * @param string $path + * @param string $mageMode + * @param bool $expectedResult + * @dataProvider hiddenDataProvider + */ + public function testIsHidden($path, $mageMode, $expectedResult) + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn($mageMode); + $this->assertSame($expectedResult, $this->model->isHidden($path)); + } + + /** + * @return array + */ + public function hiddenDataProvider() + { + return [ + ['first/path', State::MODE_PRODUCTION, false], + ['first/path', State::MODE_DEFAULT, false], + ['some/path', State::MODE_PRODUCTION, false], + ['second/path/field', State::MODE_PRODUCTION, true], + ['second/path', State::MODE_DEVELOPER, false], + ['fourth/path/value', State::MODE_PRODUCTION, false], + ['fourth/path/test', State::MODE_PRODUCTION, true], + ]; + } +} diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php new file mode 100644 index 0000000000000..3ffa762eeed8e --- /dev/null +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Config\Test\Unit\Model\Config\Structure\ElementVisibility; + +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\State; +use Magento\Framework\Config\ConfigOptionsListConstants as Constants; +use Magento\Config\Model\Config\Structure\ElementVisibility\ConcealScdField; +use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; + +class ConcealScdFieldTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var State|\PHPUnit_Framework_MockObject_MockObject + */ + private $stateMock; + + /** + * @var ConcealScdField + */ + private $model; + + /** + * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $deploymentConfigMock; + + protected function setUp() + { + $this->stateMock = $this->createMock(State::class); + + $this->deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); + + $configs = [ + 'section1/group1/field1' => ElementVisibilityInterface::DISABLED, + 'section1/group1' => ElementVisibilityInterface::HIDDEN, + 'section1' => ElementVisibilityInterface::DISABLED, + 'section1/group2' => 'no', + 'section2/group1' => ElementVisibilityInterface::DISABLED, + 'section2/group2' => ElementVisibilityInterface::HIDDEN, + 'section3' => ElementVisibilityInterface::HIDDEN, + 'section3/group1/field1' => 'no', + 'section5' => 'no', + ]; + $exemptions = [ + 'section1/group1/field3' => '', + 'section1/group2/field1' => '', + 'section2/group2/field1' => '', + 'section3/group2' => '', + ]; + + $this->model = new ConcealScdField($this->stateMock, $this->deploymentConfigMock, $configs, $exemptions); + } + + /** + * @param string $path + * @param string $mageMode + * @param int $scdOnDemand + * @param bool $isDisabled + * @param bool $isHidden + * @dataProvider disabledDataProvider + */ + public function testCheckVisibility($path, $mageMode, $scdOnDemand, $isHidden, $isDisabled) + { + $this->stateMock->expects($this->any()) + ->method('getMode') + ->willReturn($mageMode); + $this->deploymentConfigMock->expects($this->any()) + ->method('getConfigData') + ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) + ->willReturn($scdOnDemand); + + $this->assertSame($isHidden, $this->model->isHidden($path)); + $this->assertSame($isDisabled, $this->model->isDisabled($path)); + } + + /** + * @return array + */ + public function disabledDataProvider() + { + return [ + //visibility of field 'section1/group1/field1' should be applied + ['section1/group1/field1', State::MODE_PRODUCTION, 1, false, false], + ['section1/group1/field1', State::MODE_PRODUCTION, 0, false, true], + ['section1/group1/field1', State::MODE_DEFAULT, 0, false, false], + ['section1/group1/field1', State::MODE_DEFAULT, 1, false, false], + ['section1/group1/field1', State::MODE_DEVELOPER, 0, false, false], + ['section1/group1/field1', State::MODE_DEVELOPER, 1, false, false], + //visibility of group 'section1/group1' should be applied + ['section1/group1/field2', State::MODE_PRODUCTION, 1, false, false], + ['section1/group1/field2', State::MODE_PRODUCTION, 0, true, false], + ['section1/group1/field2', State::MODE_DEFAULT, 0, false, false], + ['section1/group1/field2', State::MODE_DEFAULT, 1, false, false], + ['section1/group1/field2', State::MODE_DEVELOPER, 0, false, false], + ['section1/group1/field2', State::MODE_DEVELOPER, 1, false, false], + //exemption should be applied for section1/group2/field1 + ['section1/group2/field1', State::MODE_PRODUCTION, 1, false, false], + ['section1/group2/field1', State::MODE_PRODUCTION, 0, false, false], + ['section1/group2/field1', State::MODE_DEFAULT, 0, false, false], + ['section1/group2/field1', State::MODE_DEFAULT, 1, false, false], + ['section1/group2/field1', State::MODE_DEVELOPER, 0, false, false], + ['section1/group2/field1', State::MODE_DEVELOPER, 1, false, false], + //visibility of section 'section1' should be applied +// ['section1/group2/field2', State::MODE_PRODUCTION, 1, false, false], +// ['section1/group2/field2', State::MODE_PRODUCTION, 0, false, true], +// ['section1/group2/field2', State::MODE_DEFAULT, 0, false, false], +// ['section1/group2/field2', State::MODE_DEFAULT, 1, false, false], +// ['section1/group2/field2', State::MODE_DEVELOPER, 0, false, false], +// ['section1/group2/field2', State::MODE_DEVELOPER, 1, false, false], +// //exemption should be applied for section1/group1/field3 +// ['section1/group1/field3', State::MODE_PRODUCTION, 1, false, false], +// ['section1/group1/field3', State::MODE_PRODUCTION, 0, false, false], +// ['section1/group1/field3', State::MODE_DEFAULT, 0, false, false], +// ['section1/group1/field3', State::MODE_DEFAULT, 1, false, false], +// ['section1/group1/field3', State::MODE_DEVELOPER, 0, false, false], +// ['section1/group1/field3', State::MODE_DEVELOPER, 1, false, false], +// //visibility of group 'section2/group1' should be applied +// ['section2/group1/field1', State::MODE_PRODUCTION, 1, false, false], +// ['section2/group1/field1', State::MODE_PRODUCTION, 0, false, true], +// //exemption should be applied for section2/group2/field1 +// ['section2/group2/field1', State::MODE_PRODUCTION, 1, false, false], +// ['section2/group2/field1', State::MODE_PRODUCTION, 0, false, false], +// //any rule should not be applied +// ['section2/group3/field1', State::MODE_PRODUCTION, 1, false, false], +// ['section2/group3/field1', State::MODE_PRODUCTION, 0, false, false], + + ]; + } + +// /** +// * @param string $path +// * @param string $mageMode +// * @param bool $expectedResult +// * @dataProvider hiddenDataProvider +// */ +// public function testIsHidden($path, $mageMode, $expectedResult) +// { +// $this->stateMock->expects($this->once()) +// ->method('getMode') +// ->willReturn($mageMode); +// $this->assertSame($expectedResult, $this->model->isHidden($path)); +// } +// +// /** +// * @return array +// */ +// public function hiddenDataProvider() +// { +// return [ +// ['first/path', State::MODE_PRODUCTION, false], +// ['first/path', State::MODE_DEFAULT, false], +// ['some/path', State::MODE_PRODUCTION, false], +// ['second/path/field', State::MODE_PRODUCTION, true], +// ['second/path', State::MODE_DEVELOPER, false], +// ['fourth/path/value', State::MODE_PRODUCTION, false], +// ['fourth/path/test', State::MODE_PRODUCTION, true], +// ]; +// } +} diff --git a/lib/internal/Magento/Framework/Locale/Deployed/Options.php b/lib/internal/Magento/Framework/Locale/Deployed/Options.php index ea892ee7fcab0..1fe025a9f8203 100644 --- a/lib/internal/Magento/Framework/Locale/Deployed/Options.php +++ b/lib/internal/Magento/Framework/Locale/Deployed/Options.php @@ -5,7 +5,10 @@ */ namespace Magento\Framework\Locale\Deployed; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\State; +use Magento\Framework\Config\ConfigOptionsListConstants as Constants; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Locale\AvailableLocalesInterface; use Magento\Framework\Locale\ListsInterface; @@ -45,22 +48,30 @@ class Options implements OptionInterface */ private $localeLists; + /** + * @var DeploymentConfig + */ + private $deploymentConfig; + /** * @param ListsInterface $localeLists locales list * @param State $state application state class * @param AvailableLocalesInterface $availableLocales operates with available locales * @param DesignInterface $design operates with magento design settings + * @param DeploymentConfig $deploymentConfig */ public function __construct( ListsInterface $localeLists, State $state, AvailableLocalesInterface $availableLocales, - DesignInterface $design + DesignInterface $design, + DeploymentConfig $deploymentConfig = null ) { $this->localeLists = $localeLists; $this->state = $state; $this->availableLocales = $availableLocales; $this->design = $design; + $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->get(DeploymentConfig::class); } /** @@ -115,7 +126,8 @@ public function getTranslatedOptionLocales() */ private function filterLocales(array $locales) { - if ($this->state->getMode() != State::MODE_PRODUCTION) { + if ($this->state->getMode() != State::MODE_PRODUCTION + || $this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)) { return $locales; } diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php index 417bbfd402549..8e08d70a99ab7 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php @@ -5,7 +5,9 @@ */ namespace Magento\Framework\Locale\Test\Unit\Deployed; +use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\State; +use Magento\Framework\Config\ConfigOptionsListConstants as Constants; use Magento\Framework\Locale\AvailableLocalesInterface; use Magento\Framework\Locale\Deployed\Options; use Magento\Framework\Locale\ListsInterface; @@ -40,6 +42,11 @@ class OptionsTest extends \PHPUnit\Framework\TestCase */ private $localeListsMock; + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfigMock; + /** * @var Options */ @@ -59,63 +66,105 @@ protected function setUp() ->getMockForAbstractClass(); $this->localeListsMock = $this->getMockBuilder(ListsInterface::class) ->getMockForAbstractClass(); + $this->deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); $this->model = new Options( $this->localeListsMock, $this->stateMock, $this->availableLocalesMock, - $this->designMock + $this->designMock, + $this->deploymentConfigMock ); } /** * @param string $mode + * @param int $scdOnDemand + * @param array $locales + * @dataProvider getFullLocalesDataProvider + */ + public function testGetOptionLocalesFull($mode, $scdOnDemand, $locales) + { + $this->localeListsMock->expects($this->once()) + ->method('getOptionLocales') + ->willReturn($locales); + + $this->prepareGetLocalesFull($mode, $scdOnDemand); + + $this->assertEquals($locales, array_values($this->model->getOptionLocales())); + } + + /** + * @param string $mode + * @param int $scdOnDemand + * @param array $locales + * @dataProvider getFullLocalesDataProvider + */ + public function testGetTranslatedOptionLocalesFull($mode, $scdOnDemand, $locales) + { + $this->localeListsMock->expects($this->once()) + ->method('getTranslatedOptionLocales') + ->willReturn($locales); + + $this->prepareGetLocalesFull($mode, $scdOnDemand); + + $this->assertEquals($locales, array_values($this->model->getTranslatedOptionLocales())); + } + + /** + * @param string $mode + * @param int $scdOnDemand * @param array $locales * @param array $expectedLocales * @param array $deployedCodes - * @dataProvider getLocaleDataProvider + * @dataProvider getLimitedLocalesDataProvider */ - public function testGetOptionLocales($mode, $locales, $expectedLocales, $deployedCodes) + public function testGetOptionLocalesLimited($mode, $scdOnDemand, $locales, $expectedLocales, $deployedCodes) { $this->localeListsMock->expects($this->once()) ->method('getOptionLocales') ->willReturn($locales); - $this->prepareGetLocales($mode, $deployedCodes); + $this->prepareGetLocalesLimited($mode, $scdOnDemand, $deployedCodes); $this->assertEquals($expectedLocales, array_values($this->model->getOptionLocales())); } /** * @param string $mode + * @param int $scdOnDemand * @param array $locales * @param array $expectedLocales * @param array $deployedCodes - * @dataProvider getLocaleDataProvider + * @dataProvider getLimitedLocalesDataProvider */ - public function testGetTranslatedOptionLocales($mode, $locales, $expectedLocales, $deployedCodes) + public function testGetTranslatedOptionLocalesLimited($mode, $scdOnDemand, $locales, $expectedLocales, $deployedCodes) { $this->localeListsMock->expects($this->once()) ->method('getTranslatedOptionLocales') ->willReturn($locales); - $this->prepareGetLocales($mode, $deployedCodes); + $this->prepareGetLocalesLimited($mode, $scdOnDemand, $deployedCodes); $this->assertEquals($expectedLocales, array_values($this->model->getTranslatedOptionLocales())); } /** * @param $mode + * @param int $scdOnDemand * @param $deployedCodes * @return void */ - private function prepareGetLocales($mode, $deployedCodes) + private function prepareGetLocalesLimited($mode, $scdOnDemand, $deployedCodes) { $this->stateMock->expects($this->once()) ->method('getMode') ->willReturn($mode); + $this->deploymentConfigMock->expects($this->any()) + ->method('getConfigData') + ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) + ->willReturn($scdOnDemand); - if ($mode == State::MODE_PRODUCTION) { $area = 'area'; $code = 'code'; $themeMock = $this->getMockBuilder(ThemeInterface::class) @@ -133,32 +182,94 @@ private function prepareGetLocales($mode, $deployedCodes) ->method('getList') ->with($code, $area) ->willReturn($deployedCodes); - } + } + + /** + * @param $mode + * @param int $scdOnDemand + * @return void + */ + private function prepareGetLocalesFull($mode, $scdOnDemand) + { + $this->stateMock->expects($this->once()) + ->method('getMode') + ->willReturn($mode); + $this->deploymentConfigMock->expects($this->any()) + ->method('getConfigData') + ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) + ->willReturn($scdOnDemand); + + $this->designMock->expects($this->never()) + ->method('getDesignTheme'); } /** * @return array */ - public function getLocaleDataProvider() + public function getFullLocalesDataProvider() { + $deLocale = [ + 'value' => 'de_DE', + 'label' => 'German (German)' + ]; + $daLocale = [ + 'value' => 'da_DK', + 'label' => 'Danish (Denmark)' + ]; + return [ [ State::MODE_PRODUCTION, + 1, + [$daLocale, $deLocale], + ], + [ + State::MODE_DEVELOPER, + 0, + [$daLocale, $deLocale], + ], + [ + State::MODE_DEVELOPER, + 1, + [$deLocale], + ], + [ + State::MODE_DEFAULT, + 0, + [$daLocale], + ], + [ + State::MODE_DEFAULT, + 1, + [$daLocale, $deLocale], + ], + ]; + } + + /** + * @return array + */ + public function getLimitedLocalesDataProvider() + { + $deLocale = [ + 'value' => 'de_DE', + 'label' => 'German (German)' + ]; + $daLocale = [ + 'value' => 'da_DK', + 'label' => 'Danish (Denmark)' + ]; + + return [ + [ + State::MODE_PRODUCTION, + 0, [ - [ - 'value' => 'da_DK', - 'label' => 'Danish (Denmark)' - ], - [ - 'value' => 'de_DE', - 'label' => 'German (German)' - ] + $daLocale, + $deLocale ], [ - [ - 'value' => 'de_DE', - 'label' => 'German (German)' - ] + $deLocale ], [ 'de_DE' @@ -166,38 +277,17 @@ public function getLocaleDataProvider() ], [ State::MODE_PRODUCTION, + 0, [ - [ - 'value' => 'de_DE', - 'label' => 'German (German)' - ] - ], - [], - [] - ], - [ - State::MODE_DEVELOPER, - [ - [ - 'value' => 'da_DK', - 'label' => 'Danish (Denmark)' - ], - [ - 'value' => 'de_DE', - 'label' => 'German (German)' - ] + $daLocale, + $deLocale ], [ - [ - 'value' => 'da_DK', - 'label' => 'Danish (Denmark)' - ], - [ - 'value' => 'de_DE', - 'label' => 'German (German)' - ] + $daLocale, + $deLocale ], [ + 'da_DK', 'de_DE' ] ], From e12ae6e9663bb09a6c3acf4469e61595623c99a9 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 10:17:37 +0300 Subject: [PATCH 114/333] MAGETWO-72841: Update On Save Reindex do not work on configurable product options changing --- .../Indexer/Product/Eav/AbstractAction.php | 6 ++- .../Product/Indexer/Eav/AbstractEav.php | 14 +++++- .../Product/Eav/AbstractActionTest.php | 50 +++++++++++++++---- .../Product/Indexer/Eav/SourceTest.php | 13 +++++ .../Search/Adapter/Mysql/AdapterTest.php | 1 - 5 files changed, 71 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php index 6a2642a8568f4..c16436fc90433 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Eav; +use \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav; + /** * Abstract action reindex class */ @@ -140,10 +142,12 @@ protected function syncData($indexer, $destinationTable, $ids) * @param bool $onlyParents * @return array $ids */ - protected function processRelations($indexer, $ids, $onlyParents = false) + protected function processRelations(AbstractEav $indexer, array $ids, bool $onlyParents = false) { $parentIds = $indexer->getRelationsByChild($ids); + $parentIds = array_unique(array_merge($parentIds, $ids)); $childIds = $onlyParents ? [] : $indexer->getRelationsByParent($parentIds); + return array_unique(array_merge($ids, $childIds, $parentIds)); } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php index c4e3fb1bf1e70..88c1300aa7cc2 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php @@ -82,6 +82,7 @@ public function reindexEntities($processIds) $this->_prepareIndex($processIds); $this->_prepareRelationIndex($processIds); $this->_removeNotVisibleEntityFromIndex(); + return $this; } @@ -159,11 +160,12 @@ protected function _removeNotVisibleEntityFromIndex() * @param array $parentIds the parent entity ids limitation * @return \Magento\Framework\DB\Select */ - protected function _prepareRelationIndexSelect($parentIds = null) + protected function _prepareRelationIndexSelect( array $parentIds = null) { $connection = $this->getConnection(); $idxTable = $this->getIdxTable(); $linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField(); + $select = $connection->select()->from( ['l' => $this->getTable('catalog_product_relation')], [] @@ -179,6 +181,16 @@ protected function _prepareRelationIndexSelect($parentIds = null) ['i' => $idxTable], 'l.child_id = i.entity_id AND cs.store_id = i.store_id', [] + )->join( + ['sw' => $this->getTable('store_website')], + "cs.website_id = sw.website_id", + [] + )->joinLeft( + ['cpw' => $this->getTable('catalog_product_website')], + "i.entity_id = cpw.product_id AND sw.website_id = cpw.website_id", + [] + )->where( + 'cpw.product_id IS NOT NULL' )->group( ['parent_id', 'i.attribute_id', 'i.store_id', 'i.value', 'l.child_id'] )->columns( diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php index 58654136ab5a8..52bbff6eb5597 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php @@ -113,11 +113,20 @@ public function testReindexWithoutArgumentsExecutesReindexAll() $this->_model->reindex(); } - public function testReindexWithNotNullArgumentExecutesReindexEntities() - { - $childIds = [1, 2, 3]; - $parentIds = [4]; - $reindexIds = array_merge($childIds, $parentIds); + /** + * @param array $ids + * @param array $parentIds + * @param array $childIds + * @throws \Exception + * @dataProvider reindexEntitiesDataProvider + */ + public function testReindexWithNotNullArgumentExecutesReindexEntities( + array $ids, + array $parentIds, + array $childIds + ) { + $reindexIds = array_unique(array_merge($ids, $parentIds, $childIds)); + $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) ->getMockForAbstractClass(); @@ -129,11 +138,23 @@ public function testReindexWithNotNullArgumentExecutesReindexEntities() ->disableOriginalConstructor() ->getMock(); - $eavSource->expects($this->once())->method('getRelationsByChild')->with($childIds)->willReturn($childIds); - $eavSource->expects($this->once())->method('getRelationsByParent')->with($childIds)->willReturn($parentIds); + $eavSource->expects($this->once()) + ->method('getRelationsByChild') + ->with($ids) + ->willReturn($parentIds); + $eavSource->expects($this->once()) + ->method('getRelationsByParent') + ->with(array_unique(array_merge($parentIds, $ids))) + ->willReturn($childIds); - $eavDecimal->expects($this->once())->method('getRelationsByChild')->with($reindexIds)->willReturn($reindexIds); - $eavDecimal->expects($this->once())->method('getRelationsByParent')->with($reindexIds)->willReturn([]); + $eavDecimal->expects($this->once()) + ->method('getRelationsByChild') + ->with($reindexIds) + ->willReturn($parentIds); + $eavDecimal->expects($this->once()) + ->method('getRelationsByParent') + ->with(array_unique(array_merge($parentIds, $reindexIds))) + ->willReturn($childIds); $eavSource->expects($this->once())->method('getConnection')->willReturn($connectionMock); $eavDecimal->expects($this->once())->method('getConnection')->willReturn($connectionMock); @@ -153,6 +174,15 @@ public function testReindexWithNotNullArgumentExecutesReindexEntities() ->method('create') ->will($this->returnValue($eavDecimal)); - $this->_model->reindex($childIds); + $this->_model->reindex($ids); + } + + public function reindexEntitiesDataProvider() + { + return [ + [[4], [], [1, 2, 3]], + [[3], [4], []], + [[5], [], []] + ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php index 77d6b44749943..d9dcc4b4dac39 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php @@ -97,6 +97,19 @@ public function testReindexEntitiesForConfigurableProduct() $result = $connection->fetchAll($select); $this->assertCount(0, $result); + + /** @var \Magento\Catalog\Model\Product $product1 **/ + $product1 = $productRepository->getById(10); + $product1->setStatus(Status::STATUS_ENABLED)->setWebsiteIds([]); + $productRepository->save($product1); + + /** @var \Magento\Catalog\Model\Product $product2 **/ + $product2 = $productRepository->getById(20); + $product2->setStatus(Status::STATUS_ENABLED); + $productRepository->save($product2); + + $result = $connection->fetchAll($select); + $this->assertCount(1, $result); } /** diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php index 36539da724a23..ec1538e950c12 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php @@ -523,7 +523,6 @@ public function testAdvancedSearchDateField($rangeFilter, $expectedRecordsCount) */ public function testAdvancedSearchCompositeProductWithOutOfStockOption() { - $this->markTestSkipped('MAGETWO-71445: configurable product created incorrectly - children not linked'). /** @var Attribute $attribute */ $attribute = $this->objectManager->get(Attribute::class) ->loadByCode(Product::ENTITY, 'test_configurable'); From 5da39f2680a17f73e2246bedba38a360a8d990fb Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 21 May 2018 10:38:17 +0300 Subject: [PATCH 115/333] MAGETWO-61364: [GitHub] Dependency check in the static tests fails for non core modules #7421 --- .../framework/Magento/TestFramework/Dependency/PhpRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php index 2df84c579ccbc..800bba2a880bd 100644 --- a/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php +++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php @@ -116,7 +116,7 @@ private function caseClassesAndIdentifiers($currentModule, $file, &$contents) { $pattern = '~\b(?<class>(?<module>(' . implode( - '_|', + '[_\\\\]|', Files::init()->getNamespaces() ) . '[_\\\\])[a-zA-Z0-9]+)' From ad42b71dba6c2f3879ccfbad764ae63908c90f30 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 10:52:52 +0300 Subject: [PATCH 116/333] MAGETWO-72841: Update On Save Reindex do not work on configurable product options changing --- .../Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php index 88c1300aa7cc2..60ee774f985e3 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php @@ -160,7 +160,7 @@ protected function _removeNotVisibleEntityFromIndex() * @param array $parentIds the parent entity ids limitation * @return \Magento\Framework\DB\Select */ - protected function _prepareRelationIndexSelect( array $parentIds = null) + protected function _prepareRelationIndexSelect(array $parentIds = null) { $connection = $this->getConnection(); $idxTable = $this->getIdxTable(); From 2a1cb4348fe393c2bcbbe6d9502c62badd2b91d4 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 12:30:35 +0300 Subject: [PATCH 117/333] MAGETWO-90804: "Hide from Product Page" option does not work for child of configurable product --- .../Model/Plugin/ProductIdentitiesExtender.php | 4 +--- app/code/Magento/Swatches/Helper/Data.php | 8 ++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php index 68c574194817c..981d1fab483b5 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php @@ -46,11 +46,9 @@ public function __construct(Configurable $configurableType, ProductRepositoryInt */ public function afterGetIdentities(Product $subject, array $identities): array { - $identities = (array) $identities; - foreach ($this->configurableType->getParentIdsByChild($subject->getId()) as $parentId) { $parentProduct = $this->productRepository->getById($parentId); - $identities = array_merge($identities, (array) $parentProduct->getIdentities()); + $identities = array_merge((array) $identities, (array) $parentProduct->getIdentities()); } return array_unique($identities); diff --git a/app/code/Magento/Swatches/Helper/Data.php b/app/code/Magento/Swatches/Helper/Data.php index 8a89f0f01db79..c76ed54d6c598 100644 --- a/app/code/Magento/Swatches/Helper/Data.php +++ b/app/code/Magento/Swatches/Helper/Data.php @@ -321,9 +321,7 @@ public function getProductMediaGallery(ModelProduct $product) continue; } - if (in_array('image', $mediaEntry->getTypes(), true)) { - $baseImage = $mediaEntry->getFile(); - } elseif (!$baseImage) { + if (in_array('image', $mediaEntry->getTypes(), true) || (!$baseImage)) { $baseImage = $mediaEntry->getFile(); } @@ -506,9 +504,7 @@ private function addFallbackOptions(array $fallbackValues, array $swatches) */ public function isProductHasSwatch(Product $product) { - $swatchAttributes = $this->getSwatchAttributes($product) ?: []; - - return count($swatchAttributes) > 0; + return count($this->getSwatchAttributes($product) ?: []) > 0; } /** From 8c004a7a14446cfa842cb8740c0e2e75ac07fe9e Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Mon, 21 May 2018 12:51:33 +0300 Subject: [PATCH 118/333] MAGETWO-91934: Unlock Locales Editing when SCD on Demand Mode is Enabled - fix static tests --- .../ElementVisibility/ConcealInProduction.php | 14 ++- .../ElementVisibility/ConcealScdField.php | 6 +- .../ConcealInProductionTest.php | 104 +++++++++--------- .../ElementVisibility/ConcealScdFieldTest.php | 85 +++++--------- .../Framework/Locale/Deployed/Options.php | 10 +- .../Locale/Test/Unit/Deployed/OptionsTest.php | 5 +- 6 files changed, 102 insertions(+), 122 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php index 4879dec385407..d5ded9292864a 100755 --- a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php +++ b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProduction.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Config\Model\Config\Structure\ElementVisibility; use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; @@ -80,21 +82,23 @@ public function __construct(State $state, array $configs = [], array $exemptions */ public function isHidden($path) { - $result = false; $path = $this->normalizePath($path); if ($this->state->getMode() === State::MODE_PRODUCTION && preg_match('/(?<group>(?<section>.*?)\/.*?)\/.*?/', $path, $match)) { $group = $match['group']; $section = $match['section']; $exemptions = array_keys($this->exemptions); - foreach ($this->configs as $configPath => $value) { - if ($value === static::HIDDEN && strpos($path, $configPath) !==false) { - $result = empty(array_intersect([$section, $group, $path], $exemptions)); + $checkedItems = []; + foreach ([$path, $group, $section] as $itemPath) { + $checkedItems[] = $itemPath; + if (!empty($this->configs[$itemPath])) { + return $this->configs[$itemPath] === static::HIDDEN + && empty(array_intersect($checkedItems, $exemptions)); } } } - return $result; + return false; } /** diff --git a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php index 1335921520c38..af1ca50b5910d 100755 --- a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php +++ b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Config\Model\Config\Structure\ElementVisibility; use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; @@ -90,7 +92,7 @@ public function __construct( /** * @inheritdoc */ - public function isHidden($path) + public function isHidden($path): bool { $path = $this->normalizePath($path); if ($this->state->getMode() === State::MODE_PRODUCTION @@ -115,7 +117,7 @@ public function isHidden($path) /** * @inheritdoc */ - public function isDisabled($path) + public function isDisabled($path): bool { $path = $this->normalizePath($path); if ($this->state->getMode() === State::MODE_PRODUCTION diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php index e3232f3efd2f8..5fc689f911c1c 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Config\Test\Unit\Model\Config\Structure\ElementVisibility; use Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction; @@ -28,16 +30,20 @@ protected function setUp() ->getMock(); $configs = [ - 'first/path' => ElementVisibilityInterface::DISABLED, - 'second/path' => ElementVisibilityInterface::HIDDEN, - 'third' => ElementVisibilityInterface::DISABLED, - 'third/path' => 'no', - 'third/path/field' => ElementVisibilityInterface::DISABLED, - 'first/path/field' => 'no', - 'fourth' => ElementVisibilityInterface::HIDDEN, + 'section1/group1/field1' => ElementVisibilityInterface::DISABLED, + 'section1/group1' => ElementVisibilityInterface::HIDDEN, + 'section1' => ElementVisibilityInterface::DISABLED, + 'section1/group2' => 'no', + 'section2/group1' => ElementVisibilityInterface::DISABLED, + 'section2/group2' => ElementVisibilityInterface::HIDDEN, + 'section3' => ElementVisibilityInterface::HIDDEN, + 'section3/group1/field1' => 'no', ]; $exemptions = [ - 'fourth/path/value' => '', + 'section1/group1/field3' => '', + 'section1/group2/field1' => '', + 'section2/group2/field1' => '', + 'section3/group2' => '', ]; $this->model = new ConcealInProduction($this->stateMock, $configs, $exemptions); @@ -46,65 +52,55 @@ protected function setUp() /** * @param string $path * @param string $mageMode - * @param bool $expectedResult + * @param bool $isDisabled + * @param bool $isHidden * @dataProvider disabledDataProvider */ - public function testIsDisabled($path, $mageMode, $expectedResult) + public function testCheckVisibility(string $path, string $mageMode, bool $isHidden, bool $isDisabled): void { - $this->stateMock->expects($this->once()) + $this->stateMock->expects($this->any()) ->method('getMode') ->willReturn($mageMode); - $this->assertSame($expectedResult, $this->model->isDisabled($path)); - } - - /** - * @return array - */ - public function disabledDataProvider() - { - return [ - ['first/path', State::MODE_PRODUCTION, true], - ['first/path/field', State::MODE_PRODUCTION, false], - ['first/path/field2', State::MODE_PRODUCTION, true], - ['first/path', State::MODE_DEFAULT, false], - ['some/path', State::MODE_PRODUCTION, false], - ['second/path', State::MODE_PRODUCTION, false], - ['third', State::MODE_PRODUCTION, true], - ['third/path2', State::MODE_PRODUCTION, true], - ['third/path2/field', State::MODE_PRODUCTION, true], - ['third/path', State::MODE_PRODUCTION, false], - ['third/path/field', State::MODE_PRODUCTION, true], - ['third/path/field2', State::MODE_PRODUCTION, false], - ]; - } - /** - * @param string $path - * @param string $mageMode - * @param bool $expectedResult - * @dataProvider hiddenDataProvider - */ - public function testIsHidden($path, $mageMode, $expectedResult) - { - $this->stateMock->expects($this->once()) - ->method('getMode') - ->willReturn($mageMode); - $this->assertSame($expectedResult, $this->model->isHidden($path)); + $this->assertSame($isHidden, $this->model->isHidden($path)); + $this->assertSame($isDisabled, $this->model->isDisabled($path)); } /** * @return array */ - public function hiddenDataProvider() + public function disabledDataProvider(): array { return [ - ['first/path', State::MODE_PRODUCTION, false], - ['first/path', State::MODE_DEFAULT, false], - ['some/path', State::MODE_PRODUCTION, false], - ['second/path/field', State::MODE_PRODUCTION, true], - ['second/path', State::MODE_DEVELOPER, false], - ['fourth/path/value', State::MODE_PRODUCTION, false], - ['fourth/path/test', State::MODE_PRODUCTION, true], + //visibility of field 'section1/group1/field1' should be applied + ['section1/group1/field1', State::MODE_PRODUCTION, false, true], + ['section1/group1/field1', State::MODE_DEFAULT, false, false], + ['section1/group1/field1', State::MODE_DEVELOPER, false, false], + //visibility of group 'section1/group1' should be applied + ['section1/group1/field2', State::MODE_PRODUCTION, true, false], + ['section1/group1/field2', State::MODE_DEFAULT, false, false], + ['section1/group1/field2', State::MODE_DEVELOPER, false, false], + //exemption should be applied for section1/group2/field1 + ['section1/group2/field1', State::MODE_PRODUCTION, false, false], + ['section1/group2/field1', State::MODE_DEFAULT, false, false], + ['section1/group2/field1', State::MODE_DEVELOPER, false, false], + //as 'section1/group2' has neither Disable nor Hidden rule, this field should be visible + ['section1/group2/field2', State::MODE_PRODUCTION, false, false], + //exemption should be applied for section1/group1/field3 + ['section1/group1/field3', State::MODE_PRODUCTION, false, false], + //visibility of group 'section2/group1' should be applied + ['section2/group1/field1', State::MODE_PRODUCTION, false, true], + //exemption should be applied for section2/group2/field1 + ['section2/group2/field1', State::MODE_PRODUCTION, false, false], + //any rule should not be applied + ['section2/group3/field1', State::MODE_PRODUCTION, false, false], + //any rule should not be applied + ['section3/group1/field1', State::MODE_PRODUCTION, false, false], + //visibility of section 'section3' should be applied + ['section3/group1/field2', State::MODE_PRODUCTION, true, false], + //exception from 'section3/group2' should be applied + ['section3/group2/field1', State::MODE_PRODUCTION, false, false], + ]; } } diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php index 3ffa762eeed8e..78780390413bb 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Config\Test\Unit\Model\Config\Structure\ElementVisibility; use Magento\Framework\App\DeploymentConfig; @@ -43,7 +45,6 @@ protected function setUp() 'section2/group2' => ElementVisibilityInterface::HIDDEN, 'section3' => ElementVisibilityInterface::HIDDEN, 'section3/group1/field1' => 'no', - 'section5' => 'no', ]; $exemptions = [ 'section1/group1/field3' => '', @@ -63,7 +64,8 @@ protected function setUp() * @param bool $isHidden * @dataProvider disabledDataProvider */ - public function testCheckVisibility($path, $mageMode, $scdOnDemand, $isHidden, $isDisabled) + public function testCheckVisibility(string $path, string $mageMode, + int $scdOnDemand, bool $isHidden, bool $isDisabled): void { $this->stateMock->expects($this->any()) ->method('getMode') @@ -80,7 +82,7 @@ public function testCheckVisibility($path, $mageMode, $scdOnDemand, $isHidden, $ /** * @return array */ - public function disabledDataProvider() + public function disabledDataProvider(): array { return [ //visibility of field 'section1/group1/field1' should be applied @@ -104,60 +106,31 @@ public function disabledDataProvider() ['section1/group2/field1', State::MODE_DEFAULT, 1, false, false], ['section1/group2/field1', State::MODE_DEVELOPER, 0, false, false], ['section1/group2/field1', State::MODE_DEVELOPER, 1, false, false], - //visibility of section 'section1' should be applied -// ['section1/group2/field2', State::MODE_PRODUCTION, 1, false, false], -// ['section1/group2/field2', State::MODE_PRODUCTION, 0, false, true], -// ['section1/group2/field2', State::MODE_DEFAULT, 0, false, false], -// ['section1/group2/field2', State::MODE_DEFAULT, 1, false, false], -// ['section1/group2/field2', State::MODE_DEVELOPER, 0, false, false], -// ['section1/group2/field2', State::MODE_DEVELOPER, 1, false, false], -// //exemption should be applied for section1/group1/field3 -// ['section1/group1/field3', State::MODE_PRODUCTION, 1, false, false], -// ['section1/group1/field3', State::MODE_PRODUCTION, 0, false, false], -// ['section1/group1/field3', State::MODE_DEFAULT, 0, false, false], -// ['section1/group1/field3', State::MODE_DEFAULT, 1, false, false], -// ['section1/group1/field3', State::MODE_DEVELOPER, 0, false, false], -// ['section1/group1/field3', State::MODE_DEVELOPER, 1, false, false], -// //visibility of group 'section2/group1' should be applied -// ['section2/group1/field1', State::MODE_PRODUCTION, 1, false, false], -// ['section2/group1/field1', State::MODE_PRODUCTION, 0, false, true], -// //exemption should be applied for section2/group2/field1 -// ['section2/group2/field1', State::MODE_PRODUCTION, 1, false, false], -// ['section2/group2/field1', State::MODE_PRODUCTION, 0, false, false], -// //any rule should not be applied -// ['section2/group3/field1', State::MODE_PRODUCTION, 1, false, false], -// ['section2/group3/field1', State::MODE_PRODUCTION, 0, false, false], + //as 'section1/group2' has neither Disable nor Hidden rule, this field should be visible + ['section1/group2/field2', State::MODE_PRODUCTION, 1, false, false], + ['section1/group2/field2', State::MODE_PRODUCTION, 0, false, false], + //exemption should be applied for section1/group1/field3 + ['section1/group1/field3', State::MODE_PRODUCTION, 1, false, false], + ['section1/group1/field3', State::MODE_PRODUCTION, 0, false, false], + //visibility of group 'section2/group1' should be applied + ['section2/group1/field1', State::MODE_PRODUCTION, 1, false, false], + ['section2/group1/field1', State::MODE_PRODUCTION, 0, false, true], + //exemption should be applied for section2/group2/field1 + ['section2/group2/field1', State::MODE_PRODUCTION, 1, false, false], + ['section2/group2/field1', State::MODE_PRODUCTION, 0, false, false], + //any rule should not be applied + ['section2/group3/field1', State::MODE_PRODUCTION, 1, false, false], + ['section2/group3/field1', State::MODE_PRODUCTION, 0, false, false], + //any rule should not be applied + ['section3/group1/field1', State::MODE_PRODUCTION, 1, false, false], + ['section3/group1/field1', State::MODE_PRODUCTION, 0, false, false], + //visibility of section 'section3' should be applied + ['section3/group1/field2', State::MODE_PRODUCTION, 1, false, false], + ['section3/group1/field2', State::MODE_PRODUCTION, 0, true, false], + //exception from 'section3/group2' should be applied + ['section3/group2/field1', State::MODE_PRODUCTION, 1, false, false], + ['section3/group2/field1', State::MODE_PRODUCTION, 0, false, false], ]; } - -// /** -// * @param string $path -// * @param string $mageMode -// * @param bool $expectedResult -// * @dataProvider hiddenDataProvider -// */ -// public function testIsHidden($path, $mageMode, $expectedResult) -// { -// $this->stateMock->expects($this->once()) -// ->method('getMode') -// ->willReturn($mageMode); -// $this->assertSame($expectedResult, $this->model->isHidden($path)); -// } -// -// /** -// * @return array -// */ -// public function hiddenDataProvider() -// { -// return [ -// ['first/path', State::MODE_PRODUCTION, false], -// ['first/path', State::MODE_DEFAULT, false], -// ['some/path', State::MODE_PRODUCTION, false], -// ['second/path/field', State::MODE_PRODUCTION, true], -// ['second/path', State::MODE_DEVELOPER, false], -// ['fourth/path/value', State::MODE_PRODUCTION, false], -// ['fourth/path/test', State::MODE_PRODUCTION, true], -// ]; -// } } diff --git a/lib/internal/Magento/Framework/Locale/Deployed/Options.php b/lib/internal/Magento/Framework/Locale/Deployed/Options.php index 1fe025a9f8203..82f80b8a3b576 100644 --- a/lib/internal/Magento/Framework/Locale/Deployed/Options.php +++ b/lib/internal/Magento/Framework/Locale/Deployed/Options.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Locale\Deployed; use Magento\Framework\App\DeploymentConfig; @@ -77,7 +79,7 @@ public function __construct( /** * {@inheritdoc} */ - public function getOptionLocales() + public function getOptionLocales(): array { return $this->filterLocales($this->localeLists->getOptionLocales()); } @@ -85,7 +87,7 @@ public function getOptionLocales() /** * {@inheritdoc} */ - public function getTranslatedOptionLocales() + public function getTranslatedOptionLocales(): array { return $this->filterLocales($this->localeLists->getTranslatedOptionLocales()); } @@ -93,7 +95,7 @@ public function getTranslatedOptionLocales() /** * Filter list of locales by available locales for current theme and depends on running application mode. * - * Applies filters only in production mode. + * Applies filters only in production mode when flag 'static_content_on_demand_in_production' is not enabled. * For example, if the current design theme has only one generated locale en_GB then for given array of locales: * ```php * $locales = [ @@ -124,7 +126,7 @@ public function getTranslatedOptionLocales() * @param array $locales list of locales for filtering * @return array of filtered locales */ - private function filterLocales(array $locales) + private function filterLocales(array $locales): array { if ($this->state->getMode() != State::MODE_PRODUCTION || $this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)) { diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php index 8e08d70a99ab7..7089cfbae1815 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Locale\Test\Unit\Deployed; use Magento\Framework\App\DeploymentConfig; @@ -138,7 +140,8 @@ public function testGetOptionLocalesLimited($mode, $scdOnDemand, $locales, $expe * @param array $deployedCodes * @dataProvider getLimitedLocalesDataProvider */ - public function testGetTranslatedOptionLocalesLimited($mode, $scdOnDemand, $locales, $expectedLocales, $deployedCodes) + public function testGetTranslatedOptionLocalesLimited($mode, $scdOnDemand, $locales, + $expectedLocales, $deployedCodes) { $this->localeListsMock->expects($this->once()) ->method('getTranslatedOptionLocales') From bf7c6b9036d53da39e479abdd4c3f4afe8d9e6a3 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 13:04:13 +0300 Subject: [PATCH 119/333] MAGETWO-90804: "Hide from Product Page" option does not work for child of configurable product --- .../Model/Plugin/ProductIdentitiesExtender.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php index 981d1fab483b5..0c3fc6fba6005 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php +++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/ProductIdentitiesExtender.php @@ -48,7 +48,7 @@ public function afterGetIdentities(Product $subject, array $identities): array { foreach ($this->configurableType->getParentIdsByChild($subject->getId()) as $parentId) { $parentProduct = $this->productRepository->getById($parentId); - $identities = array_merge((array) $identities, (array) $parentProduct->getIdentities()); + $identities = array_merge($identities, $parentProduct->getIdentities()); } return array_unique($identities); From 54589bc20ebc498c3c284a933bbf825b3876b3a4 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 13:05:53 +0300 Subject: [PATCH 120/333] MAGETWO-90804: "Hide from Product Page" option does not work for child of configurable product --- app/code/Magento/Swatches/Helper/Data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/Helper/Data.php b/app/code/Magento/Swatches/Helper/Data.php index c76ed54d6c598..e2191f0602b0c 100644 --- a/app/code/Magento/Swatches/Helper/Data.php +++ b/app/code/Magento/Swatches/Helper/Data.php @@ -504,7 +504,7 @@ private function addFallbackOptions(array $fallbackValues, array $swatches) */ public function isProductHasSwatch(Product $product) { - return count($this->getSwatchAttributes($product) ?: []) > 0; + return !empty($this->getSwatchAttributes($product) ?: []); } /** From 2a7c8c8ea309000ea00523732e12832800a20002 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 13:36:56 +0300 Subject: [PATCH 121/333] MAGETWO-90804: "Hide from Product Page" option does not work for child of configurable product --- app/code/Magento/Swatches/Helper/Data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/Helper/Data.php b/app/code/Magento/Swatches/Helper/Data.php index e2191f0602b0c..7d192bbb5b334 100644 --- a/app/code/Magento/Swatches/Helper/Data.php +++ b/app/code/Magento/Swatches/Helper/Data.php @@ -321,7 +321,7 @@ public function getProductMediaGallery(ModelProduct $product) continue; } - if (in_array('image', $mediaEntry->getTypes(), true) || (!$baseImage)) { + if (in_array('image', $mediaEntry->getTypes(), true) || !$baseImage) { $baseImage = $mediaEntry->getFile(); } From b3222d632c8450ab9c9d6e9bc35bddee748c87e0 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 14:00:16 +0300 Subject: [PATCH 122/333] MAGETWO-90804: "Hide from Product Page" option does not work for child of configurable product --- app/code/Magento/Swatches/Helper/Data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/Helper/Data.php b/app/code/Magento/Swatches/Helper/Data.php index 7d192bbb5b334..38879178235c0 100644 --- a/app/code/Magento/Swatches/Helper/Data.php +++ b/app/code/Magento/Swatches/Helper/Data.php @@ -504,7 +504,7 @@ private function addFallbackOptions(array $fallbackValues, array $swatches) */ public function isProductHasSwatch(Product $product) { - return !empty($this->getSwatchAttributes($product) ?: []); + return !empty($this->getSwatchAttributes($product)); } /** From 7d1e87173181d734c4a70d973fc6de501540eade Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 15:08:40 +0300 Subject: [PATCH 123/333] MAGETWO-72841: Update On Save Reindex do not work on configurable product options changing --- .../Model/Indexer/Product/Eav/AbstractAction.php | 11 +++++------ .../ResourceModel/Product/Indexer/Eav/AbstractEav.php | 6 ++---- .../Model/Indexer/Product/Eav/AbstractActionTest.php | 9 ++++++--- .../ResourceModel/Product/Indexer/Eav/SourceTest.php | 5 +++-- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php index c16436fc90433..b6206f96b91e0 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php @@ -5,7 +5,7 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Eav; -use \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav; /** * Abstract action reindex class @@ -53,7 +53,7 @@ abstract public function execute($ids); /** * Retrieve array of EAV type indexers * - * @return \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav[] + * @return AbstractEav[] */ public function getIndexers() { @@ -71,7 +71,7 @@ public function getIndexers() * Retrieve indexer instance by type * * @param string $type - * @return \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav + * @return AbstractEav * @throws \Magento\Framework\Exception\LocalizedException */ public function getIndexer($type) @@ -110,7 +110,7 @@ public function reindex($ids = null) /** * Synchronize data between index storage and original storage * - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav $indexer + * @param AbstractEav $indexer * @param string $destinationTable * @param array $ids * @throws \Exception @@ -136,9 +136,8 @@ protected function syncData($indexer, $destinationTable, $ids) /** * Retrieve product relations by children and parent * - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav $indexer + * @param AbstractEav $indexer * @param array $ids - * * @param bool $onlyParents * @return array $ids */ diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php index 60ee774f985e3..a2de248ed7ca2 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php @@ -185,12 +185,10 @@ protected function _prepareRelationIndexSelect(array $parentIds = null) ['sw' => $this->getTable('store_website')], "cs.website_id = sw.website_id", [] - )->joinLeft( + )->join( ['cpw' => $this->getTable('catalog_product_website')], - "i.entity_id = cpw.product_id AND sw.website_id = cpw.website_id", + "i.entity_id = cpw.product_id AND sw.website_id = cpw.website_id AND cpw.product_id IS NOT NULL", [] - )->where( - 'cpw.product_id IS NOT NULL' )->group( ['parent_id', 'i.attribute_id', 'i.store_id', 'i.value', 'l.child_id'] )->columns( diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php index 52bbff6eb5597..655b09b62ef0a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php @@ -117,14 +117,14 @@ public function testReindexWithoutArgumentsExecutesReindexAll() * @param array $ids * @param array $parentIds * @param array $childIds - * @throws \Exception + * @return void * @dataProvider reindexEntitiesDataProvider */ public function testReindexWithNotNullArgumentExecutesReindexEntities( array $ids, array $parentIds, array $childIds - ) { + ) : void { $reindexIds = array_unique(array_merge($ids, $parentIds, $childIds)); $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) @@ -177,12 +177,15 @@ public function testReindexWithNotNullArgumentExecutesReindexEntities( $this->_model->reindex($ids); } + /** + * @return array + */ public function reindexEntitiesDataProvider() { return [ [[4], [], [1, 2, 3]], [[3], [4], []], - [[5], [], []] + [[5], [], []], ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php index d9dcc4b4dac39..b1e0bbbf36143 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php @@ -108,8 +108,9 @@ public function testReindexEntitiesForConfigurableProduct() $product2->setStatus(Status::STATUS_ENABLED); $productRepository->save($product2); - $result = $connection->fetchAll($select); - $this->assertCount(1, $result); + $statusSelect = clone $select; + $statusSelect->reset(\Zend_Db_Select::COLUMNS)->columns(new \Zend_Db_Expr('COUNT(*)')); + $this->assertEquals(1, $connection->fetchOne($statusSelect)); } /** From 8cab4410d5a9e69c470753d10b862d84fc615c4b Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 May 2018 15:48:29 +0300 Subject: [PATCH 124/333] MAGETWO-72841: Update On Save Reindex do not work on configurable product options changing --- .../Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php | 2 +- .../Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php | 2 +- .../Model/ResourceModel/Product/Indexer/Eav/SourceTest.php | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php index a2de248ed7ca2..c33ea7c781aa3 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/AbstractEav.php @@ -187,7 +187,7 @@ protected function _prepareRelationIndexSelect(array $parentIds = null) [] )->join( ['cpw' => $this->getTable('catalog_product_website')], - "i.entity_id = cpw.product_id AND sw.website_id = cpw.website_id AND cpw.product_id IS NOT NULL", + 'i.entity_id = cpw.product_id AND sw.website_id = cpw.website_id', [] )->group( ['parent_id', 'i.attribute_id', 'i.store_id', 'i.value', 'l.child_id'] diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php index 655b09b62ef0a..9d58822fb6073 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php @@ -180,7 +180,7 @@ public function testReindexWithNotNullArgumentExecutesReindexEntities( /** * @return array */ - public function reindexEntitiesDataProvider() + public function reindexEntitiesDataProvider() : array { return [ [[4], [], [1, 2, 3]], diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php index b1e0bbbf36143..4280e9dc8915c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/SourceTest.php @@ -109,7 +109,8 @@ public function testReindexEntitiesForConfigurableProduct() $productRepository->save($product2); $statusSelect = clone $select; - $statusSelect->reset(\Zend_Db_Select::COLUMNS)->columns(new \Zend_Db_Expr('COUNT(*)')); + $statusSelect->reset(\Magento\Framework\DB\Select::COLUMNS) + ->columns(new \Magento\Framework\DB\Sql\Expression('COUNT(*)')); $this->assertEquals(1, $connection->fetchOne($statusSelect)); } From 3e59ac99f9f103c63726dd9714eacfbc6def8e75 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Mon, 21 May 2018 16:05:17 +0300 Subject: [PATCH 125/333] MAGETWO-91934: Unlock Locales Editing when SCD on Demand Mode is Enabled - fixed static tests --- .../ElementVisibility/ConcealScdFieldTest.php | 10 +++-- .../Locale/Test/Unit/Deployed/OptionsTest.php | 45 +++++++++++++------ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php index 78780390413bb..1caa7956a4079 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php @@ -64,9 +64,13 @@ protected function setUp() * @param bool $isHidden * @dataProvider disabledDataProvider */ - public function testCheckVisibility(string $path, string $mageMode, - int $scdOnDemand, bool $isHidden, bool $isDisabled): void - { + public function testCheckVisibility( + string $path, + string $mageMode, + int $scdOnDemand, + bool $isHidden, + bool $isDisabled + ): void { $this->stateMock->expects($this->any()) ->method('getMode') ->willReturn($mageMode); diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php index 7089cfbae1815..300bb83f05d19 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/Deployed/OptionsTest.php @@ -83,9 +83,11 @@ protected function setUp() * @param string $mode * @param int $scdOnDemand * @param array $locales + * @return void + * * @dataProvider getFullLocalesDataProvider */ - public function testGetOptionLocalesFull($mode, $scdOnDemand, $locales) + public function testGetOptionLocalesFull(string $mode, int $scdOnDemand, array $locales): void { $this->localeListsMock->expects($this->once()) ->method('getOptionLocales') @@ -100,9 +102,11 @@ public function testGetOptionLocalesFull($mode, $scdOnDemand, $locales) * @param string $mode * @param int $scdOnDemand * @param array $locales + * @return void + * * @dataProvider getFullLocalesDataProvider */ - public function testGetTranslatedOptionLocalesFull($mode, $scdOnDemand, $locales) + public function testGetTranslatedOptionLocalesFull(string $mode, int $scdOnDemand, array $locales): void { $this->localeListsMock->expects($this->once()) ->method('getTranslatedOptionLocales') @@ -119,10 +123,17 @@ public function testGetTranslatedOptionLocalesFull($mode, $scdOnDemand, $locales * @param array $locales * @param array $expectedLocales * @param array $deployedCodes + * @return void + * * @dataProvider getLimitedLocalesDataProvider */ - public function testGetOptionLocalesLimited($mode, $scdOnDemand, $locales, $expectedLocales, $deployedCodes) - { + public function testGetOptionLocalesLimited( + string $mode, + int $scdOnDemand, + array $locales, + array $expectedLocales, + array $deployedCodes + ): void { $this->localeListsMock->expects($this->once()) ->method('getOptionLocales') ->willReturn($locales); @@ -138,11 +149,17 @@ public function testGetOptionLocalesLimited($mode, $scdOnDemand, $locales, $expe * @param array $locales * @param array $expectedLocales * @param array $deployedCodes + * @return void + * * @dataProvider getLimitedLocalesDataProvider */ - public function testGetTranslatedOptionLocalesLimited($mode, $scdOnDemand, $locales, - $expectedLocales, $deployedCodes) - { + public function testGetTranslatedOptionLocalesLimited( + string $mode, + int $scdOnDemand, + array $locales, + array $expectedLocales, + array $deployedCodes + ): void { $this->localeListsMock->expects($this->once()) ->method('getTranslatedOptionLocales') ->willReturn($locales); @@ -153,12 +170,12 @@ public function testGetTranslatedOptionLocalesLimited($mode, $scdOnDemand, $loca } /** - * @param $mode + * @param string $mode * @param int $scdOnDemand - * @param $deployedCodes + * @param array $deployedCodes * @return void */ - private function prepareGetLocalesLimited($mode, $scdOnDemand, $deployedCodes) + private function prepareGetLocalesLimited(string $mode, int $scdOnDemand, $deployedCodes): void { $this->stateMock->expects($this->once()) ->method('getMode') @@ -188,11 +205,11 @@ private function prepareGetLocalesLimited($mode, $scdOnDemand, $deployedCodes) } /** - * @param $mode + * @param string $mode * @param int $scdOnDemand * @return void */ - private function prepareGetLocalesFull($mode, $scdOnDemand) + private function prepareGetLocalesFull(string $mode, int $scdOnDemand): void { $this->stateMock->expects($this->once()) ->method('getMode') @@ -209,7 +226,7 @@ private function prepareGetLocalesFull($mode, $scdOnDemand) /** * @return array */ - public function getFullLocalesDataProvider() + public function getFullLocalesDataProvider(): array { $deLocale = [ 'value' => 'de_DE', @@ -252,7 +269,7 @@ public function getFullLocalesDataProvider() /** * @return array */ - public function getLimitedLocalesDataProvider() + public function getLimitedLocalesDataProvider(): array { $deLocale = [ 'value' => 'de_DE', From a1bd7291b4b2ba7bd4afc2bce5113790fd018ef9 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Mon, 21 May 2018 09:27:16 -0500 Subject: [PATCH 126/333] MAGETWO-90663: Error/Exception is thrown when user tries to delete product from /checkout/cart page after already been removed from mini-shopping cart - fix static test failures --- .../Checkout/Section/StoreFrontRemoveItemModalSection.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml index f7aecdbdd6572..e6d994fb587b0 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml @@ -1,3 +1,11 @@ +<?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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="StoreFrontRemoveItemModalSection"> From 4a6769ff0d4b1d65aa019a72c851a15a14f854f9 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Mon, 21 May 2018 10:08:09 -0500 Subject: [PATCH 127/333] MAGETWO-91440: Unassigned Attribute Values Render as N/A in Product Comparison block - fixing display issues when an attribute is empty for all products in the list to not show at all --- .../Catalog/view/frontend/templates/product/compare/list.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fbc748bd615fa..c2829eca26a83 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 @@ -116,7 +116,7 @@ <?php $block->getImage($item, 'product_small_image')->toHtml(); ?> <?php break; default: ?> - <?= /* @escapeNotVerified */ $index . '-'.$helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode()) ?> + <?= /* @escapeNotVerified */ $helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode()) ?> <?php break; } ?> </div> From e1e3fc80ecafd79f8b1af097f8f17a1356d03c87 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Mon, 21 May 2018 10:58:19 -0500 Subject: [PATCH 128/333] ENGCOM-1635: GraphQL - Added sort by options to Products GraphQL type #12 --- app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php index 70c3f1c00b27f..cc791ce780c14 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php @@ -123,10 +123,9 @@ public function resolve( 'items' => $searchResult->getProductsSearchResult(), 'page_info' => [ 'page_size' => $searchCriteria->getPageSize(), - 'current_page' => $currentPage, - 'sort_fields' => [], + 'current_page' => $currentPage ], - 'filters' => $this->filtersDataProvider->getData($layerType), + 'filters' => $this->filtersDataProvider->getData($layerType) ]; $result = function () use ($data) { From aae7fbcfd40b9acd97b12691a7c66e5018dc2f5d Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <okorshenko@magento.com> Date: Mon, 21 May 2018 12:09:31 -0500 Subject: [PATCH 129/333] Fixed typo in MagentoUi abstract.js - used singular possessive --- app/code/Magento/Ui/view/base/web/js/form/element/abstract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index a6fe0159fc785..5177b4a378d69 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -220,7 +220,7 @@ define([ }, /** - * Sets 'value' as 'hidden' properties value, triggers 'toggle' event, + * Sets 'value' as 'hidden' property's value, triggers 'toggle' event, * sets instance's hidden identifier in params storage based on * 'value'. * From bc7da05de8f529ea3c193860ae22e73b28ef4f47 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Mon, 21 May 2018 14:02:34 -0500 Subject: [PATCH 130/333] MC-90: Admin should be able to create a cart price rule for a specific coupon code --- .../SalesRule/Data/SalesCouponData.xml | 3 + .../Page/AdminCartPriceRulesPage.xml | 14 +++++ .../AdminCartPriceRulesFormSection.xml | 26 ++++++++ .../Section/AdminCartPriceRulesSection.xml | 18 ++++++ ...inCreateCartPriceRuleForCouponCodeTest.xml | 60 +++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Page/AdminCartPriceRulesPage.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Data/SalesCouponData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Data/SalesCouponData.xml index c216bb10242b4..4d93ea208fc3a 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Data/SalesCouponData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Data/SalesCouponData.xml @@ -15,4 +15,7 @@ <data key="type">0</data> <var key="rule_id" entityType="SalesRule" entityKey="rule_id"/> </entity> + <entity name="_defaultCoupon" type="SalesRuleCoupon"> + <data key="code" unique="suffix">defaultCoupon</data> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Page/AdminCartPriceRulesPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Page/AdminCartPriceRulesPage.xml new file mode 100644 index 0000000000000..66172f4ac146d --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Page/AdminCartPriceRulesPage.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminCartPriceRulesPage" url="sales_rule/promo_quote/" area="admin" module="SalesRule"> + <section name="AdminCartPriceRulesSection"/> + <section name="AdminCartPriceRulesFormSection"/> + </page> +</pages> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml new file mode 100644 index 0000000000000..fe845aad5857b --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminCartPriceRulesFormSection"> + <element name="save" type="button" selector="#save" timeout="30"/> + + <!-- Rule Information (the main form on the page) --> + <element name="ruleInformationHeader" type="button" selector="div[data-index='rule_information']" timeout="30"/> + <element name="ruleName" type="input" selector="input[name='name']"/> + <element name="websites" type="multiselect" selector="select[name='website_ids']"/> + <element name="customerGroups" type="multiselect" selector="select[name='customer_group_ids']"/> + <element name="coupon" type="select" selector="select[name='coupon_type']"/> + <element name="couponCode" type="input" selector="input[name='coupon_code']"/> + + <!-- Actions sub-form --> + <element name="actionsHeader" type="button" selector="div[data-index='actions']" timeout="30"/> + <element name="apply" type="select" selector="select[name='simple_action']"/> + <element name="discountAmount" type="input" selector="input[name='discount_amount']"/> + </section> +</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesSection.xml new file mode 100644 index 0000000000000..f96fd5dd7f4c6 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesSection.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminCartPriceRulesSection"> + <element name="addNewRuleButton" type="button" selector="#add" timeout="30"/> + <element name="messages" type="text" selector=".messages"/> + <element name="filterByNameInput" type="input" selector="input[name='name']"/> + <element name="searchButton" type="button" selector="#promo_quote_grid button[title='Search']" timeout="30"/> + <element name="nameColumns" type="text" selector="td[data-column='name']"/> + <element name="rowContainingText" type="text" selector="//*[@id='promo_quote_grid_table']/tbody/tr[td//text()[contains(., '{{var1}}')]]" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml new file mode 100644 index 0000000000000..68ae0c32b1ab5 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminCreateCartPriceRuleForCouponCodeTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Admin should be able to create a cart price rule for a specific coupon code"/> + <description value="Admin should be able to create a cart price rule for a specific coupon code"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-90"/> + <group value="SalesRule"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a cart price rule based on a coupon code --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" 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"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="5" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + + <!-- Verify initial successful save --> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + <fillField selector="{{AdminCartPriceRulesSection.filterByNameInput}}" userInput="{{_defaultCoupon.code}}" stepKey="filterByName"/> + <click selector="{{AdminCartPriceRulesSection.searchButton}}" stepKey="doFilter"/> + <see selector="{{AdminCartPriceRulesSection.nameColumns}}" userInput="{{_defaultCoupon.code}}" stepKey="seeRuleInResults"/> + + <!-- Verify further on the Rule's edit page --> + <click selector="{{AdminCartPriceRulesSection.rowContainingText(_defaultCoupon.code)}}" stepKey="goToEditRule"/> + <seeInField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="seeRuleName"/> + <seeOptionIsSelected selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="seeWebsites"/> + <seeOptionIsSelected selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="seeCustomerGroup"/> + <seeOptionIsSelected selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="seeCouponType"/> + <seeInField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{_defaultCoupon.code}}" stepKey="seeCouponCode"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions2"/> + <seeOptionIsSelected selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="seeActionType"/> + <seeInField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="5" stepKey="seeDiscountAmount"/> + </test> +</tests> From f275a21d0dc1d4e28e5add7be50d6ae1d992fe3b Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Mon, 21 May 2018 14:06:14 -0500 Subject: [PATCH 131/333] MAGETWO-91440: Unassigned Attribute Values Render as N/A in Product Comparison block - adding tests --- ...tProductsCompareWithEmptyAttributeTest.xml | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml new file mode 100644 index 0000000000000..38872c4c58dcf --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontProductsCompareWithEmptyAttributeTest"> + <annotations> + <title value="Product attribute is not visible on product compare page if it is empty"/> + <description value="Product attribute should not be visible on the product compare page if it is empty for all products that are being compared, not even displayed as N/A"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-91960"/> + <group value="productCompare"/> + </annotations> + <before> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createProductAttribute"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> + <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/> + <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> + <dragAndDrop selector1="{{UnassignedAttributes.ProductAttributeName('testattribute')}}" selector2="{{Group.FolderName('Product Details')}}" stepKey="moveProductAttributeToGroup"/> + <click selector="{{AttributeSetSection.Save}}" stepKey="saveAttributeSet"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear" /> + <seeElement selector=".message-success" stepKey="assertSuccess"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> + <amOnPage url="{{StorefrontProductPage.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="goProductPageOnStorefront1"/> + <!-- View Simple Product 1 --> + <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="goProductPageOnStorefront1"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" after="browseClickCategorySimpleProduct1View"/> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <amOnPage url="{{StorefrontProductPage.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="goProductPageOnStorefront2"/> + <!-- View Simple Product 2 --> + <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" after="goProductPageOnStorefront2"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View" after="commentViewSimpleProduct2"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2Viewloaded" after="browseClickCategorySimpleProduct2View"/> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + <amOnPage url="{{StorefrontProductPage.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="goProductPageOnStorefront3"/> + <!-- Check products in comparison sidebar --> + <!-- Check simple product 1 in comparison sidebar --> + <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="goProductPageOnStorefront3"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- Check simple product2 in comparison sidebar --> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar" after="compareSimpleProduct1InSidebar"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- Check products on comparison page --> + <!-- Check simple product 1 on comparison page --> + <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage"/> + <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison" after="compareOpenComparePage"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <seeElement selector="//table[@id='product-comparison']/tbody/tr/th/*[contains(text(),'SKU')]" stepKey="seeCompareAttribute1"/> + <dontSeeElement selector="//table[@id='product-comparison']/tbody/tr/th/*[contains(text(),'testattribute')]" stepKey="seeCompareAttribute2"/> + </test> +</tests> From 8937697f5f6c3a74acf68ff20c21d70ffa0752a9 Mon Sep 17 00:00:00 2001 From: Facundo Capua <facundocapua@gmail.com> Date: Mon, 21 May 2018 16:18:21 -0300 Subject: [PATCH 132/333] Include 'products' in category query --- .../Model/Resolver/Category/Products.php | 106 ++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 11 ++ .../Magento/GraphQl/Catalog/CategoryTest.php | 160 ++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php new file mode 100644 index 0000000000000..4575f06c4fc83 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php @@ -0,0 +1,106 @@ +<?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\Api\ProductRepositoryInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder; +use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; + +class Products + implements ResolverInterface +{ + /** @var \Magento\Catalog\Api\ProductRepositoryInterface */ + private $productRepository; + + /** @var Builder */ + private $searchCriteriaBuilder; + + /** @var Filter */ + private $filterQuery; + + /** @var ValueFactory */ + private $valueFactory; + + /** + * @param ProductRepositoryInterface $productRepository + * @param Builder $searchCriteriaBuilder + * @param Filter $filterQuery + * @param ValueFactory $valueFactory + */ + public function __construct( + ProductRepositoryInterface $productRepository, + Builder $searchCriteriaBuilder, + Filter $filterQuery, + ValueFactory $valueFactory + ) { + $this->productRepository = $productRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->filterQuery = $filterQuery; + $this->valueFactory = $valueFactory; + } + + /** + * {@inheritdoc} + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ): Value { + $args['filter'] = [ + 'category_ids' => [ + 'eq' => $value['id'] + ] + ]; + $searchCriteria = $this->searchCriteriaBuilder->build($field->getName(), $args); + $searchCriteria->setCurrentPage($args['currentPage']); + $searchCriteria->setPageSize($args['pageSize']); + $searchResult = $this->filterQuery->getResult($searchCriteria, $info); + + //possible division by 0 + if ($searchCriteria->getPageSize()) { + $maxPages = ceil($searchResult->getTotalCount() / $searchCriteria->getPageSize()); + } else { + $maxPages = 0; + } + + $currentPage = $searchCriteria->getCurrentPage(); + if ($searchCriteria->getCurrentPage() > $maxPages && $searchResult->getTotalCount() > 0) { + $currentPage = new GraphQlInputException( + __( + 'currentPage value %1 specified is greater than the number of pages available.', + [$maxPages] + ) + ); + } + + $data = [ + 'total_count' => $searchResult->getTotalCount(), + 'items' => $searchResult->getProductsSearchResult(), + 'page_info' => [ + 'page_size' => $searchCriteria->getPageSize(), + 'current_page' => $currentPage + ] + ]; + + $result = function () use ($data) { + return $data; + }; + + return $this->valueFactory->create($result); + } + +} \ No newline at end of file diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index ca1ff78654319..64e56a738bbf6 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -375,6 +375,11 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model updated_at: String @doc(description: "Timestamp indicating when the category was updated") product_count: Int @doc(description: "The number of products in the category") default_sort_by: String @doc(description: "The attribute to use for sorting") + products( + pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), + currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), + sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") + ): CategoryProducts @doc(description: "The list of products assigned to the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products") } type CustomizableRadioOption implements CustomizableOptionInterface @doc(description: "CustomizableRadioOption contains information about a set of radio buttons that are defined as part of a customizable option") { @@ -404,6 +409,12 @@ type Products @doc(description: "The Products object is the top-level object ret filters: [LayerFilter] @doc(description: "Layered navigation filters array") } +type CategoryProducts @doc(description: "The category products object returned in the Category query") { + items: [ProductInterface] @doc(description: "An array of products that match the specified search criteria") + page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query") + total_count: Int @doc(description: "The number of products returned") +} + input ProductFilterInput @doc(description: "ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { name: FilterTypeInput @doc(description: "The product name. Customers use this name to identify the product.") sku: FilterTypeInput @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index eb138d738ea10..78a7aa8bed63a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -9,6 +9,7 @@ use Magento\Framework\DataObject; use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Catalog\Api\ProductRepositoryInterface; class CategoryTest extends GraphQlAbstract { @@ -109,4 +110,163 @@ public function testCategoriesTree() $responseDataObject->getData('category/children/7/children/1/children/0/id') ); } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCategoryProducts() + { + $categoryId = 4; + $query = <<<QUERY +{ + category(id: {$categoryId}) { + products { + total_count + page_info { + current_page + page_size + } + items { + attribute_set_id + country_of_manufacture + created_at + description + gift_message_available + id + categories { + name + url_path + available_sort_by + level + } + image + image_label + meta_description + meta_keyword + meta_title + media_gallery_entries { + disabled + file + id + label + media_type + position + types + content { + base64_encoded_data + type + name + } + video_content { + media_type + video_description + video_metadata + video_provider + video_title + video_url + } + } + name + new_from_date + new_to_date + options_container + + price { + minimalPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + maximalPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + regularPrice { + amount { + value + currency + } + adjustments { + amount { + value + currency + } + code + description + } + } + } + product_links { + link_type + linked_product_sku + linked_product_type + position + sku + } + short_description + sku + small_image + small_image_label + special_from_date + special_price + special_to_date + swatch_image + tax_class_id + thumbnail + thumbnail_label + tier_price + tier_prices { + customer_group_id + percentage_value + qty + value + website_id + } + type_id + updated_at + url_key + url_path + websites { + id + name + code + sort_order + default_group_id + is_default + } + + } + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('products', $response['category']); + $this->assertArrayHasKey('total_count', $response['category']['products']); + $this->assertEquals(2, $response['category']['products']['total_count']); + $this->assertEquals(1, $response['category']['products']['page_info']['current_page']); + $this->assertEquals(20, $response['category']['products']['page_info']['page_size']); + } } From bd521971f40e5319d5f7d9e8a1b7e785f19810c3 Mon Sep 17 00:00:00 2001 From: pdohogne-magento <pdohogne@magento.com> Date: Fri, 18 May 2018 14:39:12 -0500 Subject: [PATCH 133/333] MAGETWO-87492: Adding a script to run 2.3 upgrade steps that need to be done before composer update / setup:upgrade --- bin/UpgradeScripts/2.3_upgrade_additions.php | 251 +++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 bin/UpgradeScripts/2.3_upgrade_additions.php diff --git a/bin/UpgradeScripts/2.3_upgrade_additions.php b/bin/UpgradeScripts/2.3_upgrade_additions.php new file mode 100644 index 0000000000000..1658c8a4e6681 --- /dev/null +++ b/bin/UpgradeScripts/2.3_upgrade_additions.php @@ -0,0 +1,251 @@ +#!/usr/bin/php +<?php +/** + * Script for updating Magento with 2.3 requirements that can't be done by composer update or setup:upgrade + * + * Steps included: + * - Require new version of the metapackage + * - Updating "require-dev" section + * - Add "Zend\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" to composer.json "autoload":"psr-4" section + * - Updating Magento/Updater if it's installed + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$_scriptName = basename(__FILE__); + +define( + 'SYNOPSIS', +<<<SYNOPSIS +Updates Magento with 2.3 requirements that can't be done by `composer update` or `bin/magento setup:upgrade`. +This should be run prior to running `composer update` or `bin/magento setup:upgrade`. + +Steps included: + - Require new version of the metapackage + - Updating "require-dev" section + - Add "Zend\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" to composer.json "autoload":"psr-4" section + - Updating Magento/Updater if it's installed + +Usage: php -f $_scriptName -- --root="</path/to/magento/root/>" [--composer=</path/to/composer/executable>] + [--edition=<community|enterprise>] [--version=<magento_package_version>] [--repo=<composer_repo_url>] + [--help] + +Required: + --root="</path/to/magento/root/>" + Path to the Magento installation root directory + +Optional: + --composer="</path/to/composer/executable>" + Path to the composer executable + - Default: The composer found in PATH + + --edition=<community|enterprise> + The Magento edition to upgrade to. Open Source = 'community', Commerce = 'enterprise' + - Default: The edition currently required in composer.json + + --version=<magento_package_version> + The Magento version to upgrade to + - Default: The value for the "version" field in composer.json + + --repo=<composer_repo_url> + The Magento composer repository to pull new packages from + - Default: The Magento repository configured in composer.json + + --help + Display this message +SYNOPSIS +); + + +$opts = getopt('', [ + 'root:', + 'composer:', + 'edition:', + 'version:', + 'repo:', + 'help' +]); + +if (isset($opts['help'])) { + echo SYNOPSIS . PHP_EOL; + exit(0); +} + +try { + if (empty($opts['root'])) { + throw new BadMethodCallException("Magento root must be given with '--root'" . PHP_EOL . PHP_EOL . SYNOPSIS); + } + + $rootDir = $opts['root']; + if (!is_dir($rootDir)) { + throw new InvalidArgumentException("Magento root directory '$rootDir' does not exist"); + } + + $cmd = (!empty($opts['composer']) ? $opts['composer'] : 'composer') . " --working-dir='$rootDir'"; + $jsonData = json_decode(file_get_contents("$rootDir/composer.json"), true); + + $version = !empty($opts['version']) ? $opts['version'] : $jsonData['version']; + if (empty($version)) { + throw new InvalidArgumentException('Value not found for "version" field in composer.json'); + } + + if (!empty($opts['edition'])) { + $edition = $opts['edition']; + } + else { + $editionRegex = '|^magento/product\-(?<edition>[a-z]+)\-edition$|'; + + foreach (array_keys($jsonData["require"]) as $requiredPackage) { + if (preg_match($editionRegex, $requiredPackage, $matches)) { + $edition = $matches['edition']; + break; + } + } + if (empty($edition)) { + throw new InvalidArgumentException('No valid Magento edition found in composer.json requirements'); + } + } + + echo "Backing up $rootDir/composer.json" . PHP_EOL; + copy("$rootDir/composer.json", "$rootDir/composer.json.bak"); + + echo "Updating Magento product requirement to magento/product-$edition-edition=$version" . PHP_EOL; + if ($edition == "enterprise") { + execVerbose("$cmd remove --verbose magento/product-community-edition --no-update"); + } + execVerbose("$cmd require --verbose magento/product-$edition-edition=$version --no-update"); + + echo 'Updating "require-dev" section of composer.json' . PHP_EOL; + execVerbose("$cmd require --dev --verbose " . + "phpunit/phpunit:~6.2.0 " . + "friendsofphp/php-cs-fixer:~2.10.1 " . + "lusitanian/oauth:~0.8.10 " . + "pdepend/pdepend:2.5.2 " . + "sebastian/phpcpd:~3.0.0 " . + "squizlabs/php_codesniffer:3.2.2 --no-update"); + + execVerbose("$cmd remove --dev --verbose sjparkinson/static-review fabpot/php-cs-fixer --no-update"); + + echo 'Adding "Zend\\\\Mvc\\\\Controller\\\\": "setup/src/Zend/Mvc/Controller/" to "autoload":"psr-4"' . PHP_EOL; + $jsonData = json_decode(file_get_contents("$rootDir/composer.json"), true); + $jsonData["autoload"]["psr-4"]["Zend\\Mvc\\Controller\\"] = "setup/src/Zend/Mvc/Controller/"; + + $jsonData["version"] = $version; + file_put_contents("$rootDir/composer.json", json_encode($jsonData, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)); + + if (file_exists("$rootDir/update")) { + echo "Replacing Magento/Updater" . PHP_EOL; + + $mageUrls = []; + if (isset($opts['repo'])) { + $mageUrls[] = $opts['repo']; + } + else { + $composerUrls = array_map(function ($r) { return $r["url"]; }, + array_filter($jsonData['repositories']), function ($r) { return $r["type"] == "composer"; }); + $mageUrls = array_filter($composerUrls, function($u) { return strpos($u, ".mage") !== false; }); + + if (count($mageUrls) == 0) { + throw new InvalidArgumentException('No Magento composer repository urls found in composer.json'); + } + } + + echo "Backing up $rootDir/update" . PHP_EOL; + rename("$rootDir/update", "$rootDir/update.bak"); + $newPackage = "magento/project-$edition-edition=$version"; + foreach ($mageUrls as $repoUrl) { + try { + deleteFilepath("$rootDir/temp_update"); + execVerbose("$cmd create-project --repository=$repoUrl $newPackage $rootDir/temp_update --no-install"); + rename("$rootDir/temp_update/update", "$rootDir/update"); + echo "Upgraded Magento/Updater from magento/project-$edition-edition $version on $repoUrl" . PHP_EOL; + unset($exception); + break; + } + catch (Exception $e) { + echo "Failed to find Magento package on $repoUrl" . PHP_EOL; + $exception = $e; + } + } + deleteFilepath("$rootDir/temp_update"); + + if (isset($exception)) { + throw $exception; + } + } +} catch (Exception $e) { + if ($e->getPrevious()) { + $message = (string)$e->getPrevious(); + } else { + $message = $e->getMessage(); + } + + try { + error_log($message . PHP_EOL . PHP_EOL . "Error encountered; resetting backups" . PHP_EOL); + if (file_exists("$rootDir/update.bak")) { + deleteFilepath("$rootDir/update_temp"); + deleteFilepath("$rootDir/update"); + rename("$rootDir/update.bak", "$rootDir/update"); + } + + if (file_exists("$rootDir/composer.json.bak")) { + deleteFilepath("$rootDir/composer.json"); + rename("$rootDir/composer.json.bak", "$rootDir/composer.json"); + } + } + catch (Exception $e) { + error_log($e->getMessage() . PHP_EOL); + } + + exit($e->getCode() == 0 ? 1 : $e->getCode()); +} + +/** + * Execute a command with automatic escaping of arguments + * + * @param string $command + * @return array + * @throws Exception + */ +function execVerbose($command) +{ + $args = func_get_args(); + $args = array_map('escapeshellarg', $args); + $args[0] = $command; + $command = call_user_func_array('sprintf', $args); + echo $command . PHP_EOL; + exec($command . " 2>&1", $output, $exitCode); + $outputString = join(PHP_EOL, $output); + if (0 !== $exitCode) { + throw new Exception($outputString, $exitCode); + } + echo $outputString . PHP_EOL; + return $output; +} + +/** + * Deletes a file or a directory and all its contents + * + * @param string $path + * @throws Exception + */ +function deleteFilepath($path) { + if (!file_exists($path)) { + return; + } + if (is_dir($path)) { + $files = array_diff(scandir($path), array('..', '.')); + foreach ($files as $file) { + deleteFilepath("$path/$file"); + } + rmdir($path); + } + else { + unlink($path); + } + if (file_exists($path)) { + throw new Exception("Failed to delete $path"); + } +} From 8753c5b24dc3fe5edd49ca9113a282f7120baad7 Mon Sep 17 00:00:00 2001 From: Facundo Capua <facundocapua@gmail.com> Date: Mon, 21 May 2018 16:33:29 -0300 Subject: [PATCH 134/333] Fix code style issues --- .../CatalogGraphQl/Model/Resolver/Category/Products.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php index 4575f06c4fc83..baa8f0c8bc5d3 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php @@ -17,8 +17,7 @@ use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -class Products - implements ResolverInterface +class Products implements ResolverInterface { /** @var \Magento\Catalog\Api\ProductRepositoryInterface */ private $productRepository; @@ -102,5 +101,4 @@ public function resolve( return $this->valueFactory->create($result); } - -} \ No newline at end of file +} From 5bbe2c79d09e550c57250f17d58a886b728a14c1 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Mon, 21 May 2018 15:05:27 -0500 Subject: [PATCH 135/333] MC-86: Admin should be able to remove a configuration from a Configurable Product - Add severity --- .../Test/AdminConfigurableProductUpdateTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml index 26316dda3ad26..028c2d7444e63 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml @@ -253,6 +253,7 @@ <stories value="Edit a configurable product in admin"/> <title value="Admin should be able to remove a configuration from a Configurable Product"/> <description value="Admin should be able to remove a configuration from a Configurable Product"/> + <severity value="AVERAGE"/> <testCaseId value="MC-86"/> <group value="ConfigurableProduct"/> </annotations> From 426940e175e26b9aad5179c49b92abf7e546d4f6 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Mon, 21 May 2018 15:08:29 -0500 Subject: [PATCH 136/333] MC-95: Admin should be able to edit configuration to add a value to an existing attribute - Add severity tag --- .../Test/AdminConfigurableProductUpdateTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml index 4aca40b761fd1..e56e502a20dc8 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateTest.xml @@ -253,6 +253,7 @@ <stories value="Edit a configurable product in admin"/> <title value="Admin should be able to edit configuration to add a value to an existing attribute"/> <description value="Admin should be able to edit configuration to add a value to an existing attribute"/> + <severity value="AVERAGE"/> <testCaseId value="MC-95"/> <group value="ConfigurableProduct"/> </annotations> From f8ca650b6e13ca13bd44227e1ba0e160577cb858 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Tue, 8 May 2018 16:19:59 -0500 Subject: [PATCH 137/333] MC-132: add advance catalog search for simple product tests. - search by product name - search by product sku - search by product description - search by product short description - search by price (cherry picked from commit 86e0d11) --- .../StorefrontCatalogSearchActionGroup.xml | 40 ++++++ ...AdvanceCatalogSearchSimpleProductsTest.xml | 136 ++++++++++++++++++ .../CatalogSearch/composer.json | 8 +- 3 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/ActionGroup/StorefrontCatalogSearchActionGroup.xml index 5e6bfeb448e13..fdf0564780024 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/ActionGroup/StorefrontCatalogSearchActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/ActionGroup/StorefrontCatalogSearchActionGroup.xml @@ -51,6 +51,46 @@ <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> + <!-- Storefront advanced catalog search by product name --> + <actionGroup name="StorefrontAdvancedCatalogSearchByProductNameActionGroup"> + <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"> + <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"> + <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"> + <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"> <arguments> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml new file mode 100644 index 0000000000000..7ea0880aceb2b --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for simple products"/> + <title value="Advanced catalog search for simple products by product name"/> + <description value="Advanced catalog search for simple products by product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-132"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameActionGroup" stepKey="search"> + <argument name="name" value="$$createProductOne.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> + <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for simple products"/> + <title value="Advanced catalog search for simple products by product sku"/> + <description value="Advanced catalog search for simple products by product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-133"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductSkuActionGroup" stepKey="search"> + <argument name="sku" value="$$createProductOne.sku$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> + + <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for simple products"/> + <title value="Advanced catalog search for simple products by product description"/> + <description value="Advanced catalog search for simple products by product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-134"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByDescriptionActionGroup" stepKey="search"> + <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> + + <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for simple products"/> + <title value="Advanced catalog search for simple products by product short description"/> + <description value="Advanced catalog search for simple products by product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-135"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup" stepKey="search"> + <argument name="shortDescription" value="$$createProductOne.custom_attributes[short_description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> + + <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for simple products"/> + <title value="Advanced catalog search for simple products by product price"/> + <description value="Advanced catalog search for simple products by price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-136"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="search"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="priceFrom" value="$$createProductOne.price$$"/> + <argument name="priceTo" value="$$createProductOne.price$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/composer.json b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/composer.json index 8446dd8fba446..65e7b9c781e61 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/composer.json +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/composer.json @@ -12,19 +12,19 @@ }, "require": { "magento/magento2-functional-testing-framework": "1.0.0", + "magento/magento2-functional-test-module-backend": "100.0.0-dev", "magento/magento2-functional-test-module-catalog": "100.0.0-dev", + "magento/magento2-functional-test-module-eav": "100.0.0-dev", "magento/magento2-functional-test-module-search": "100.0.0-dev", "magento/magento2-functional-test-module-theme": "100.0.0-dev", "magento/magento2-functional-test-module-ui": "100.0.0-dev", "magento/magento2-functional-test-module-store": "100.0.0-dev", + "magento/magento2-functional-test-module-catalog-inventory": "100.0.0-dev", "php": "~7.1.3||~7.2.0" }, "suggest": { - "magento/magento2-functional-test-module-backend": "100.0.0-dev", - "magento/magento2-functional-test-module-catalog-inventory": "100.0.0-dev", "magento/magento2-functional-test-module-customer": "100.0.0-dev", - "magento/magento2-functional-test-module-directory": "100.0.0-dev", - "magento/magento2-functional-test-module-eav": "100.0.0-dev" + "magento/magento2-functional-test-module-directory": "100.0.0-dev" }, "autoload": { "psr-4": { From b4f59cd10d5df240fbc282d8ec7a001422af4062 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Thu, 10 May 2018 11:11:04 -0500 Subject: [PATCH 138/333] MC-137: add advance catalog search for virtual product tests. - search by product name - search by product sku - search by product description - search by product short description - search by price (cherry picked from commit 063899d) --- .../Catalog/Data/ProductData.xml | 14 ++ ...dvanceCatalogSearchVirtualProductsTest.xml | 136 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml index 70e15717142d9..06d4ea8569dfc 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml @@ -239,4 +239,18 @@ <requiredEntity type="product_option">ProductOptionDateTime</requiredEntity> <requiredEntity type="product_option">ProductOptionTime</requiredEntity> </entity> + <entity name="ApiVirtualProductWithDescription" type="product"> + <data key="sku" unique="suffix">api-virtual-product</data> + <data key="type_id">virtual</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">Api Virtual Product</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-virtual-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml new file mode 100644 index 0000000000000..0b3f2031205f2 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdvanceCatalogSearchVirtualProductByNameTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for virtual products"/> + <title value="Advanced catalog search for virtual products by product name"/> + <description value="Advanced catalog search for virtual products by product name"/> + <severity value="MAJOR"/> + <testCaseId value="MC-137"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameActionGroup" stepKey="search"> + <argument name="name" value="$$createProductOne.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> + <test name="AdvanceCatalogSearchVirtualProductBySkuTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for virtual products"/> + <title value="Advanced catalog search for virtual products by product sku"/> + <description value="Advanced catalog search for virtual products by product sku"/> + <severity value="MAJOR"/> + <testCaseId value="MC-162"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductSkuActionGroup" stepKey="search"> + <argument name="sku" value="$$createProductOne.sku$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> + + <test name="AdvanceCatalogSearchVirtualProductByDescriptionTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for virtual products"/> + <title value="Advanced catalog search for virtual products by product description"/> + <description value="Advanced catalog search for virtual products by product description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-163"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByDescriptionActionGroup" stepKey="search"> + <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> + + <test name="AdvanceCatalogSearchVirtualProductByShortDescriptionTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for virtual products"/> + <title value="Advanced catalog search for virtual products by product short description"/> + <description value="Advanced catalog search for virtual products by product short description"/> + <severity value="MAJOR"/> + <testCaseId value="MC-164"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup" stepKey="search"> + <argument name="shortDescription" value="$$createProductOne.custom_attributes[short_description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> + + <test name="AdvanceCatalogSearchVirtualProductByPriceTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Advanced catalog search for virtual products"/> + <title value="Advanced catalog search for virtual products by product price"/> + <description value="Advanced catalog search for virtual products by price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-165"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="createProductOne"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + </after> + + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="search"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="priceFrom" value="$$createProductOne.price$$"/> + <argument name="priceTo" value="$$createProductOne.price$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + </test> +</tests> From 2267f37663b3eae78cd938a3f66dd05bba787722 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Mon, 14 May 2018 11:03:44 -0500 Subject: [PATCH 139/333] MC-132: add advance catalog search for simple product tests. - search by product name - search by product sku - search by product description - search by product short description - search by price (cherry picked from commit 8adb74c) --- ...CatalogSearchAdvancedResultMainSection.xml | 1 + ...AdvanceCatalogSearchSimpleProductsTest.xml | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml index f211561177da4..bf143d5f4b8b5 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml @@ -16,5 +16,6 @@ <element name="productCount" type="text" selector="#toolbar-amount"/> <element name="message" type="text" selector="div.message div"/> <element name="itemFound" type="text" selector=".search.found>strong"/> + <element name="productName" type="text" selector=".product.name.product-item-name>a"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml index 7ea0880aceb2b..6bc9ed42ef13d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml @@ -12,8 +12,8 @@ <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for simple products"/> - <title value="Advanced catalog search for simple products by product name"/> - <description value="Advanced catalog search for simple products by product name"/> + <title value="Guest customer should be able to find simple products by product name in advanced catalog search"/> + <description value="Guest customer should be able to find simple products by product name in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-132"/> <group value="CatalogSearch"/> @@ -36,8 +36,8 @@ <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for simple products"/> - <title value="Advanced catalog search for simple products by product sku"/> - <description value="Advanced catalog search for simple products by product sku"/> + <title value="Guest customer should be able to find simple products by product sku in advanced catalog search"/> + <description value="Guest customer should be able to find simple products by product sku in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-133"/> <group value="CatalogSearch"/> @@ -55,14 +55,15 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for simple products"/> - <title value="Advanced catalog search for simple products by product description"/> - <description value="Advanced catalog search for simple products by product description"/> + <title value="Guest customer should be able to find simple products by product description in advanced catalog search"/> + <description value="Guest customer should be able to find simple products by product description in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-134"/> <group value="CatalogSearch"/> @@ -80,14 +81,15 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for simple products"/> - <title value="Advanced catalog search for simple products by product short description"/> - <description value="Advanced catalog search for simple products by product short description"/> + <title value="Guest customer should be able to find simple products by product short description in advanced catalog search"/> + <description value="Guest customer should be able to find simple products by product short description in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-135"/> <group value="CatalogSearch"/> @@ -105,14 +107,15 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for simple products"/> - <title value="Advanced catalog search for simple products by product price"/> - <description value="Advanced catalog search for simple products by price"/> + <title value="Guest customer should be able to find simple products by product price in advanced catalog search"/> + <description value="Guest customer should be able to find simple products by product price in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-136"/> <group value="CatalogSearch"/> @@ -132,5 +135,6 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> </tests> From 9751bdf685a33aafdf91848e9f734213be73a4f4 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Mon, 14 May 2018 11:04:27 -0500 Subject: [PATCH 140/333] MC-137: add advance catalog search for virtual product tests. - search by product name - search by product sku - search by product description - search by product short description - search by price (cherry picked from commit a4d72c2) --- ...dvanceCatalogSearchVirtualProductsTest.xml | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml index 0b3f2031205f2..ad8ca7dc81bb9 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml @@ -12,8 +12,8 @@ <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for virtual products"/> - <title value="Advanced catalog search for virtual products by product name"/> - <description value="Advanced catalog search for virtual products by product name"/> + <title value="Guest customer should be able to find virtual products by product name in advanced catalog search"/> + <description value="Guest customer should be able to find virtual products by product name in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-137"/> <group value="CatalogSearch"/> @@ -31,13 +31,14 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> <test name="AdvanceCatalogSearchVirtualProductBySkuTest"> <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for virtual products"/> - <title value="Advanced catalog search for virtual products by product sku"/> - <description value="Advanced catalog search for virtual products by product sku"/> + <title value="Guest customer should be able to find virtual products by product sku in advanced catalog search"/> + <description value="Guest customer should be able to find virtual products by product sku in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-162"/> <group value="CatalogSearch"/> @@ -55,14 +56,15 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> <test name="AdvanceCatalogSearchVirtualProductByDescriptionTest"> <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for virtual products"/> - <title value="Advanced catalog search for virtual products by product description"/> - <description value="Advanced catalog search for virtual products by product description"/> + <title value="Guest customer should be able to find virtual products by product description in advanced catalog search"/> + <description value="Guest customer should be able to find virtual products by product description in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-163"/> <group value="CatalogSearch"/> @@ -80,14 +82,15 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> <test name="AdvanceCatalogSearchVirtualProductByShortDescriptionTest"> <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for virtual products"/> - <title value="Advanced catalog search for virtual products by product short description"/> - <description value="Advanced catalog search for virtual products by product short description"/> + <title value="Guest customer should be able to find virtual products by product short description in advanced catalog search"/> + <description value="Guest customer should be able to find virtual products by product short description in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-164"/> <group value="CatalogSearch"/> @@ -105,14 +108,15 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> <test name="AdvanceCatalogSearchVirtualProductByPriceTest"> <annotations> <features value="CatalogSearch"/> <stories value="Advanced catalog search for virtual products"/> - <title value="Advanced catalog search for virtual products by product price"/> - <description value="Advanced catalog search for virtual products by price"/> + <title value="Guest customer should be able to find virtual products by product price in advanced catalog search"/> + <description value="Guest customer should be able to find virtual products by product price in advanced catalog search"/> <severity value="MAJOR"/> <testCaseId value="MC-165"/> <group value="CatalogSearch"/> @@ -132,5 +136,6 @@ </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProductOne.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> </test> </tests> From 8ff66ecb87ff6a1b897d2e8be004f8258188f0bf Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@magento.com> Date: Thu, 17 May 2018 11:23:38 -0500 Subject: [PATCH 141/333] MC-132: add advance catalog search for simple product tests. - search by product name - search by product sku - search by product description - search by product short description - search by price (cherry picked from commit c004119) --- ...AdvanceCatalogSearchSimpleProductsTest.xml | 30 +++++++++---------- ...dvanceCatalogSearchVirtualProductsTest.xml | 30 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml index 6bc9ed42ef13d..f52a86810842e 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml @@ -11,9 +11,9 @@ <test name="AdvanceCatalogSearchSimpleProductByNameTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for simple products"/> - <title value="Guest customer should be able to find simple products by product name in advanced catalog search"/> - <description value="Guest customer should be able to find simple products by product name in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product name"/> + <description value="Guest customer should be able to advance search simple product with product name"/> <severity value="MAJOR"/> <testCaseId value="MC-132"/> <group value="CatalogSearch"/> @@ -35,9 +35,9 @@ <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for simple products"/> - <title value="Guest customer should be able to find simple products by product sku in advanced catalog search"/> - <description value="Guest customer should be able to find simple products by product sku in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product sku"/> + <description value="Guest customer should be able to advance search simple product with product sku"/> <severity value="MAJOR"/> <testCaseId value="MC-133"/> <group value="CatalogSearch"/> @@ -61,9 +61,9 @@ <test name="AdvanceCatalogSearchSimpleProductByDescriptionTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for simple products"/> - <title value="Guest customer should be able to find simple products by product description in advanced catalog search"/> - <description value="Guest customer should be able to find simple products by product description in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product description"/> + <description value="Guest customer should be able to advance search simple product with product description"/> <severity value="MAJOR"/> <testCaseId value="MC-134"/> <group value="CatalogSearch"/> @@ -87,9 +87,9 @@ <test name="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for simple products"/> - <title value="Guest customer should be able to find simple products by product short description in advanced catalog search"/> - <description value="Guest customer should be able to find simple products by product short description in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product short description"/> + <description value="Guest customer should be able to advance search simple product with product short description"/> <severity value="MAJOR"/> <testCaseId value="MC-135"/> <group value="CatalogSearch"/> @@ -113,9 +113,9 @@ <test name="AdvanceCatalogSearchSimpleProductByPriceTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for simple products"/> - <title value="Guest customer should be able to find simple products by product price in advanced catalog search"/> - <description value="Guest customer should be able to find simple products by product price in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product price"/> + <description value="Guest customer should be able to advance search simple product with product price"/> <severity value="MAJOR"/> <testCaseId value="MC-136"/> <group value="CatalogSearch"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml index ad8ca7dc81bb9..80b1b294fecd5 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml @@ -11,9 +11,9 @@ <test name="AdvanceCatalogSearchVirtualProductByNameTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for virtual products"/> - <title value="Guest customer should be able to find virtual products by product name in advanced catalog search"/> - <description value="Guest customer should be able to find virtual products by product name in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product name"/> + <description value="Guest customer should be able to advance search virtual product with product name"/> <severity value="MAJOR"/> <testCaseId value="MC-137"/> <group value="CatalogSearch"/> @@ -36,9 +36,9 @@ <test name="AdvanceCatalogSearchVirtualProductBySkuTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for virtual products"/> - <title value="Guest customer should be able to find virtual products by product sku in advanced catalog search"/> - <description value="Guest customer should be able to find virtual products by product sku in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product sku"/> + <description value="Guest customer should be able to advance search virtual product with product sku"/> <severity value="MAJOR"/> <testCaseId value="MC-162"/> <group value="CatalogSearch"/> @@ -62,9 +62,9 @@ <test name="AdvanceCatalogSearchVirtualProductByDescriptionTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for virtual products"/> - <title value="Guest customer should be able to find virtual products by product description in advanced catalog search"/> - <description value="Guest customer should be able to find virtual products by product description in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product description"/> + <description value="Guest customer should be able to advance search virtual product with product description"/> <severity value="MAJOR"/> <testCaseId value="MC-163"/> <group value="CatalogSearch"/> @@ -88,9 +88,9 @@ <test name="AdvanceCatalogSearchVirtualProductByShortDescriptionTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for virtual products"/> - <title value="Guest customer should be able to find virtual products by product short description in advanced catalog search"/> - <description value="Guest customer should be able to find virtual products by product short description in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product short description"/> + <description value="Guest customer should be able to advance search virtual product with product short description"/> <severity value="MAJOR"/> <testCaseId value="MC-164"/> <group value="CatalogSearch"/> @@ -114,9 +114,9 @@ <test name="AdvanceCatalogSearchVirtualProductByPriceTest"> <annotations> <features value="CatalogSearch"/> - <stories value="Advanced catalog search for virtual products"/> - <title value="Guest customer should be able to find virtual products by product price in advanced catalog search"/> - <description value="Guest customer should be able to find virtual products by product price in advanced catalog search"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product price"/> + <description value="Guest customer should be able to advance search virtual product with product price"/> <severity value="MAJOR"/> <testCaseId value="MC-165"/> <group value="CatalogSearch"/> From 8a3eb4b401f6b76caf1b2e4f4cf72f178550be92 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Mon, 21 May 2018 16:46:56 -0500 Subject: [PATCH 142/333] MAGETWO-91240: Automate MFTF --- .../ActionGroup/DisplayOutOfStockProductActionGroup.xml | 2 ++ .../CatalogInventory/Section/InventorySection.xml | 6 ++++-- .../Cms/Section/CmsNewPagePageActionsSection.xml | 2 +- .../tests/functional/Magento/FunctionalTest/PageBuilder | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) create mode 120000 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/ActionGroup/DisplayOutOfStockProductActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/ActionGroup/DisplayOutOfStockProductActionGroup.xml index d990fcc2d935b..b75cbff116761 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/ActionGroup/DisplayOutOfStockProductActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/ActionGroup/DisplayOutOfStockProductActionGroup.xml @@ -11,6 +11,8 @@ <actionGroup name="displayOutOfStockProduct"> <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> <waitForPageLoad stepKey="waitForConfigPageToLoad"/> + <conditionalClick stepKey="expandProductStockOptions" selector="{{InventoryConfigSection.ProductStockOptionsTab}}" dependentSelector="{{InventoryConfigSection.CheckIfProductStockOptionsTabExpanded}}" visible="true" /> + <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="waitForDisplayOutOfStockOption" /> <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForSwitcherDropdown" /> <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="Yes" stepKey="switchToYes" /> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Section/InventorySection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Section/InventorySection.xml index 45fea63e88e75..526d8b5730eb0 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Section/InventorySection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogInventory/Section/InventorySection.xml @@ -7,7 +7,9 @@ --> <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"> <section name="InventoryConfigSection"> - <element name="DisplayOutOfStockSystemValue" type="button" selector="#cataloginventory_options_show_out_of_stock_inherit"/> - <element name="DisplayOutOfStockDropdown" type="button" selector="#cataloginventory_options_show_out_of_stock"/> + <element name="ProductStockOptionsTab" type="button" selector="#cataloginventory_options-head"/> + <element name="CheckIfProductStockOptionsTabExpanded" type="button" selector="#cataloginventory_options-head:not(.open)"/> + <element name="DisplayOutOfStockSystemValue" type="checkbox" selector="#cataloginventory_options_show_out_of_stock_inherit"/> + <element name="DisplayOutOfStockDropdown" type="select" selector="#cataloginventory_options_show_out_of_stock"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePageActionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePageActionsSection.xml index 3ceef6d422782..16c67394b5c0b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePageActionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePageActionsSection.xml @@ -16,7 +16,7 @@ <element name="splitButtonMenu" type="button" selector="//ul[@data-ui-id='save-button-dropdown-menu']" timeout="10"/> <element name="expandSplitButton" type="button" selector="//button[@data-ui-id='save-button-dropdown']" timeout="10"/> <element name="cmsPageTitle" type="text" selector=".page-header .page-title"/> - <element name="pageTitle" type="input" selector="//*[@name='title']"/> + <element name="pageTitle" type="input" selector="input[name='title']"/> <element name="showHideEditor" type="button" selector="//*[@id='togglecms_page_form_content']"/> <element name="contentSectionName" type="input" selector="//div[@class='fieldset-wrapper-title']//span[.='Content']"/> <element name="content" type="input" selector="//textarea[@name='content']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder new file mode 120000 index 0000000000000..523b3ef8895b9 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder @@ -0,0 +1 @@ +/Users/nsmith/vagrant-magento/magento2ce/magento2-page-builder/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder \ No newline at end of file From a435536c48ac94d6fde4f98f4b1c58cffe2f9f22 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@magento.com> Date: Mon, 21 May 2018 17:44:20 -0500 Subject: [PATCH 143/333] Merge branch 'team3-delivery' of github.com:magento-trigger/magento2ce into team3-delivery --- app/code/Magento/Braintree/etc/di.xml | 2 +- .../Adminhtml/Product/Attribute/Save.php | 9 +-- .../Frontend/Inputtype/Presentation.php | 12 +-- .../Frontend/InputType/PresentationTest.php | 80 +++++++++++++++++++ .../view/frontend/web/js/shopping-cart.js | 23 +++++- .../Checkout/view/frontend/web/js/sidebar.js | 1 + .../Magento/Ui/Block/Wysiwyg/ActiveEditor.php | 6 +- .../Vault/Model/PaymentTokenFactory.php | 5 ++ ...dminEditTextEditorProductAttributeTest.xml | 2 - .../Section/CheckoutCartProductSection.xml | 2 + .../StoreFrontRemoveItemModalSection.xml | 16 ++++ .../Section/StorefrontMiniCartSection.xml | 1 + ...koutForProductsDeletedFromMiniCartTest.xml | 54 +++++++++++++ .../Section/CmsNewPagePageActionsSection.xml | 2 +- .../ActionGroup/SwitcherActionGroup.xml | 9 ++- .../Test/TestCase/ReorderUsingVaultTest.xml | 2 +- 16 files changed, 201 insertions(+), 25 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Frontend/InputType/PresentationTest.php create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml index 5f4a345760f2d..2bb4cea6742d6 100644 --- a/app/code/Magento/Braintree/etc/di.xml +++ b/app/code/Magento/Braintree/etc/di.xml @@ -447,7 +447,7 @@ <!-- PayPal value handlers infrastructure --> <type name="Magento\Braintree\Gateway\Response\PayPal\VaultDetailsHandler"> <arguments> - <argument name="paymentTokenFactory" xsi:type="object">Magento\Vault\Model\AccountPaymentTokenFactory</argument> + <argument name="paymentTokenFactory" xsi:type="object">Magento\Vault\Api\Data\PaymentTokenFactoryInterface</argument> </arguments> </type> <virtualType name="BraintreePayPalValueHandlerPool" type="Magento\Payment\Gateway\Config\ValueHandlerPool"> diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php index 6a9abe0a4c64e..5335837a4d3c4 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php @@ -139,9 +139,7 @@ public function execute() ->setName($name) ->getAttributeSet(); } catch (AlreadyExistsException $alreadyExists) { - $this->messageManager->addErrorMessage( - __('A "%1" attribute set name already exists. Create a new name and try again.', $name) - ); + $this->messageManager->addErrorMessage(__('An attribute set named \'%1\' already exists.', $name)); $this->_session->setAttributeData($data); return $this->returnResult('catalog/*/edit', ['_current' => true], ['error' => true]); } catch (LocalizedException $e) { @@ -202,6 +200,8 @@ public function execute() } } + $data = $this->presentation->convertPresentationDataToInputType($data); + if ($attributeId) { if (!$model->getId()) { $this->messageManager->addErrorMessage(__('This attribute no longer exists.')); @@ -216,7 +216,6 @@ public function execute() $data['attribute_code'] = $model->getAttributeCode(); $data['is_user_defined'] = $model->getIsUserDefined(); - $data['frontend_input'] = $model->getFrontendInput(); } else { /** * @todo add to helper and specify all relations for properties @@ -229,8 +228,6 @@ public function execute() ); } - $data = $this->presentation->convertPresentationDataToInputType($data); - $data += ['is_filterable' => 0, 'is_filterable_in_search' => 0]; if ($model->getIsUserDefined() === null || $model->getIsUserDefined() != 0) { diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Frontend/Inputtype/Presentation.php b/app/code/Magento/Catalog/Model/Product/Attribute/Frontend/Inputtype/Presentation.php index 28e0f22fc6ec9..03b8c7aa1cadc 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Frontend/Inputtype/Presentation.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Frontend/Inputtype/Presentation.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Attribute\Frontend\Inputtype; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; @@ -19,9 +21,9 @@ class Presentation * Get input type for presentation layer from stored input type. * * @param Attribute $attribute - * @return string + * @return string|null */ - public function getPresentationInputType(Attribute $attribute) + public function getPresentationInputType(Attribute $attribute) :?string { $inputType = $attribute->getFrontendInput(); if ($inputType == 'textarea' && $attribute->getIsWysiwygEnabled()) { @@ -37,12 +39,12 @@ public function getPresentationInputType(Attribute $attribute) * * @return array */ - public function convertPresentationDataToInputType(array $data) + public function convertPresentationDataToInputType(array $data) : array { - if ($data['frontend_input'] === 'texteditor') { + if (isset($data['frontend_input']) && $data['frontend_input'] === 'texteditor') { $data['is_wysiwyg_enabled'] = 1; $data['frontend_input'] = 'textarea'; - } elseif ($data['frontend_input'] === 'textarea') { + } elseif (isset($data['frontend_input']) && $data['frontend_input'] === 'textarea') { $data['is_wysiwyg_enabled'] = 0; } return $data; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Frontend/InputType/PresentationTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Frontend/InputType/PresentationTest.php new file mode 100644 index 0000000000000..16dff2d210f27 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Frontend/InputType/PresentationTest.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\Test\Unit\Model\Product\Attribute\Frontend\InputType; + +class PresentationTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Catalog\Model\Product\Attribute\Frontend\Inputtype\Presentation + */ + private $presentation; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute| \PHPUnit_Framework_MockObject_MockObject + */ + private $attributeMock; + + protected function setUp() + { + $this->presentation = new \Magento\Catalog\Model\Product\Attribute\Frontend\Inputtype\Presentation(); + $this->attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @param string $inputType + * @param boolean $isWysiwygEnabled + * @param string $expectedResult + * @dataProvider getPresentationInputTypeDataProvider + */ + public function testGetPresentationInputType(string $inputType, bool $isWysiwygEnabled, string $expectedResult) + { + $this->attributeMock->expects($this->once())->method('getFrontendInput')->willReturn($inputType); + $this->attributeMock->expects($this->any())->method('getIsWysiwygEnabled')->willReturn($isWysiwygEnabled); + $this->assertEquals($expectedResult, $this->presentation->getPresentationInputType($this->attributeMock)); + } + + public function getPresentationInputTypeDataProvider() + { + return [ + 'attribute_is_textarea_and_wysiwyg_enabled' => ['textarea', true, 'texteditor'], + 'attribute_is_input_and_wysiwyg_enabled' => ['input', true, 'input'], + 'attribute_is_textarea_and_wysiwyg_disabled' => ['textarea', false, 'textarea'], + ]; + } + + /** + * @param array $data + * @param array $expectedResult + * @dataProvider convertPresentationDataToInputTypeDataProvider + */ + public function testConvertPresentationDataToInputType(array $data, array $expectedResult) + { + $this->assertEquals($expectedResult, $this->presentation->convertPresentationDataToInputType($data)); + } + + public function convertPresentationDataToInputTypeDataProvider() + { + return [ + [['key' => 'value'], ['key' => 'value']], + [ + ['frontend_input' => 'texteditor'], + ['frontend_input' => 'textarea', 'is_wysiwyg_enabled' => 1] + ], + [ + ['frontend_input' => 'textarea'], + ['frontend_input' => 'textarea', 'is_wysiwyg_enabled' => 0] + ], + [ + ['frontend_input' => 'input'], + ['frontend_input' => 'input'] + ] + ]; + } +} diff --git a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js index 399321bd2f67d..3ea49cd981d90 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js @@ -12,7 +12,7 @@ define([ $.widget('mage.shoppingCart', { /** @inheritdoc */ _create: function () { - var items, i; + var items, i, reload; $(this.options.emptyCartButton).on('click', $.proxy(function () { $(this.options.emptyCartButton).attr('name', 'update_cart_action_temp'); @@ -36,6 +36,27 @@ define([ $(this.options.continueShoppingButton).on('click', $.proxy(function () { location.href = this.options.continueShoppingUrl; }, this)); + + $(document).on('ajax:removeFromCart', $.proxy(function () { + reload = true; + $('div.block.block-minicart').on('dropdowndialogclose', $.proxy(function () { + if (reload === true) { + location.reload(); + reload = false; + } + $('div.block.block-minicart').off('dropdowndialogclose'); + })); + }, this)); + $(document).on('ajax:updateItemQty', $.proxy(function () { + reload = true; + $('div.block.block-minicart').on('dropdowndialogclose', $.proxy(function () { + if (reload === true) { + location.reload(); + reload = false; + } + $('div.block.block-minicart').off('dropdowndialogclose'); + })); + }, this)); } }); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index dab40f026645d..3fb8743e951c8 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -220,6 +220,7 @@ define([ */ _updateItemQtyAfter: function (elem) { this._hideItemButton(elem); + $(document).trigger('ajax:updateItemQty'); }, /** diff --git a/app/code/Magento/Ui/Block/Wysiwyg/ActiveEditor.php b/app/code/Magento/Ui/Block/Wysiwyg/ActiveEditor.php index fbb2cb4979a00..b6077b7b1625d 100644 --- a/app/code/Magento/Ui/Block/Wysiwyg/ActiveEditor.php +++ b/app/code/Magento/Ui/Block/Wysiwyg/ActiveEditor.php @@ -17,6 +17,7 @@ class ActiveEditor extends \Magento\Framework\View\Element\Template { const DEFAULT_EDITOR_PATH = 'mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter'; + /** * @var ScopeConfigInterface */ @@ -27,11 +28,6 @@ class ActiveEditor extends \Magento\Framework\View\Element\Template */ private $availableAdapterPaths; - /** - * @param Context $context - * @param ScopeConfigInterface $scopeConfig - * @param array $data - */ /** * ActiveEditor constructor. * @param Context $context diff --git a/app/code/Magento/Vault/Model/PaymentTokenFactory.php b/app/code/Magento/Vault/Model/PaymentTokenFactory.php index e0b5d742fe8a4..6249fa4944a2c 100644 --- a/app/code/Magento/Vault/Model/PaymentTokenFactory.php +++ b/app/code/Magento/Vault/Model/PaymentTokenFactory.php @@ -22,6 +22,11 @@ class PaymentTokenFactory implements PaymentTokenFactoryInterface */ private $tokenTypes = []; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + /** * PaymentTokenFactory constructor. * @param ObjectManagerInterface $objectManager diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminEditTextEditorProductAttributeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminEditTextEditorProductAttributeTest.xml index 4e73647e596b2..7b0135973e211 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminEditTextEditorProductAttributeTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminEditTextEditorProductAttributeTest.xml @@ -16,8 +16,6 @@ <description value="Admin should be able to switch between 2 version of Tinymce in the admin back-end."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-85745"/> - <!-- Skipped, see https://jira.corp.magento.com/browse/MQE-890 --> - <group value="skip"/> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartProductSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartProductSection.xml index 6e4f52f689eb4..92c10ca83a76d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartProductSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartProductSection.xml @@ -24,5 +24,7 @@ <element name="ProductOptionByNameAndAttribute" type="input" selector="//main//table[@id='shopping-cart-table']//tbody//tr[.//strong[contains(@class, 'product-item-name')]//a[contains(text(), '{{var1}}')]]//dl[@class='item-options']//dt[.='{{var2}}']/following-sibling::dd[1]" parameterized="true"/> + <element name="RemoveItem" type="button" + selector="//table[@id='shopping-cart-table']//tbody//tr[contains(@class,'item-actions')]//a[contains(@class,'action-delete')]"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.xml new file mode 100644 index 0000000000000..e6d994fb587b0 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StoreFrontRemoveItemModalSection.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StoreFrontRemoveItemModalSection"> + <element name="message" type="text" selector="aside.confirm div.modal-content"/> + <element name="ok" type="button" selector="aside.confirm .modal-footer .action-primary"/> + <element name="cancel" type="button" selector="aside.confirm .modal-footer .action-secondary"/> + </section> +</sections> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml index 8f21770491f58..bdd97130a9715 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/StorefrontMiniCartSection.xml @@ -22,5 +22,6 @@ <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="deleteMiniCartItem" type="button" selector=".action.delete" timeout="30"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml new file mode 100644 index 0000000000000..ac29f8aa0bcc9 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="NoErrorCartCheckoutForProductsDeletedFromMiniCartTest"> + <annotations> + <features value="Checkout"/> + <stories value="Delete product from Storefront checkout"/> + <title value="Delete product from Checkout"/> + <description value="No error from cart should be thrown for product deleted from minicart"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-91451"/> + <group value="checkout"/> + </annotations> + <!-- Preconditions --> + <before> + <!-- Simple product is created with price = 100 --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <field key="price">100.00</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addProductToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="1" stepKey="seeCartQuantity"/> + <!-- open the minicart --> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart1"/> + <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="editProductFromMiniCart"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart2"/> + <click selector="{{StorefrontMinicartSection.deleteMiniCartItem}}" stepKey="deleteMiniCartItem"/> + <waitForElementVisible selector="{{StoreFrontRemoveItemModalSection.message}}" stepKey="waitFortheConfirmationModal"/> + <see selector="{{StoreFrontRemoveItemModalSection.message}}" userInput="Are you sure you would like to remove this item from the shopping cart?" stepKey="seeDeleteConfirmationMessage"/> + <click selector="{{StoreFrontRemoveItemModalSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForDeleteToFinish"/> + <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteProductFromCheckoutCart"/> + <waitForPageLoad stepKey="WaitForPageLoad3"/> + <see userInput="You have no items in your shopping cart." stepKey="seeNoItemsInShoppingCart"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePageActionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePageActionsSection.xml index 16c67394b5c0b..64ee4262a51dd 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePageActionsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Cms/Section/CmsNewPagePageActionsSection.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="CmsNewPagePageActionsSection"> <element name="savePage" type="button" selector="#save_and_close" timeout="10"/> <element name="reset" type="button" selector="#reset"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/SwitcherActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/SwitcherActionGroup.xml index 66c4a2b56cb15..f127b14931c98 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/SwitcherActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/SwitcherActionGroup.xml @@ -11,9 +11,12 @@ <actionGroup name="SwitchToVersion4ActionGroup"> <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> <waitForPageLoad stepKey="waitForConfigPageToLoad"/> - <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> - <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForDropdown" /> - <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="Yes" stepKey="switchToYes" /> + <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true" /> + <waitForElementVisible selector="{{ContentManagementSection.SwitcherSystemValue}}" stepKey="waitForCheckbox" /> + <uncheckOption selector="{{ContentManagementSection.SwitcherSystemValue}}" stepKey="uncheckUseSystemValue"/> + <waitForElementVisible selector="{{ContentManagementSection.Switcher}}" stepKey="waitForSwitcherDropdown" /> + <selectOption selector="{{ContentManagementSection.Switcher}}" userInput="TinyMCE 4" stepKey="switchToVersion4" /> + <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions" /> <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> </actionGroup> </actionGroups> diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml index bea169dc64ad3..7f15a6ba03f15 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml @@ -26,7 +26,7 @@ <data name="creditCard/data/payment_code" xsi:type="string">braintree</data> <data name="configData" xsi:type="string">braintree, braintree_use_vault</data> <data name="status" xsi:type="string">Processing</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test, severity:S1</data> + <data name="tag" xsi:type="string">severity:S1</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderSuccessCreateMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> <constraint name="Magento\Sales\Test\Constraint\AssertAuthorizationInCommentsHistory" /> From fa506cfed1ed8b8bf49d601c54cf79380f2488e4 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@magento.com> Date: Mon, 21 May 2018 18:05:16 -0500 Subject: [PATCH 144/333] MAGETWO-91895: Build stabilization - Fixed URL for creating StoreView --- .../Store/ActionGroup/CreateCustomStoreViewActionGroup.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/CreateCustomStoreViewActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/CreateCustomStoreViewActionGroup.xml index 80c442d2e13b9..7315a4fcde06f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/CreateCustomStoreViewActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/CreateCustomStoreViewActionGroup.xml @@ -11,8 +11,7 @@ <arguments> <argument name="storeGroupName" defaultValue="customStoreGroup.name"/> </arguments> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="clickCreateStoreViewButton"/> + <amOnPage url="{{AdminSystemStoreViewPage.url}}" stepKey="amOnAdminSystemStoreViewPage"/> <waitForPageLoad time="30" stepKey="waitForProductPageLoad"/> <selectOption userInput="{{storeGroupName}}" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> <fillField userInput="{{customStore.name}}" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> From 69117347143d18045a79f7fbab832985f936d990 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Mon, 21 May 2018 21:27:32 -0500 Subject: [PATCH 145/333] MAGETWO-91895: Build stabilization --- .../tests/functional/Magento/FunctionalTest/PageBuilder | 1 - 1 file changed, 1 deletion(-) delete mode 120000 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder deleted file mode 120000 index 523b3ef8895b9..0000000000000 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder +++ /dev/null @@ -1 +0,0 @@ -/Users/nsmith/vagrant-magento/magento2ce/magento2-page-builder/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/PageBuilder \ No newline at end of file From 8a8ed6613cebd8bddd1f1d9504af47d8ac18cc55 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Tue, 22 May 2018 10:34:47 +0530 Subject: [PATCH 146/333] Removed Illegal trailing whitespace. --- .../view/adminhtml/web/js/components/dynamic-rows-tier-price.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js index 430b3882d29db..45211faa550b7 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js @@ -37,7 +37,7 @@ define([ */ changePage: function (page) { this.clear(); /* Clear the children when directly edit the text field */ - + if (page === 1 && !this.recordData().length) { return false; } From 1c89cdea97f36519564f13a9fcc217b44b4f63b0 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 22 May 2018 09:54:00 +0300 Subject: [PATCH 147/333] MAGETWO-61364: [GitHub] Dependency check in the static tests fails for non core modules #7421 --- .../framework/Magento/TestFramework/Dependency/DiRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/DiRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/DiRule.php index 2b4497c544ab7..cdaa49e8d37fb 100644 --- a/dev/tests/static/framework/Magento/TestFramework/Dependency/DiRule.php +++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/DiRule.php @@ -40,7 +40,7 @@ private function getPattern() { if ($this->pattern === null) { $this->pattern = '~\b(?<class>(?<module>(' - . implode('_|', Files::init()->getNamespaces()) + . implode('[_\\\\]|', Files::init()->getNamespaces()) . '[_\\\\])[a-zA-Z0-9]+)[a-zA-Z0-9_\\\\]*)\b~'; } From 780e91dfc57cad564f8ea562ac02a1dd5647f33d Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 22 May 2018 11:55:14 +0300 Subject: [PATCH 148/333] MAGETWO-90801: Can't create customer account on checkout success page when gender field is required --- .../Customer/Model/Delegation/Storage.php | 2 +- .../ResourceModel/CustomerRepository.php | 57 +++++-------------- .../TestCase/CreateSalesRuleEntityTest.xml | 2 - .../TrackingShipmentForPlacedOrderTest.xml | 2 - .../OrderCustomerDelegateInterfaceTest.php | 2 + 5 files changed, 17 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/Customer/Model/Delegation/Storage.php b/app/code/Magento/Customer/Model/Delegation/Storage.php index 2b7e30a20f175..71a61d59057cb 100644 --- a/app/code/Magento/Customer/Model/Delegation/Storage.php +++ b/app/code/Magento/Customer/Model/Delegation/Storage.php @@ -120,7 +120,7 @@ public function consumeNewOperation() $this->logger->error($exception); $serialized = null; } - if (!$serialized) { + if ($serialized === null) { return null; } diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index a0b9b83c982ed..29e35c721a3be 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -7,6 +7,7 @@ namespace Magento\Customer\Model\ResourceModel; use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Delegation\Data\NewOperation; use Magento\Customer\Model\Customer\NotificationStorage; use Magento\Framework\Api\DataObjectHelper; @@ -163,13 +164,11 @@ public function __construct( * {@inheritdoc} * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $passwordHash = null) + public function save(CustomerInterface $customer, $passwordHash = null) { /** @var NewOperation|null $delegatedNewOperation */ - $delegatedNewOperation = !$customer->getId() - ? $this->delegatedStorage->consumeNewOperation() : null; + $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null; $prevCustomerData = null; $prevCustomerDataArr = null; if ($customer->getId()) { @@ -183,41 +182,25 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $prevCustomerData ); - $origAddresses = $customer->getAddresses(); $customer->setAddresses([]); - $customerData = $this->extensibleDataObjectConverter->toNestedArray( - $customer, - [], - \Magento\Customer\Api\Data\CustomerInterface::class - ); - + $customerData = $this->extensibleDataObjectConverter->toNestedArray($customer, [], CustomerInterface::class); $customer->setAddresses($origAddresses); /** @var Customer $customerModel */ - $customerModel = $this->customerFactory->create( - ['data' => $customerData] - ); - //Model's actual ID field maybe different than "id" - //so "id" field from $customerData may be ignored. + $customerModel = $this->customerFactory->create(['data' => $customerData]); + //Model's actual ID field maybe different than "id" so "id" field from $customerData may be ignored. $customerModel->setId($customer->getId()); - $storeId = $customerModel->getStoreId(); if ($storeId === null) { $customerModel->setStoreId($this->storeManager->getStore()->getId()); } - // Need to use attribute set or future updates can cause data loss if (!$customerModel->getAttributeSetId()) { - $customerModel->setAttributeSetId( - \Magento\Customer\Api\CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER - ); + $customerModel->setAttributeSetId(CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER); } $this->populateCustomerWithSecureData($customerModel, $passwordHash); - // If customer email was changed, reset RpToken info - if ($prevCustomerData - && $prevCustomerData->getEmail() !== $customerModel->getEmail() - ) { + if ($prevCustomerData && $prevCustomerData->getEmail() !== $customerModel->getEmail()) { $customerModel->setRpToken(null); $customerModel->setRpTokenCreatedAt(null); } @@ -225,30 +208,22 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa && null !== $prevCustomerDataArr && array_key_exists('default_billing', $prevCustomerDataArr) ) { - $customerModel->setDefaultBilling( - $prevCustomerDataArr['default_billing'] - ); + $customerModel->setDefaultBilling($prevCustomerDataArr['default_billing']); } if (!array_key_exists('default_shipping', $customerArr) && null !== $prevCustomerDataArr && array_key_exists('default_shipping', $prevCustomerDataArr) ) { - $customerModel->setDefaultShipping( - $prevCustomerDataArr['default_shipping'] - ); + $customerModel->setDefaultShipping($prevCustomerDataArr['default_shipping']); } - $customerModel->save(); $this->customerRegistry->push($customerModel); $customerId = $customerModel->getId(); - if (!$customer->getAddresses() && $delegatedNewOperation && $delegatedNewOperation->getCustomer()->getAddresses() ) { - $customer->setAddresses( - $delegatedNewOperation->getCustomer()->getAddresses() - ); + $customer->setAddresses($delegatedNewOperation->getCustomer()->getAddresses()); } if ($customer->getAddresses() !== null) { if ($customer->getId()) { @@ -260,7 +235,6 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa } else { $existingAddressIds = []; } - $savedAddressIds = []; foreach ($customer->getAddresses() as $address) { $address->setCustomerId($customerId) @@ -270,7 +244,6 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa $savedAddressIds[] = $address->getId(); } } - $addressIdsToDelete = array_diff($existingAddressIds, $savedAddressIds); foreach ($addressIdsToDelete as $addressId) { $this->addressRepository->deleteById($addressId); @@ -278,14 +251,12 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa } $this->customerRegistry->remove($customerId); $savedCustomer = $this->get($customer->getEmail(), $customer->getWebsiteId()); - $this->eventManager->dispatch( 'customer_save_after_data_object', [ 'customer_data_object' => $savedCustomer, 'orig_customer_data_object' => $prevCustomerData, - 'delegate_data' => $delegatedNewOperation - ? $delegatedNewOperation->getAdditionalData() : [] + 'delegate_data' => $delegatedNewOperation ? $delegatedNewOperation->getAdditionalData() : [], ] ); @@ -350,7 +321,7 @@ public function getList(SearchCriteriaInterface $searchCriteria) $collection = $this->customerFactory->create()->getCollection(); $this->extensionAttributesJoinProcessor->process( $collection, - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); // This is needed to make sure all the attributes are properly loaded foreach ($this->customerMetadata->getAllAttributesMetadata() as $metadata) { @@ -382,7 +353,7 @@ public function getList(SearchCriteriaInterface $searchCriteria) /** * {@inheritdoc} */ - public function delete(\Magento\Customer\Api\Data\CustomerInterface $customer) + public function delete(CustomerInterface $customer) { return $this->deleteById($customer->getId()); } diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml index eba2f95a1f720..586ad2acee203 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml @@ -116,8 +116,6 @@ <constraint name="Magento\SalesRule\Test\Constraint\AssertCartPriceRuleConditionIsApplied" /> </variation> <variation name="CreateSalesRuleEntityTestVariation5"> - <data name="issue" xsi:type="string">MAGETWO-89831: Unstable FAT: Magento\SalesRule\Test\TestCase\CreateSalesRuleEntityTest</data> - <data name="tag" xsi:type="string">stable:no</data> <data name="address/data/country_id" xsi:type="string">United States</data> <data name="address/data/region_id" xsi:type="string">California</data> <data name="address/data/postcode" xsi:type="string">95814</data> diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/TrackingShipmentForPlacedOrderTest.xml b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/TrackingShipmentForPlacedOrderTest.xml index e366a9cfb546a..6df9a51e51125 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/TrackingShipmentForPlacedOrderTest.xml +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/TrackingShipmentForPlacedOrderTest.xml @@ -8,8 +8,6 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Shipping\Test\TestCase\TrackingShipmentForPlacedOrderTest" summary="Create shipment for order."> <variation name="TrackingShipmentForPlacedOrderTestVariation1" summary="Creating shipment for order placed within Flat Rate." ticketId="MAGETWO-65163"> - <data name="issue" xsi:type="string">MAGETWO-89858: Unstable FAT: Magento\Shipping\Test\TestCase\TrackingShipmentForPlacedOrderTest</data> - <data name="tag" xsi:type="string">stable:no</data> <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> diff --git a/dev/tests/integration/testsuite/Magento/Sales/Api/OrderCustomerDelegateInterfaceTest.php b/dev/tests/integration/testsuite/Magento/Sales/Api/OrderCustomerDelegateInterfaceTest.php index 1b6ef8fc26810..459e8fdbf5087 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Api/OrderCustomerDelegateInterfaceTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Api/OrderCustomerDelegateInterfaceTest.php @@ -18,6 +18,8 @@ use PHPUnit\Framework\TestCase; /** + * Test for Magento\Sales\Api\OrderCustomerDelegateInterface class. + * * @magentoAppIsolation enabled */ class OrderCustomerDelegateInterfaceTest extends TestCase From 6f23c88d58c2a1b4bf89e4ea3cdb8de8836383a0 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 22 May 2018 12:25:50 +0300 Subject: [PATCH 149/333] MAGETWO-90802: Fix modularity of \Magento\Catalog\Block\Adminhtml\Product\Edit\JsTest --- .../Model/ResourceModel/Calculation/Rule.php | 2 +- .../Annotation/ApiDataFixture.php | 11 +++-- .../Block/Adminhtml/Product/Edit/JsTest.php | 13 ++---- .../Magento/Tax/_files/tax_classes.php | 9 ++++ .../Tax/_files/tax_classes_rollback.php | 43 +++++++++++++++++++ 5 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_rollback.php diff --git a/app/code/Magento/Tax/Model/ResourceModel/Calculation/Rule.php b/app/code/Magento/Tax/Model/ResourceModel/Calculation/Rule.php index e509f477bcd8d..91fd0f4dcffb3 100644 --- a/app/code/Magento/Tax/Model/ResourceModel/Calculation/Rule.php +++ b/app/code/Magento/Tax/Model/ResourceModel/Calculation/Rule.php @@ -6,7 +6,7 @@ namespace Magento\Tax\Model\ResourceModel\Calculation; /** - * Tax rate resource model + * Tax rule resource model * * @author Magento Core Team <core@magentocommerce.com> */ diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php index bd83cdcaf3fb1..88ac682f6b282 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php @@ -100,6 +100,7 @@ protected function _getFixtures($scope, \PHPUnit\Framework\TestCase $test) * Execute single fixture script * * @param string|array $fixture + * @throws \Throwable */ protected function _applyOneFixture($fixture) { @@ -110,9 +111,13 @@ protected function _applyOneFixture($fixture) require $fixture; } } catch (\Exception $e) { - echo 'Exception occurred when running the ' - . (is_array($fixture) || is_scalar($fixture) ? json_encode($fixture) : 'callback') - . ' fixture: ', PHP_EOL, $e; + throw new \Exception( + sprintf( + "Exception occurred when running the %s fixture: \n%s", + (\is_array($fixture) || is_scalar($fixture) ? json_encode($fixture) : 'callback'), + $e->getMessage() + ) + ); } $this->_appliedFixtures[] = $fixture; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/JsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/JsTest.php index 79989c24ea96d..f2043b3c5fff1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/JsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/JsTest.php @@ -29,23 +29,18 @@ public function testGetAllRatesByProductClassJson() /** @var \Magento\Catalog\Block\Adminhtml\Product\Edit\Js $block */ $block = $objectManager->create(\Magento\Catalog\Block\Adminhtml\Product\Edit\Js::class); $jsonResult = $block->getAllRatesByProductClassJson(); - $decodedResult = json_decode($jsonResult); - $this->assertNotEmpty($decodedResult, 'Resulting JSON is invalid.'); - $taxClassesArray = (array)$decodedResult; + $this->assertJson($jsonResult, 'Resulting JSON is invalid.'); + $decodedResult = json_decode($jsonResult, true); + $this->assertNotNull($decodedResult, 'Cannot decode resulting JSON.'); $noneTaxClass = 0; $defaultProductTaxClass = 2; $expectedProductTaxClasses = array_unique( array_merge($fixtureTaxRule->getProductTaxClasses(), [$defaultProductTaxClass, $noneTaxClass]) ); - $this->assertCount( - count($expectedProductTaxClasses), - $taxClassesArray, - 'Invalid quantity of rates for tax classes.' - ); foreach ($expectedProductTaxClasses as $taxClassId) { $this->assertArrayHasKey( "value_{$taxClassId}", - $taxClassesArray, + $decodedResult, "Rates for tax class with ID '{$taxClassId}' is missing." ); } diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes.php b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes.php index 95f69b1e522ee..515cda55ec584 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes.php @@ -38,6 +38,15 @@ \Magento\Tax\Model\ClassModel::TAX_CLASS_TYPE_PRODUCT )->save(); +// Tax class created but not used in the rule to ensure that unused tax classes are handled properly +$productTaxClass3 = $objectManager->create( + \Magento\Tax\Model\ClassModel::class +)->setClassName( + 'ProductTaxClass3' +)->setClassType( + \Magento\Tax\Model\ClassModel::TAX_CLASS_TYPE_PRODUCT +)->save(); + $taxRate = [ 'tax_country_id' => 'US', 'tax_region_id' => '12', diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_rollback.php b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_rollback.php new file mode 100644 index 0000000000000..51ba48b881736 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_classes_rollback.php @@ -0,0 +1,43 @@ +<?php +/** + * 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(); + +$taxRules = [ + 'Test Rule', + 'Test Rule Duplicate', +]; +$taxClasses = [ + 'ProductTaxClass1', + 'ProductTaxClass2', + 'ProductTaxClass3', + 'CustomerTaxClass1', + 'CustomerTaxClass2', +]; + + +$taxRuleResource = $objectManager->get(\Magento\Tax\Model\ResourceModel\Calculation\Rule::class); +foreach ($taxRules as $taxRuleCode) { + $taxRule = $objectManager->create(\Magento\Tax\Model\Calculation\Rule::class); + $taxRuleResource->load($taxRule, $taxRuleCode, 'code'); + $taxRuleResource->delete($taxRule); +} + +/** @var \Magento\Tax\Model\ResourceModel\TaxClass $resourceModel */ +$resourceModel = $objectManager->get(\Magento\Tax\Model\ResourceModel\TaxClass::class); + +foreach ($taxClasses as $taxClass) { + try { + /** @var \Magento\Tax\Model\ClassModel $taxClassEntity */ + $taxClassEntity = $objectManager->create(\Magento\Tax\Model\ClassModel::class); + $resourceModel->load($taxClassEntity, $taxClass, 'class_name'); + $resourceModel->delete($taxClassEntity); + } catch (\Magento\Framework\Exception\CouldNotDeleteException $couldNotDeleteException) { + // It's okay if the entity already wiped from the database + } +} From 822ba0c5d0a96ef65259de676cb78651a9325674 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Tue, 22 May 2018 12:56:55 +0300 Subject: [PATCH 150/333] MAGETWO-91934: Unlock Locales Editing when SCD on Demand Mode is Enabled - update di.xml --- app/code/Magento/Backend/etc/adminhtml/di.xml | 10 ++++++++-- app/code/Magento/Config/etc/adminhtml/di.xml | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index 1276033b33056..c49ad60694279 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -139,17 +139,23 @@ <type name="Magento\Backend\Model\Menu\Builder"> <plugin name="SetupMenuBuilder" type="Magento\Backend\Model\Setup\MenuBuilder" /> </type> - <type name="Magento\Config\Model\Config\Structure\ConcealInProductionConfigList"> + <type name="Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction"> <arguments> <argument name="configs" xsi:type="array"> <item name="dev" xsi:type="const">Magento\Config\Model\Config\Structure\ElementVisibilityInterface::HIDDEN</item> - <item name="general/locale/code" xsi:type="const">Magento\Config\Model\Config\Structure\ElementVisibilityInterface::DISABLED</item> </argument> <argument name="exemptions" xsi:type="array"> <item name="dev/debug/debug_logging" xsi:type="string"/> </argument> </arguments> </type> + <type name="Magento\Config\Model\Config\Structure\ElementVisibility\ConcealScdField"> + <arguments> + <argument name="configs" xsi:type="array"> + <item name="general/locale/code" xsi:type="const">Magento\Config\Model\Config\Structure\ElementVisibilityInterface::DISABLED</item> + </argument> + </arguments> + </type> <type name="Magento\Backend\Model\Search\Config\Result\Builder"> <arguments> <argument name="structureElementTypes" xsi:type="array"> diff --git a/app/code/Magento/Config/etc/adminhtml/di.xml b/app/code/Magento/Config/etc/adminhtml/di.xml index c21c06c7f3e1f..c0cd92df0d8ca 100644 --- a/app/code/Magento/Config/etc/adminhtml/di.xml +++ b/app/code/Magento/Config/etc/adminhtml/di.xml @@ -14,7 +14,8 @@ <type name="Magento\Config\Model\Config\Structure\ElementVisibilityComposite"> <arguments> <argument name="visibility" xsi:type="array"> - <item name="productionVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ConcealInProductionConfigList</item> + <item name="productionVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction</item> + <item name="productionScdOnDemandVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealScdField</item> </argument> </arguments> </type> From bdffd58983f51f5ee620e7847500e548f9e12a9c Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 22 May 2018 14:27:44 +0300 Subject: [PATCH 151/333] MAGETWO-90797: Deleting bundled options does not work --- .../Product/Form/Modifier/BundlePanel.php | 21 ++++++-- .../web/js/components/bundle-checkbox.js | 9 ++-- .../js/components/bundle-dynamic-rows-grid.js | 53 ++++++++++++++++++- .../bundle-user-defined-checkbox.js | 30 +++++++++++ .../js/components/bundle-checkbox.test.js | 1 - 5 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-user-defined-checkbox.js diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php index c0624be8e7a97..98fd96c52ccd9 100644 --- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php +++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php @@ -314,7 +314,8 @@ protected function getBundleOptions() 'template' => 'ui/dynamic-rows/templates/collapsible', 'additionalClasses' => 'admin__field-wide', 'dataScope' => 'data.bundle_options', - 'bundleSelectionsName' => 'product_bundle_container.bundle_selections' + 'isDefaultFieldScope' => 'is_default', + 'bundleSelectionsName' => 'product_bundle_container.bundle_selections', ], ], ], @@ -378,7 +379,10 @@ protected function getBundleOptions() 'selection_qty' => '', ], 'links' => ['insertData' => '${ $.provider }:${ $.dataProvider }'], - 'source' => 'product' + 'imports' => [ + 'inputType' => '${$.provider}:${$.dataScope}.type', + ], + 'source' => 'product', ], ], ], @@ -594,11 +598,14 @@ protected function getBundleSelections() 'config' => [ 'componentType' => Container::NAME, 'isTemplate' => true, - 'component' => 'Magento_Bundle/js/components/bundle-record', + 'component' => 'Magento_Ui/js/dynamic-rows/record', 'is_collection' => true, 'imports' => [ - 'onTypeChanged' => '${ $.provider }:${ $.bundleOptionsDataScope }.type' - ] + 'inputType' => '${$.parentName}:inputType', + ], + 'exports' => [ + 'isDefaultValue' => '${$.parentName}:isDefaultValue.${$.index}', + ], ], ], ], @@ -691,11 +698,15 @@ protected function getBundleSelections() 'componentType' => Form\Field::NAME, 'formElement' => Form\Element\Checkbox::NAME, 'dataType' => Form\Element\DataType\Price::NAME, + 'component' => 'Magento_Bundle/js/components/bundle-user-defined-checkbox', 'label' => __('User Defined'), 'dataScope' => 'selection_can_change_qty', 'value' => '1', 'valueMap' => ['true' => '1', 'false' => '0'], 'sortOrder' => 110, + 'imports' => [ + 'inputType' => '${$.parentName}:inputType', + ], ], ], ], diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-checkbox.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-checkbox.js index b608cff85b067..09331d37bb3b6 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-checkbox.js +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-checkbox.js @@ -14,7 +14,10 @@ define([ clearing: false, parentContainer: '', parentSelections: '', - changer: '' + changer: '', + exports: { + value: '${$.parentName}:isDefaultValue' + } }, /** @@ -58,10 +61,6 @@ define([ this.prefer = typeMap[type]; this.elementTmpl(this.templates[typeMap[type]]); - - if (this.prefer === 'radio' && this.checked()) { - this.clearValues(); - } }, /** diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js index 428361f459544..a6fc84765cc65 100644 --- a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-dynamic-rows-grid.js @@ -14,7 +14,57 @@ define([ label: '', columnsHeader: false, columnsHeaderAfterRender: true, - addButton: false + addButton: false, + isDefaultFieldScope: 'is_default', + defaultRecords: { + use: [], + moreThanOne: false, + state: {} + }, + listens: { + inputType: 'onInputTypeChange', + isDefaultValue: 'onIsDefaultValue' + } + }, + + /** + * Handler for type select. + * + * @param {String} inputType - changed. + */ + onInputTypeChange: function (inputType) { + if (this.defaultRecords.moreThanOne && (inputType === 'radio' || inputType === 'select')) { + _.each(this.defaultRecords.use, function (index, counter) { + this.source.set( + this.dataScope + '.bundle_selections.' + index + '.' + this.isDefaultFieldScope, + counter ? '0' : '1' + ); + }.bind(this)); + } + }, + + /** + * Handler for is_default field. + * + * @param {Object} data - changed data. + */ + onIsDefaultValue: function (data) { + var cb, + use = 0; + + this.defaultRecords.use = []; + + cb = function (elem, key) { + + if (~~elem) { + this.defaultRecords.use.push(key); + use++; + } + + this.defaultRecords.moreThanOne = use > 1; + }.bind(this); + + _.each(data, cb); }, /** @@ -29,7 +79,6 @@ define([ recordIndex; this.parsePagesData(data); - this.templates.record.bundleOptionsDataScope = this.dataScope; if (newData.length) { if (this.insertData().length) { diff --git a/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-user-defined-checkbox.js b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-user-defined-checkbox.js new file mode 100644 index 0000000000000..a7ceded02d0c3 --- /dev/null +++ b/app/code/Magento/Bundle/view/adminhtml/web/js/components/bundle-user-defined-checkbox.js @@ -0,0 +1,30 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/element/single-checkbox' +], function (Checkbox) { + 'use strict'; + + return Checkbox.extend({ + defaults: { + listens: { + inputType: 'onInputTypeChange' + } + }, + + /** + * Handler for "inputType" property + * + * @param {String} data + */ + onInputTypeChange: function (data) { + data === 'checkbox' || data === 'multi' ? + this.clear() + .visible(false) : + this.visible(true); + } + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Bundle/adminhtml/js/components/bundle-checkbox.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Bundle/adminhtml/js/components/bundle-checkbox.test.js index 88c28961d97e1..06206efa2bb3c 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Bundle/adminhtml/js/components/bundle-checkbox.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Bundle/adminhtml/js/components/bundle-checkbox.test.js @@ -56,7 +56,6 @@ define(['Magento_Bundle/js/components/bundle-checkbox', 'uiRegistry'], function unit.changeType('select'); expect(unit.prefer).toBe('radio'); - expect(unit.clearValues).toHaveBeenCalled(); }); }); }); From beb32b1df8e3a7bd44fb1d702fde0ff1a8830210 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 22 May 2018 17:11:42 +0300 Subject: [PATCH 152/333] MAGETWO-90729: Imported products are incorrectly in stock if 'allow_backorders' enabled --- .../Model/Import/Product.php | 76 +++++++++++-------- .../Magento/Catalog/Model/ProductTest.php | 45 +++++++++++ .../_files/product_simple_out_of_stock.php | 53 +++++++++++++ .../product_simple_out_of_stock_rollback.php | 28 +++++++ .../Model/Import/ProductTest.php | 45 +++++++++++ ...port_with_backorders_enabled_and_0_qty.csv | 2 + 6 files changed, 216 insertions(+), 33 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_enabled_and_0_qty.csv diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 7d175d524e287..3c591a4f4e08c 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2151,40 +2151,8 @@ protected function _saveStockItem() $row = []; $sku = $rowData[self::COL_SKU]; if ($this->skuProcessor->getNewSku($sku) !== null) { - $row['product_id'] = $this->skuProcessor->getNewSku($sku)['entity_id']; + $row = $this->formatStockDataForRow($rowData); $productIdsToReindex[] = $row['product_id']; - - $row['website_id'] = $this->stockConfiguration->getDefaultScopeId(); - $row['stock_id'] = $this->stockRegistry->getStock($row['website_id'])->getStockId(); - - $stockItemDo = $this->stockRegistry->getStockItem($row['product_id'], $row['website_id']); - $existStockData = $stockItemDo->getData(); - - $row = array_merge( - $this->defaultStockData, - array_intersect_key($existStockData, $this->defaultStockData), - array_intersect_key($rowData, $this->defaultStockData), - $row - ); - $row['sku'] = $sku; - - if ($this->stockConfiguration->isQty( - $this->skuProcessor->getNewSku($sku)['type_id'] - ) - ) { - $stockItemDo->setData($row); - $row['is_in_stock'] = $this->stockStateProvider->verifyStock($stockItemDo); - if ($this->stockStateProvider->verifyNotification($stockItemDo)) { - $row['low_stock_date'] = gmdate( - 'Y-m-d H:i:s', - (new \DateTime())->getTimestamp() - ); - } - $row['stock_status_changed_auto'] = - (int)!$this->stockStateProvider->verifyStock($stockItemDo); - } else { - $row['qty'] = 0; - } } if (!isset($stockData[$sku])) { @@ -2875,4 +2843,46 @@ private function getExistingSku($sku) { return $this->_oldSku[strtolower($sku)]; } + + /** + * Format row data to DB compatible values. + * + * @param array $rowData + * @return array + */ + private function formatStockDataForRow(array $rowData): array + { + $sku = $rowData[self::COL_SKU]; + $row['product_id'] = $this->skuProcessor->getNewSku($sku)['entity_id']; + $row['website_id'] = $this->stockConfiguration->getDefaultScopeId(); + $row['stock_id'] = $this->stockRegistry->getStock($row['website_id'])->getStockId(); + + $stockItemDo = $this->stockRegistry->getStockItem($row['product_id'], $row['website_id']); + $existStockData = $stockItemDo->getData(); + + $row = array_merge( + $this->defaultStockData, + array_intersect_key($existStockData, $this->defaultStockData), + array_intersect_key($rowData, $this->defaultStockData), + $row + ); + + if ($this->stockConfiguration->isQty($this->skuProcessor->getNewSku($sku)['type_id'])) { + $stockItemDo->setData($row); + $row['is_in_stock'] = $stockItemDo->getBackorders() && isset($row['is_in_stock']) + ? $row['is_in_stock'] + : $this->stockStateProvider->verifyStock($stockItemDo); + if ($this->stockStateProvider->verifyNotification($stockItemDo)) { + $row['low_stock_date'] = $this->dateTime->gmDate( + 'Y-m-d H:i:s', + (new \DateTime())->getTimestamp() + ); + } + $row['stock_status_changed_auto'] = (int)!$this->stockStateProvider->verifyStock($stockItemDo); + } else { + $row['qty'] = 0; + } + + return $row; + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php index 8b33c962cc809..b00090850e09b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model; use Magento\Framework\App\Filesystem\DirectoryList; @@ -552,4 +554,47 @@ public function testGetOptions() } } } + + /** + * Check stock status changing if backorders functionality enabled. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider productWithBackordersDataProvider + * @param int $qty + * @param int $stockStatus + * @param bool $expectedStockStatus + * + * @return void + */ + public function testSaveWithBackordersEnabled(int $qty, int $stockStatus, bool $expectedStockStatus): void + { + $product = $this->productRepository->get('simple-out-of-stock', true, null, true); + $stockItem = $product->getExtensionAttributes()->getStockItem(); + $this->assertEquals(false, $stockItem->getIsInStock()); + $stockData = [ + 'backorders' => 1, + 'qty' => $qty, + 'is_in_stock' => $stockStatus, + ]; + $product->setStockData($stockData); + $product->save(); + $stockItem = $product->getExtensionAttributes()->getStockItem(); + + $this->assertEquals($expectedStockStatus, $stockItem->getIsInStock()); + } + + /** + * DataProvider for the testSaveWithBackordersEnabled() + * @return array + */ + public function productWithBackordersDataProvider(): array + { + return [ + [0, 0, false], + [0, 1, true], + [-1, 0, false], + [-1, 1, true], + [1, 1, true], + ]; + } } 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 new file mode 100644 index 0000000000000..46af93ab01c76 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(); + +/** @var \Magento\TestFramework\ObjectManager $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->get(\Magento\Catalog\Api\CategoryLinkManagementInterface::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) + ->setId(1) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product') + ->setSku('simple-out-of-stock') + ->setPrice(10) + ->setWeight(1) + ->setShortDescription("Short description") + ->setTaxClassId(0) + ->setDescription('Description with <b>html tag</b>') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ] + )->setCanSaveCustomOptions(true) + ->setHasOptions(true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepositoryFactory */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productRepository->save($product); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock_rollback.php new file mode 100644 index 0000000000000..c4df4454aa07c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple-out-of-stock', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); 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 27638f4b092dd..a168ef3140f5f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -2181,4 +2181,49 @@ public function testProductsWithMultipleStoresWhenMediaIsDisabled(): void $this->assertTrue($errors->getErrorsCount() === 0); $this->assertTrue($this->_model->importData()); } + + /** + * Test that imported product stock status with backorders functionality enabled can be set to 'out of stock'. + * + * @magentoDataIsolation enabled + * @magentoAppIsolation enabled + * + * @return void + */ + public function testImportWithBackordersEnabled(): void + { + $this->importFile('products_to_import_with_backorders_enabled_and_0_qty.csv'); + $product = $this->getProductBySku('simple_new'); + $this->assertFalse($product->getDataByKey('quantity_and_stock_status')['is_in_stock']); + } + + /** + * Import file by providing import filename in parameters. + * + * @param string $fileName + * @return void + */ + private function importFile(string $fileName): void + { + $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + \Magento\ImportExport\Model\Import\Source\Csv::class, + [ + 'file' => __DIR__ . '/_files/' . $fileName, + 'directory' => $directory, + ] + ); + $errors = $this->_model->setParameters( + [ + 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product', + \Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE => 1, + ] + )->setSource($source)->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + + $this->_model->importData(); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_enabled_and_0_qty.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_enabled_and_0_qty.csv new file mode 100644 index 0000000000000..e2107766c37bd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_enabled_and_0_qty.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,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 ,,,,,,,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",0,0,1,0,1,0,1,1,10000,1,0,1,1,1,0,1,1,0,0,0,1,,,,,,,,,,,,, From 6b41a71023f18db2ea5541e56307f58dc6b6c4bd Mon Sep 17 00:00:00 2001 From: pdohogne-magento <pdohogne@magento.com> Date: Tue, 22 May 2018 12:46:26 -0500 Subject: [PATCH 153/333] MAGETWO-87492: Renaming 2.3 upgrade script --- .../{2.3_upgrade_additions.php => pre_composer_update_2.3.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bin/UpgradeScripts/{2.3_upgrade_additions.php => pre_composer_update_2.3.php} (100%) diff --git a/bin/UpgradeScripts/2.3_upgrade_additions.php b/bin/UpgradeScripts/pre_composer_update_2.3.php similarity index 100% rename from bin/UpgradeScripts/2.3_upgrade_additions.php rename to bin/UpgradeScripts/pre_composer_update_2.3.php From 7607778a189e8a0ed064a7911d708fa7ed73f2aa Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Tue, 22 May 2018 16:28:54 -0500 Subject: [PATCH 154/333] MAGETWO-91450: date reverts in past after clicking out of product edit - added functional test --- .../AdminProductGridActionGroup.xml | 9 +++ .../Catalog/Data/CustomAttributeData.xml | 4 ++ .../Catalog/Data/ProductData.xml | 14 +++++ .../AdminProductAttributeGridSection.xml | 4 ++ .../Section/AdminProductFormActionSection.xml | 5 +- .../Section/AdminProductGridFilterSection.xml | 1 + ...roductGridFilteringByDateAttributeTest.xml | 58 +++++++++++++++++++ 7 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminProductGridFilteringByDateAttributeTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminProductGridActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminProductGridActionGroup.xml index 9bfbab2d5d872..6398010f06687 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminProductGridActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminProductGridActionGroup.xml @@ -53,6 +53,15 @@ <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> </actionGroup> + <!--Filter the product grid by new from date filter--> + <actionGroup name="filterProductGridBySetNewFromDate"> + <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"> <arguments> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/CustomAttributeData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/CustomAttributeData.xml index b143c3932f936..c0f2e391a4e2b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/CustomAttributeData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/CustomAttributeData.xml @@ -31,4 +31,8 @@ <data key="attribute_code">short_description</data> <data key="value" unique="suffix">API Product Short Description</data> </entity> + <entity name="ApiProductNewsFromDate" type="custom_attribute"> + <data key="attribute_code">news_from_date</data> + <data key="value">2018-05-17 00:00:00</data> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml index cc6a32e7223f9..6e1bc96729f55 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml @@ -240,4 +240,18 @@ <requiredEntity type="product_option">ProductOptionDateTime</requiredEntity> <requiredEntity type="product_option">ProductOptionTime</requiredEntity> </entity> + <entity name="SimpleProductWithNewFromDate" type="product"> + <data key="sku" unique="suffix">SimpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">125.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">1000</data> + <data key="urlKey" unique="suffix">simpleproduct</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductNewsFromDate</requiredEntity> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductAttributeGridSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductAttributeGridSection.xml index 97e8a913e6010..f1347643cbd9d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductAttributeGridSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductAttributeGridSection.xml @@ -11,5 +11,9 @@ <section name="AdminProductAttributeGridSection"> <element name="AttributeCode" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true"/> <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> + <element name="GridFilterFrontEndLabel" type="input" selector="#attributeGrid_filter_frontend_label"/> + <element name="Search" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> + <element name="ResetFilter" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> + <element name="FirstRow" type="button" selector="//*[@id='attributeGrid_table']/tbody/tr[1]"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormActionSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormActionSection.xml index 16e060557ab09..e55a45d590910 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormActionSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormActionSection.xml @@ -11,8 +11,9 @@ <section name="AdminProductFormActionSection"> <element name="backButton" type="button" selector="#back" timeout="30"/> <element name="saveButton" type="button" selector="#save-button" timeout="30"/> - <element name="saveArrow" type="button" selector="button[data-ui-id='save-button-dropdown']" timeout="10"/> - <element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/> + <element name="saveArrow" type="button" selector="button[data-ui-id='save-button-dropdown']" timeout="30"/> + <!--<element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/>--> + <element name="saveAndClose" type="button" selector="span[id='save_and_close']" timeout="30"/> <element name="changeStoreButton" type="button" selector="#store-change-button" timeout="10"/> <element name="selectStoreView" type="button" selector="//ul[@data-role='stores-list']/li/a[normalize-space(.)='{{var1}}']" timeout="10" parameterized="true"/> </section> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductGridFilterSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductGridFilterSection.xml index 3436bbf0c08d2..b0b8d90c8625e 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductGridFilterSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductGridFilterSection.xml @@ -28,6 +28,7 @@ <element name="priceFilterTo" type="input" selector="input.admin__control-text[name='price[to]']"/> <element name="typeFilter" type="select" selector="select.admin__control-select[name='type_id']"/> <element name="statusFilter" type="select" selector="select.admin__control-select[name='status']"/> + <element name="newFromDateFilter" type="input" selector="input.admin__control-text[name='news_from_date[from]']"/> <element name="keywordSearch" type="input" selector="input#fulltext"/> <element name="keywordSearchButton" type="button" selector=".data-grid-search-control-wrap button.action-submit" timeout="30"/> </section> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminProductGridFilteringByDateAttributeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminProductGridFilteringByDateAttributeTest.xml new file mode 100644 index 0000000000000..43edc3a54e00b --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminProductGridFilteringByDateAttributeTest.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminProductGridFilteringByDateAttributeTest"> + <annotations> + <title value="Verify Set Product as new Filter input on Product Grid doesn't getreset to currentDate"/> + <description value="Data input in the new from date filter field should not change"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-92019"/> + <group value="product"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleProductWithNewFromDate" stepKey="createSimpleProductWithDate"/> + </before> + <after> + <deleteData createDataKey="createSimpleProductWithDate" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttribute"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="Set Product as New from Date" stepKey="setAttributeLabel"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromGrid"/> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="openAdvancedPropertiesTab"/> + <selectOption selector="#is_filterable_in_grid" userInput="Yes" stepKey="isFilterableInGrid"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad time="30" stepKey="waitForProductGridPageLoad"/> + <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="openColumnsdropDown1"/> + <checkOption selector="{{AdminProductGridFilterSection.viewColumnOption('Set Product as New from Date')}}" stepKey="showProductAsNewColumn"/> + <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"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnFirstRowProductGrid"/> + <waitForPageLoad stepKey="waitForProductEditPageToLoad"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProductForm"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="expandFilters"/> + <seeInField selector="{{AdminProductGridFilterSection.newFromDateFilter}}" userInput="05/16/2018" stepKey="checkForNewFromDate"/> + <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="openColumnsDropdown2"/> + <uncheckOption selector="{{AdminProductGridFilterSection.viewColumnOption('Set Product as New from Date')}}" stepKey="hideProductAsNewColumn"/> + <dontSeeElement selector="{{AdminProductGridSection.columnHeader('Set Product as New from Date')}}" stepKey="dontSeeNewFromDateColumn"/> + <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="closeColumnsDropdown2"/> + <click selector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearGridFilters"/> + </test> + </tests> \ No newline at end of file From d9f09dd13d7386ed2c3305b2090e7efc64f1c980 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@magento.com> Date: Tue, 22 May 2018 16:46:11 -0500 Subject: [PATCH 155/333] MAGETWO-90277: Cannot Save Short Description --- .../Catalog/Controller/Adminhtml/Product/Attribute/Save.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php index 5335837a4d3c4..e054a9d49b437 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php @@ -216,6 +216,7 @@ public function execute() $data['attribute_code'] = $model->getAttributeCode(); $data['is_user_defined'] = $model->getIsUserDefined(); + $data['frontend_input'] = $data['frontend_input'] ?? $model->getFrontendInput(); } else { /** * @todo add to helper and specify all relations for properties From 00b22e77d0dc79c7599bee4c1af5cad71546bcb5 Mon Sep 17 00:00:00 2001 From: Facundo Capua <facundocapua@gmail.com> Date: Tue, 22 May 2018 20:58:28 -0300 Subject: [PATCH 156/333] Added some documentation to the class --- .../CatalogGraphQl/Model/Resolver/Category/Products.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php index baa8f0c8bc5d3..5927e747c2238 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php @@ -17,6 +17,9 @@ use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +/** + * Category products resolver, used by GraphQL endpoints to retrieve products assigned to a category + */ class Products implements ResolverInterface { /** @var \Magento\Catalog\Api\ProductRepositoryInterface */ From 9075259456d8434820babaafa60655121c180336 Mon Sep 17 00:00:00 2001 From: Facundo Capua <facundocapua@gmail.com> Date: Tue, 22 May 2018 20:59:11 -0300 Subject: [PATCH 157/333] Adjust documentation to describe the actual functionality --- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 64e56a738bbf6..f9e43959c79d9 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -410,7 +410,7 @@ type Products @doc(description: "The Products object is the top-level object ret } type CategoryProducts @doc(description: "The category products object returned in the Category query") { - items: [ProductInterface] @doc(description: "An array of products that match the specified search criteria") + items: [ProductInterface] @doc(description: "An array of products that are assigned to the category") page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query") total_count: Int @doc(description: "The number of products returned") } From 4224794b9a046b0bb80f5566a1eda689dee01956 Mon Sep 17 00:00:00 2001 From: Facundo Capua <facundocapua@gmail.com> Date: Tue, 22 May 2018 20:59:35 -0300 Subject: [PATCH 158/333] Added assertions to the product information returned in the tests --- .../Magento/GraphQl/Catalog/CategoryTest.php | 147 +++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 78a7aa8bed63a..f6121bea2d830 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -9,7 +9,9 @@ use Magento\Framework\DataObject; use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\ObjectManager; class CategoryTest extends GraphQlAbstract { @@ -172,7 +174,6 @@ public function testCategoryProducts() new_from_date new_to_date options_container - price { minimalPrice { amount { @@ -268,5 +269,149 @@ public function testCategoryProducts() $this->assertEquals(2, $response['category']['products']['total_count']); $this->assertEquals(1, $response['category']['products']['page_info']['current_page']); $this->assertEquals(20, $response['category']['products']['page_info']['page_size']); + + /** + * @var ProductRepositoryInterface $productRepository + */ + $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); + $firstProductSku = 'simple'; + $firstProduct = $productRepository->get($firstProductSku, false, null, true); + $this->assertBaseFields($firstProduct, $response['category']['products']['items'][0]); + $this->assertAttributes($response['category']['products']['items'][0]); + $this->assertWebsites($firstProduct, $response['category']['products']['items'][0]['websites']); + + $secondProductSku = '12345'; + $secondProduct = $productRepository->get($secondProductSku, false, null, true); + $this->assertBaseFields($secondProduct, $response['category']['products']['items'][1]); + $this->assertAttributes($response['category']['products']['items'][1]); + $this->assertWebsites($secondProduct, $response['category']['products']['items'][1]['websites']); + } + + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertBaseFields($product, $actualResponse) + { + + $assertionMap = [ + ['response_field' => 'attribute_set_id', 'expected_value' => $product->getAttributeSetId()], + ['response_field' => 'created_at', 'expected_value' => $product->getCreatedAt()], + ['response_field' => 'id', 'expected_value' => $product->getId()], + ['response_field' => 'name', 'expected_value' => $product->getName()], + ['response_field' => 'price', 'expected_value' => + [ + 'minimalPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + 'regularPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + 'maximalPrice' => [ + 'amount' => [ + 'value' => $product->getPrice(), + 'currency' => 'USD' + ], + 'adjustments' => [] + ], + ] + ], + ['response_field' => 'sku', 'expected_value' => $product->getSku()], + ['response_field' => 'type_id', 'expected_value' => $product->getTypeId()], + ['response_field' => 'updated_at', 'expected_value' => $product->getUpdatedAt()], +// ['response_field' => 'weight', 'expected_value' => $product->getWeight()], + ]; + + $this->assertResponseFields($actualResponse, $assertionMap); + } + + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertWebsites($product, $actualResponse) + { + $assertionMap = [ + [ + 'id' => current($product->getExtensionAttributes()->getWebsiteIds()), + 'name' => 'Main Website', + 'code' => 'base', + 'sort_order' => 0, + 'default_group_id' => '1', + 'is_default' => true, + ] + ]; + + $this->assertEquals($actualResponse, $assertionMap); + } + + /** + * @param array $actualResponse + */ + private function assertAttributes($actualResponse) + { + $eavAttributes = [ + 'url_key', + 'description', + 'meta_description', + 'meta_keyword', + 'meta_title', + 'short_description', + 'tax_class_id', + 'country_of_manufacture', + 'gift_message_available', + 'new_from_date', + 'new_to_date', + 'options_container', + 'special_price', + 'special_from_date', + 'special_to_date', + ]; + + foreach($eavAttributes as $eavAttribute){ + $this->assertArrayHasKey($eavAttribute, $actualResponse); + } + } + + /** + * @param array $actualResponse + * @param array $assertionMap ['response_field_name' => 'response_field_value', ...] + * OR [['response_field' => $field, 'expected_value' => $value], ...] + */ + private function assertResponseFields($actualResponse, $assertionMap) + { + foreach ($assertionMap as $key => $assertionData) { + $expectedValue = isset($assertionData['expected_value']) + ? $assertionData['expected_value'] + : $assertionData; + $responseField = isset($assertionData['response_field']) ? $assertionData['response_field'] : $key; + self::assertNotNull( + $expectedValue, + "Value of '{$responseField}' field must not be NULL" + ); + self::assertEquals( + $expectedValue, + $actualResponse[$responseField], + "Value of '{$responseField}' field in response does not match expected value: " + . var_export($expectedValue, true) + ); + } + } + + private function eavAttributesToGraphQlSchemaFieldTranslator($attributeCode) + { + if(isset($this->eavAttributesToGraphQlSchemaFieldMap[$attributeCode])){ + return $this->eavAttributesToGraphQlSchemaFieldMap[$attributeCode]; + } + + return $attributeCode; } } From ba6bf77e781dd633134a293a1521d13938cabf07 Mon Sep 17 00:00:00 2001 From: Saravanan <saravananv14@live.com> Date: Wed, 23 May 2018 10:31:08 +0530 Subject: [PATCH 159/333] Removed newly added comments --- .../view/adminhtml/web/js/components/dynamic-rows-tier-price.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js index 45211faa550b7..8d8a40a639db3 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js @@ -36,7 +36,7 @@ define([ * @param {Number} page - current page */ changePage: function (page) { - this.clear(); /* Clear the children when directly edit the text field */ + this.clear(); if (page === 1 && !this.recordData().length) { return false; From 6de6006220c91be431e68710b481e50393ed2578 Mon Sep 17 00:00:00 2001 From: Saravanan <saravananv14@live.com> Date: Wed, 23 May 2018 10:46:11 +0530 Subject: [PATCH 160/333] Inherited the parent method to fix pagination issue --- .../adminhtml/web/js/components/dynamic-rows-tier-price.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js index 8d8a40a639db3..4c94b02576b3f 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js @@ -30,11 +30,7 @@ define([ this.labels(labels); }, - /** - * Change page - * - * @param {Number} page - current page - */ + /** @inheritdoc */ changePage: function (page) { this.clear(); From 64b5f63834c2a61446948baf725135941ccc3341 Mon Sep 17 00:00:00 2001 From: David Manners <dmanners87@gmail.com> Date: Wed, 23 May 2018 08:37:22 +0000 Subject: [PATCH 161/333] ADHOC: update the contribution templates to include a not on actual and expected results - Making it so that screenshots and logs are optional --- .github/ISSUE_TEMPLATE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index f6c8b354f42cc..12ad4e452b1c7 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -28,8 +28,8 @@ ### Expected result <!--- Tell us what should happen --> -1. [Screenshot, logs] +1. [Screenshots, logs or description] ### Actual result <!--- Tell us what happens instead --> -1. [Screenshot, logs] +1. [Screenshots, logs or description] From accf0180d61057912e61ec566670f25be6ef9eab Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Sat, 19 May 2018 15:36:18 +0530 Subject: [PATCH 162/333] Removed duplicate line and added comment on variable --- .../Sales/Block/Adminhtml/Order/Create/Totals/Discount.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Discount.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Discount.php index 738f9a4fef999..00b91c164fb3e 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Discount.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Discount.php @@ -17,7 +17,9 @@ class Discount extends \Magento\Sales\Block\Adminhtml\Order\Create\Totals\Defaul { //protected $_template = 'tax/checkout/subtotal.phtml'; - //protected $_template = 'tax/checkout/subtotal.phtml'; + /** + * @var \Magento\Tax\Model\Config + */ protected $_taxConfig; /** From f9a86bcbea894a8a056ad567e61104da8f9377e3 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Sat, 19 May 2018 20:28:14 +0530 Subject: [PATCH 163/333] Removed commented line, --- .../Sales/Block/Adminhtml/Order/Create/Totals/Discount.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Discount.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Discount.php index 00b91c164fb3e..b6b1d4f59715e 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Discount.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Discount.php @@ -15,8 +15,6 @@ */ class Discount extends \Magento\Sales\Block\Adminhtml\Order\Create\Totals\DefaultTotals { - //protected $_template = 'tax/checkout/subtotal.phtml'; - /** * @var \Magento\Tax\Model\Config */ From 467ebb2b234def50f0ca2f95ad372953bfa60071 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Sat, 19 May 2018 13:23:33 +0530 Subject: [PATCH 164/333] Refactor validate code in Tax module --- .../adminhtml/templates/class/page/edit.phtml | 7 +++++++ .../Tax/view/adminhtml/web/js/page/validate.js | 15 +++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js diff --git a/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml index 8930a4de28fe5..d039747219621 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml @@ -18,3 +18,10 @@ require(['jquery', "mage/mage"], function(jQuery){ }); </script> +<script type="text/x-magento-init"> + { + "#<?= /* @escapeNotVerified */ $block->getRenameFormId() ?>": { + "Magento_Tax/js/page/validate": {} + } + } +</script> diff --git a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js b/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js new file mode 100644 index 0000000000000..797dfdfdb9dfe --- /dev/null +++ b/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js @@ -0,0 +1,15 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'mage/mage' +], function(jQuery){ + 'use strict'; + + return function (data, element) { + jQuery(element).mage('form').mage('validation'); + } +}); \ No newline at end of file From 4ec581c858b79cc40862c8f746e3efb3658af67b Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Sat, 19 May 2018 20:49:01 +0530 Subject: [PATCH 165/333] Fixed semicolon issue --- app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js b/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js index 797dfdfdb9dfe..50afc80689477 100644 --- a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js +++ b/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js @@ -11,5 +11,5 @@ define([ return function (data, element) { jQuery(element).mage('form').mage('validation'); - } -}); \ No newline at end of file + }; +}); From 4078907739178bb2339b0f343fb3567acbb7ea8e Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Sun, 20 May 2018 20:15:18 +0530 Subject: [PATCH 166/333] Fixed coding standard issue --- app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js b/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js index 50afc80689477..a49f199ba56b6 100644 --- a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js +++ b/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js @@ -6,7 +6,7 @@ define([ 'jquery', 'mage/mage' -], function(jQuery){ +], function (jQuery) { 'use strict'; return function (data, element) { From 60ed6801a43698cb88edb30cc262b7f9d38f49ee Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <strpwebstudio@gmail.com> Date: Tue, 22 May 2018 15:40:15 +0300 Subject: [PATCH 167/333] Format the javascript code in Tax module #15343 - Remove old realization --- .../Tax/view/adminhtml/templates/class/page/edit.phtml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml index d039747219621..18e86549a1ff9 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml @@ -11,13 +11,6 @@ <?= $block->getSaveButtonHtml() ?> </div> <?= $block->getRenameFormHtml() ?> -<script> -require(['jquery', "mage/mage"], function(jQuery){ - - jQuery('#<?= /* @escapeNotVerified */ $block->getRenameFormId() ?>').mage('form').mage('validation'); - -}); -</script> <script type="text/x-magento-init"> { "#<?= /* @escapeNotVerified */ $block->getRenameFormId() ?>": { From 891fcdb92757f7bc86c5cebc6420f53191157e11 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Sun, 20 May 2018 23:25:25 -0700 Subject: [PATCH 168/333] Fixed set template syntax issue --- app/code/Magento/Backend/Block/Dashboard.php | 2 +- app/code/Magento/Backend/Block/Dashboard/Graph.php | 2 +- app/code/Magento/Backend/Block/Dashboard/Grid.php | 2 +- app/code/Magento/Backend/Block/Dashboard/Sales.php | 2 +- app/code/Magento/Backend/Block/Dashboard/Totals.php | 2 +- app/code/Magento/Backend/Block/Page/Copyright.php | 2 +- app/code/Magento/Backend/Block/Page/Footer.php | 2 +- app/code/Magento/Backend/Block/Page/Header.php | 2 +- .../Backend/Block/Store/Switcher/Form/Renderer/Fieldset.php | 2 +- .../Block/Store/Switcher/Form/Renderer/Fieldset/Element.php | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Backend/Block/Dashboard.php b/app/code/Magento/Backend/Block/Dashboard.php index 8d0a061621fe3..e1e87d8d4c5a3 100644 --- a/app/code/Magento/Backend/Block/Dashboard.php +++ b/app/code/Magento/Backend/Block/Dashboard.php @@ -20,7 +20,7 @@ class Dashboard extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'dashboard/index.phtml'; + protected $_template = 'Magento_Backend::dashboard/index.phtml'; /** * @return void diff --git a/app/code/Magento/Backend/Block/Dashboard/Graph.php b/app/code/Magento/Backend/Block/Dashboard/Graph.php index 301dffbdc4987..8e238ccab44cb 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Graph.php +++ b/app/code/Magento/Backend/Block/Dashboard/Graph.php @@ -90,7 +90,7 @@ class Graph extends \Magento\Backend\Block\Dashboard\AbstractDashboard /** * @var string */ - protected $_template = 'dashboard/graph.phtml'; + protected $_template = 'Magento_Backend::dashboard/graph.phtml'; /** * Adminhtml dashboard data diff --git a/app/code/Magento/Backend/Block/Dashboard/Grid.php b/app/code/Magento/Backend/Block/Dashboard/Grid.php index 602b5e414d538..f7f9a79f17eb0 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Grid.php +++ b/app/code/Magento/Backend/Block/Dashboard/Grid.php @@ -17,7 +17,7 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended /** * @var string */ - protected $_template = 'dashboard/grid.phtml'; + protected $_template = 'Magento_Backend::dashboard/grid.phtml'; /** * Setting default for every grid on dashboard diff --git a/app/code/Magento/Backend/Block/Dashboard/Sales.php b/app/code/Magento/Backend/Block/Dashboard/Sales.php index d0f056230bcd1..6d7a4d6458a8e 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Sales.php +++ b/app/code/Magento/Backend/Block/Dashboard/Sales.php @@ -15,7 +15,7 @@ class Sales extends \Magento\Backend\Block\Dashboard\Bar /** * @var string */ - protected $_template = 'dashboard/salebar.phtml'; + protected $_template = 'Magento_Backend::dashboard/salebar.phtml'; /** * @var \Magento\Framework\Module\Manager diff --git a/app/code/Magento/Backend/Block/Dashboard/Totals.php b/app/code/Magento/Backend/Block/Dashboard/Totals.php index 96ae6dd636380..4dcda3677584c 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Totals.php +++ b/app/code/Magento/Backend/Block/Dashboard/Totals.php @@ -16,7 +16,7 @@ class Totals extends \Magento\Backend\Block\Dashboard\Bar /** * @var string */ - protected $_template = 'dashboard/totalbar.phtml'; + protected $_template = 'Magento_Backend::dashboard/totalbar.phtml'; /** * @var \Magento\Framework\Module\Manager diff --git a/app/code/Magento/Backend/Block/Page/Copyright.php b/app/code/Magento/Backend/Block/Page/Copyright.php index 062497d6a8304..a1b61352930b5 100644 --- a/app/code/Magento/Backend/Block/Page/Copyright.php +++ b/app/code/Magento/Backend/Block/Page/Copyright.php @@ -18,5 +18,5 @@ class Copyright extends \Magento\Backend\Block\Template * * @var string */ - protected $_template = 'page/copyright.phtml'; + protected $_template = 'Magento_Backend::page/copyright.phtml'; } diff --git a/app/code/Magento/Backend/Block/Page/Footer.php b/app/code/Magento/Backend/Block/Page/Footer.php index 368869b79e15c..3d1570e5ddfe7 100644 --- a/app/code/Magento/Backend/Block/Page/Footer.php +++ b/app/code/Magento/Backend/Block/Page/Footer.php @@ -17,7 +17,7 @@ class Footer extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'page/footer.phtml'; + protected $_template = 'Magento_Backend::page/footer.phtml'; /** * @var \Magento\Framework\App\ProductMetadataInterface diff --git a/app/code/Magento/Backend/Block/Page/Header.php b/app/code/Magento/Backend/Block/Page/Header.php index b7ed05ce58e95..c2c5f7472b370 100644 --- a/app/code/Magento/Backend/Block/Page/Header.php +++ b/app/code/Magento/Backend/Block/Page/Header.php @@ -18,7 +18,7 @@ class Header extends \Magento\Backend\Block\Template /** * @var string */ - protected $_template = 'page/header.phtml'; + protected $_template = 'Magento_Backend::page/header.phtml'; /** * Backend data diff --git a/app/code/Magento/Backend/Block/Store/Switcher/Form/Renderer/Fieldset.php b/app/code/Magento/Backend/Block/Store/Switcher/Form/Renderer/Fieldset.php index 2f9b73f0ae037..6fe8416784c2e 100644 --- a/app/code/Magento/Backend/Block/Store/Switcher/Form/Renderer/Fieldset.php +++ b/app/code/Magento/Backend/Block/Store/Switcher/Form/Renderer/Fieldset.php @@ -25,7 +25,7 @@ class Fieldset extends \Magento\Backend\Block\Template implements RendererInterf /** * @var string */ - protected $_template = 'store/switcher/form/renderer/fieldset.phtml'; + protected $_template = 'Magento_Backend::store/switcher/form/renderer/fieldset.phtml'; /** * Retrieve an element diff --git a/app/code/Magento/Backend/Block/Store/Switcher/Form/Renderer/Fieldset/Element.php b/app/code/Magento/Backend/Block/Store/Switcher/Form/Renderer/Fieldset/Element.php index ddd1f1a9178cd..71d4db6849bd2 100644 --- a/app/code/Magento/Backend/Block/Store/Switcher/Form/Renderer/Fieldset/Element.php +++ b/app/code/Magento/Backend/Block/Store/Switcher/Form/Renderer/Fieldset/Element.php @@ -23,7 +23,7 @@ class Element extends \Magento\Backend\Block\Widget\Form\Renderer\Fieldset\Eleme /** * @var string */ - protected $_template = 'store/switcher/form/renderer/fieldset/element.phtml'; + protected $_template = 'Magento_Backend::store/switcher/form/renderer/fieldset/element.phtml'; /** * Retrieve an element From dffcf1bd62839ef8b5c31d59bcdce104639bd585 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 23 May 2018 12:47:06 +0300 Subject: [PATCH 169/333] MAGETWO-90357: After Deleting Product Update shopping cart is empty --- .../Sales/Observer/Backend/SubtractQtyFromQuotesObserver.php | 2 +- .../Unit/Observer/Backend/SubtractQtyFromQuotesObserverTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Observer/Backend/SubtractQtyFromQuotesObserver.php b/app/code/Magento/Sales/Observer/Backend/SubtractQtyFromQuotesObserver.php index 775a7dab95cfe..cd8c705750d6c 100644 --- a/app/code/Magento/Sales/Observer/Backend/SubtractQtyFromQuotesObserver.php +++ b/app/code/Magento/Sales/Observer/Backend/SubtractQtyFromQuotesObserver.php @@ -31,6 +31,6 @@ public function __construct(\Magento\Quote\Model\ResourceModel\Quote $quote) public function execute(\Magento\Framework\Event\Observer $observer) { $product = $observer->getEvent()->getProduct(); - $this->_quote->substractProductFromQuotes($product); + $this->_quote->subtractProductFromQuotes($product); } } diff --git a/app/code/Magento/Sales/Test/Unit/Observer/Backend/SubtractQtyFromQuotesObserverTest.php b/app/code/Magento/Sales/Test/Unit/Observer/Backend/SubtractQtyFromQuotesObserverTest.php index a6a828c888fc0..949121eadee44 100644 --- a/app/code/Magento/Sales/Test/Unit/Observer/Backend/SubtractQtyFromQuotesObserverTest.php +++ b/app/code/Magento/Sales/Test/Unit/Observer/Backend/SubtractQtyFromQuotesObserverTest.php @@ -48,7 +48,7 @@ public function testSubtractQtyFromQuotes() ['getId', 'getStatus', '__wakeup'] ); $this->_eventMock->expects($this->once())->method('getProduct')->will($this->returnValue($productMock)); - $this->_quoteMock->expects($this->once())->method('substractProductFromQuotes')->with($productMock); + $this->_quoteMock->expects($this->once())->method('subtractProductFromQuotes')->with($productMock); $this->_model->execute($this->_observerMock); } } From 0eb2b6bbb275558ff2f5438d4559bdf9bd4dde6f Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 23 May 2018 14:39:03 +0300 Subject: [PATCH 170/333] MAGETWO-92005: Make "Other Payment Methods" section in Admin expanded by default --- .../Magento/Paypal/etc/adminhtml/system.xml | 1 + .../Page/AdminConfigPaymentMethodsPage.xml | 12 +++++++++ .../Section/OtherPaymentsConfigSection.xml | 14 ++++++++++ .../Test/AdminConfigPaymentsSectionState.xml | 26 +++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Page/AdminConfigPaymentMethodsPage.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Section/OtherPaymentsConfigSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Test/AdminConfigPaymentsSectionState.xml diff --git a/app/code/Magento/Paypal/etc/adminhtml/system.xml b/app/code/Magento/Paypal/etc/adminhtml/system.xml index 1c8da8127f8fe..c1ff4c9b1c6ca 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system.xml @@ -32,6 +32,7 @@ </group> <group id="other_payment_methods" translate="label" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Other Payment Methods:</label> + <attribute type="expanded">1</attribute> <fieldset_css>paypal-top-section payments-other-header</fieldset_css> <frontend_model>\Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> </group> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Page/AdminConfigPaymentMethodsPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Page/AdminConfigPaymentMethodsPage.xml new file mode 100644 index 0000000000000..9fa1a71e3441b --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Page/AdminConfigPaymentMethodsPage.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminConfigPaymentMethodsPage" url="admin/system_config/edit/section/payment/" area="admin" module="Magento_Config"> + <section name="OtherPaymentsConfigSection"/> + </page> +</pages> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Section/OtherPaymentsConfigSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Section/OtherPaymentsConfigSection.xml new file mode 100644 index 0000000000000..024c1317dba2f --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Section/OtherPaymentsConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="OtherPaymentsConfigSection"> + <element name="expandedTab" type="button" selector="#payment_us_other_payment_methods-head.open"/> + </section> +</sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Test/AdminConfigPaymentsSectionState.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Test/AdminConfigPaymentsSectionState.xml new file mode 100644 index 0000000000000..90c8dd5896198 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Paypal/Test/AdminConfigPaymentsSectionState.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminConfigPaymentsSectionState"> + <annotations> + <description value="Other Payment Methods section in Admin expanded by default"/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-92043"/> + </annotations> + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <seeElement selector="{{OtherPaymentsConfigSection.expandedTab}}" stepKey="seeSectionExpanded"/> + </test> +</tests> From 5f405b438e6a12fa1331946bf204c2112f6f708b Mon Sep 17 00:00:00 2001 From: Facundo Capua <facundocapua@gmail.com> Date: Wed, 23 May 2018 09:19:00 -0300 Subject: [PATCH 171/333] Fix code styles issues --- .../testsuite/Magento/GraphQl/Catalog/CategoryTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index f6121bea2d830..033b48b5ab9b5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -376,7 +376,7 @@ private function assertAttributes($actualResponse) 'special_to_date', ]; - foreach($eavAttributes as $eavAttribute){ + foreach ($eavAttributes as $eavAttribute) { $this->assertArrayHasKey($eavAttribute, $actualResponse); } } @@ -408,7 +408,7 @@ private function assertResponseFields($actualResponse, $assertionMap) private function eavAttributesToGraphQlSchemaFieldTranslator($attributeCode) { - if(isset($this->eavAttributesToGraphQlSchemaFieldMap[$attributeCode])){ + if (isset($this->eavAttributesToGraphQlSchemaFieldMap[$attributeCode])) { return $this->eavAttributesToGraphQlSchemaFieldMap[$attributeCode]; } From 998f3fc93b9caa4f92b96eac57882f8ccd0ac9f1 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 23 May 2018 15:22:41 +0300 Subject: [PATCH 172/333] MAGETWO-90729: Imported products are incorrectly in stock if 'allow_backorders' enabled --- .../Magento/CatalogImportExport/Model/Import/Product.php | 8 +++----- .../Catalog/_files/product_simple_out_of_stock.php | 6 +++--- .../CatalogImportExport/Model/Import/ProductTest.php | 6 ++++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 3c591a4f4e08c..f618bded9655e 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2869,14 +2869,12 @@ private function formatStockDataForRow(array $rowData): array if ($this->stockConfiguration->isQty($this->skuProcessor->getNewSku($sku)['type_id'])) { $stockItemDo->setData($row); - $row['is_in_stock'] = $stockItemDo->getBackorders() && isset($row['is_in_stock']) + $row['is_in_stock'] = isset($row['is_in_stock']) && $stockItemDo->getBackorders() ? $row['is_in_stock'] : $this->stockStateProvider->verifyStock($stockItemDo); if ($this->stockStateProvider->verifyNotification($stockItemDo)) { - $row['low_stock_date'] = $this->dateTime->gmDate( - 'Y-m-d H:i:s', - (new \DateTime())->getTimestamp() - ); + $row['low_stock_date'] = + gmdate(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT, (new \DateTime())->getTimestamp()); } $row['stock_status_changed_auto'] = (int)!$this->stockStateProvider->verifyStock($stockItemDo); } else { 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 46af93ab01c76..6630c0d69e34f 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 @@ -18,8 +18,7 @@ $product = $objectManager->create(\Magento\Catalog\Model\Product::class); $product->isObjectNew(true); $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) - ->setId(1) - ->setAttributeSetId(4) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setWebsiteIds([1]) ->setName('Simple Product') ->setSku('simple-out-of-stock') @@ -40,7 +39,8 @@ 'is_qty_decimal' => 0, 'is_in_stock' => 0, ] - )->setCanSaveCustomOptions(true) + ) + ->setCanSaveCustomOptions(true) ->setHasOptions(true); /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepositoryFactory */ 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 a168ef3140f5f..7a231fef638f0 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -2185,7 +2185,7 @@ public function testProductsWithMultipleStoresWhenMediaIsDisabled(): void /** * Test that imported product stock status with backorders functionality enabled can be set to 'out of stock'. * - * @magentoDataIsolation enabled + * @magentoDbIsolation enabled * @magentoAppIsolation enabled * * @return void @@ -2220,7 +2220,9 @@ private function importFile(string $fileName): void 'entity' => 'catalog_product', \Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE => 1, ] - )->setSource($source)->validateData(); + ) + ->setSource($source) + ->validateData(); $this->assertTrue($errors->getErrorsCount() == 0); From ec3eb1ae80716712b1fca54a7becc62a4686815b Mon Sep 17 00:00:00 2001 From: Saravanan <saravananv14@live.com> Date: Wed, 23 May 2018 18:11:45 +0530 Subject: [PATCH 173/333] Fixed product tier pricing pagination issue in admin --- .../js/components/dynamic-rows-tier-price.js | 23 ------------------- .../base/web/js/dynamic-rows/dynamic-rows.js | 2 ++ 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js index 4c94b02576b3f..b5c0e7a95d401 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js @@ -28,29 +28,6 @@ define([ }); this.labels(labels); - }, - - /** @inheritdoc */ - changePage: function (page) { - this.clear(); - - if (page === 1 && !this.recordData().length) { - return false; - } - - if (~~page > this.pages()) { - this.currentPage(this.pages()); - - return false; - } else if (~~page < 1) { - this.currentPage(1); - - return false; - } - - this.initChildren(); - - return true; } }); }); diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js index 680b6a2b1e54e..75a9c920c6531 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js @@ -721,6 +721,8 @@ define([ * @param {Number} page - current page */ changePage: function (page) { + this.clear(); + if (page === 1 && !this.recordData().length) { return false; } From a9ba5b894d967c3bd0cf743143cb1f4ea26df26f Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Wed, 23 May 2018 16:09:02 +0300 Subject: [PATCH 174/333] MAGETWO-91934: Unlock Locales Editing when SCD on Demand Mode is Enabled - remove duplication --- app/code/Magento/Backend/etc/adminhtml/di.xml | 2 +- ...ConcealInProductionWithoutScdOnDemand.php} | 68 ++------ ...cealInProductionWithoutScdOnDemandTest.php | 148 ++++++++++++++++++ .../ElementVisibility/ConcealScdFieldTest.php | 140 ----------------- app/code/Magento/Config/etc/adminhtml/di.xml | 3 +- 5 files changed, 166 insertions(+), 195 deletions(-) rename app/code/Magento/Config/Model/Config/Structure/ElementVisibility/{ConcealScdField.php => ConcealInProductionWithoutScdOnDemand.php} (56%) create mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php delete mode 100644 app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index c49ad60694279..33bacc6c8cc4b 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -149,7 +149,7 @@ </argument> </arguments> </type> - <type name="Magento\Config\Model\Config\Structure\ElementVisibility\ConcealScdField"> + <type name="Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProductionWithoutScdOnDemand"> <arguments> <argument name="configs" xsi:type="array"> <item name="general/locale/code" xsi:type="const">Magento\Config\Model\Config\Structure\ElementVisibilityInterface::DISABLED</item> diff --git a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php similarity index 56% rename from app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php rename to app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php index af1ca50b5910d..fcd055088b10d 100755 --- a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealScdField.php +++ b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php @@ -14,10 +14,10 @@ /** * Defines status of visibility of form elements on Stores > Settings > Configuration page - * in Admin Panel in Production mode. + * when Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION is enabled. * @api */ -class ConcealScdField implements ElementVisibilityInterface +class ConcealInProductionWithoutScdOnDemand implements ElementVisibilityInterface { /** * The list of form element paths with concrete visibility status. @@ -39,13 +39,6 @@ class ConcealScdField implements ElementVisibilityInterface */ private $configs = []; - /** - * The object that has information about the state of the system. - * - * @var State - */ - private $state; - /** * * The list of form element paths which ignore visibility status. @@ -66,24 +59,31 @@ class ConcealScdField implements ElementVisibilityInterface */ private $exemptions = []; + /** + * @var ConcealInProduction + */ + private $concealInProduction; + /** * @var DeploymentConfig */ private $deploymentConfig; /** + * @param ConcealInProductionFactory $concealInProductionFactory * @param State $state The object that has information about the state of the system * @param DeploymentConfig $deploymentConfig Deployment configuration reader * @param array $configs The list of form element paths with concrete visibility status. * @param array $exemptions The list of form element paths which ignore visibility status. */ public function __construct( - State $state, + ConcealInProductionFactory $concealInProductionFactory, DeploymentConfig $deploymentConfig, array $configs = [], array $exemptions = [] ) { - $this->state = $state; + $this->concealInProduction = $concealInProductionFactory + ->create(['configs' => $configs, 'exemptions' => $exemptions]); $this->deploymentConfig = $deploymentConfig; $this->configs = $configs; $this->exemptions = $exemptions; @@ -94,23 +94,9 @@ public function __construct( */ public function isHidden($path): bool { - $path = $this->normalizePath($path); - if ($this->state->getMode() === State::MODE_PRODUCTION - && !$this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) - && preg_match('/(?<group>(?<section>.*?)\/.*?)\/.*?/', $path, $match)) { - $group = $match['group']; - $section = $match['section']; - $exemptions = array_keys($this->exemptions); - $checkedItems = []; - foreach ([$path, $group, $section] as $itemPath) { - $checkedItems[] = $itemPath; - if (!empty($this->configs[$itemPath])) { - return $this->configs[$itemPath] === static::HIDDEN - && empty(array_intersect($checkedItems, $exemptions)); - } - } + if (!$this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)) { + return $this->concealInProduction->isHidden($path); } - return false; } @@ -119,33 +105,9 @@ public function isHidden($path): bool */ public function isDisabled($path): bool { - $path = $this->normalizePath($path); - if ($this->state->getMode() === State::MODE_PRODUCTION - && !$this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)) { - while ($path) { - if (!empty($this->configs[$path])) { - return $this->configs[$path] === static::DISABLED; - } - - $position = strripos($path, '/'); - if ($position === false) { - break; - } - $path = $position !== false ? substr($path, 0, $position) : null; - } + if (!$this->deploymentConfig->getConfigData(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION)) { + return $this->concealInProduction->isDisabled($path); } - return false; } - - /** - * Returns normalized path. - * - * @param string $path The path to be normalized - * @return string The normalized path - */ - private function normalizePath($path) - { - return trim($path, '/'); - } } diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php new file mode 100644 index 0000000000000..49cebb7f5ff0a --- /dev/null +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php @@ -0,0 +1,148 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Config\Test\Unit\Model\Config\Structure\ElementVisibility; + +use Magento\Framework\App\DeploymentConfig; +use \Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction; +use \Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProductionFactory; +use Magento\Framework\Config\ConfigOptionsListConstants as Constants; +use Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProductionWithoutScdOnDemand; +use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; + +class ConcealInProductionWithoutScdOnDemandTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ConcealInProduction|\PHPUnit_Framework_MockObject_MockObject + */ + private $concealInProductionMock; + + /** + * @var ConcealInProductionWithoutScdOnDemand + */ + private $model; + + /** + * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $deploymentConfigMock; + + protected function setUp() + { + $concealInProductionFactoryMock = $this->createMock(ConcealInProductionFactory::class); + + $this->concealInProductionMock = $this->createMock(ConcealInProduction::class); + + $this->deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); + + $configs = [ + 'section1/group1/field1' => ElementVisibilityInterface::DISABLED, + 'section1/group1' => ElementVisibilityInterface::HIDDEN, + 'section1' => ElementVisibilityInterface::DISABLED, + 'section1/group2' => 'no', + 'section2/group1' => ElementVisibilityInterface::DISABLED, + 'section2/group2' => ElementVisibilityInterface::HIDDEN, + 'section3' => ElementVisibilityInterface::HIDDEN, + 'section3/group1/field1' => 'no', + ]; + $exemptions = [ + 'section1/group1/field3' => '', + 'section1/group2/field1' => '', + 'section2/group2/field1' => '', + 'section3/group2' => '', + ]; + + $concealInProductionFactoryMock->expects($this->any()) + ->method('create') + ->with(['configs' => $configs, 'exemptions' => $exemptions]) + ->willReturn($this->concealInProductionMock); + + $this->model = new ConcealInProductionWithoutScdOnDemand( + $concealInProductionFactoryMock, + $this->deploymentConfigMock, + $configs, + $exemptions + ); + } + + public function testIsHiddenScdOnDemandEnabled(): void + { + $path = 'section1/group1/field1'; + $this->deploymentConfigMock->expects($this->once()) + ->method('getConfigData') + ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) + ->willReturn(true); + $this->concealInProductionMock->expects($this->never()) + ->method('isHidden'); + + $this->assertFalse($this->model->isHidden($path)); + } + + public function testIsDisabledScdOnDemandEnabled(): void + { + $path = 'section1/group1/field1'; + $this->deploymentConfigMock->expects($this->once()) + ->method('getConfigData') + ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) + ->willReturn(true); + $this->concealInProductionMock->expects($this->never()) + ->method('isDisabled'); + + $this->assertFalse($this->model->isDisabled($path)); + } + + /** + * @param bool $isHidden + * + * @dataProvider visibilityDataProvider + */ + public function testIsHiddenScdOnDemandDisabled($isHidden): void + { + $path = 'section1/group1/field1'; + $this->deploymentConfigMock->expects($this->once()) + ->method('getConfigData') + ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) + ->willReturn(false); + $this->concealInProductionMock->expects($this->once()) + ->method('isHidden') + ->with($path) + ->willReturn($isHidden); + + $this->assertSame($isHidden, $this->model->isHidden($path)); + } + + /** + * @param bool $isDisabled + * + * @dataProvider visibilityDataProvider + */ + public function testIsDisabledScdOnDemandDisabled($isDisabled): void + { + $path = 'section1/group1/field1'; + $this->deploymentConfigMock->expects($this->once()) + ->method('getConfigData') + ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) + ->willReturn(false); + $this->concealInProductionMock->expects($this->once()) + ->method('isDisabled') + ->with($path) + ->willReturn($isDisabled); + + $this->assertSame($isDisabled, $this->model->isDisabled($path)); + } + + /** + * @return array + */ + public function visibilityDataProvider(): array + { + return [ + [true], + [false], + ]; + } +} diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php deleted file mode 100644 index 1caa7956a4079..0000000000000 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealScdFieldTest.php +++ /dev/null @@ -1,140 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Config\Test\Unit\Model\Config\Structure\ElementVisibility; - -use Magento\Framework\App\DeploymentConfig; -use Magento\Framework\App\State; -use Magento\Framework\Config\ConfigOptionsListConstants as Constants; -use Magento\Config\Model\Config\Structure\ElementVisibility\ConcealScdField; -use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; - -class ConcealScdFieldTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var State|\PHPUnit_Framework_MockObject_MockObject - */ - private $stateMock; - - /** - * @var ConcealScdField - */ - private $model; - - /** - * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject - */ - private $deploymentConfigMock; - - protected function setUp() - { - $this->stateMock = $this->createMock(State::class); - - $this->deploymentConfigMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); - - $configs = [ - 'section1/group1/field1' => ElementVisibilityInterface::DISABLED, - 'section1/group1' => ElementVisibilityInterface::HIDDEN, - 'section1' => ElementVisibilityInterface::DISABLED, - 'section1/group2' => 'no', - 'section2/group1' => ElementVisibilityInterface::DISABLED, - 'section2/group2' => ElementVisibilityInterface::HIDDEN, - 'section3' => ElementVisibilityInterface::HIDDEN, - 'section3/group1/field1' => 'no', - ]; - $exemptions = [ - 'section1/group1/field3' => '', - 'section1/group2/field1' => '', - 'section2/group2/field1' => '', - 'section3/group2' => '', - ]; - - $this->model = new ConcealScdField($this->stateMock, $this->deploymentConfigMock, $configs, $exemptions); - } - - /** - * @param string $path - * @param string $mageMode - * @param int $scdOnDemand - * @param bool $isDisabled - * @param bool $isHidden - * @dataProvider disabledDataProvider - */ - public function testCheckVisibility( - string $path, - string $mageMode, - int $scdOnDemand, - bool $isHidden, - bool $isDisabled - ): void { - $this->stateMock->expects($this->any()) - ->method('getMode') - ->willReturn($mageMode); - $this->deploymentConfigMock->expects($this->any()) - ->method('getConfigData') - ->with(Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION) - ->willReturn($scdOnDemand); - - $this->assertSame($isHidden, $this->model->isHidden($path)); - $this->assertSame($isDisabled, $this->model->isDisabled($path)); - } - - /** - * @return array - */ - public function disabledDataProvider(): array - { - return [ - //visibility of field 'section1/group1/field1' should be applied - ['section1/group1/field1', State::MODE_PRODUCTION, 1, false, false], - ['section1/group1/field1', State::MODE_PRODUCTION, 0, false, true], - ['section1/group1/field1', State::MODE_DEFAULT, 0, false, false], - ['section1/group1/field1', State::MODE_DEFAULT, 1, false, false], - ['section1/group1/field1', State::MODE_DEVELOPER, 0, false, false], - ['section1/group1/field1', State::MODE_DEVELOPER, 1, false, false], - //visibility of group 'section1/group1' should be applied - ['section1/group1/field2', State::MODE_PRODUCTION, 1, false, false], - ['section1/group1/field2', State::MODE_PRODUCTION, 0, true, false], - ['section1/group1/field2', State::MODE_DEFAULT, 0, false, false], - ['section1/group1/field2', State::MODE_DEFAULT, 1, false, false], - ['section1/group1/field2', State::MODE_DEVELOPER, 0, false, false], - ['section1/group1/field2', State::MODE_DEVELOPER, 1, false, false], - //exemption should be applied for section1/group2/field1 - ['section1/group2/field1', State::MODE_PRODUCTION, 1, false, false], - ['section1/group2/field1', State::MODE_PRODUCTION, 0, false, false], - ['section1/group2/field1', State::MODE_DEFAULT, 0, false, false], - ['section1/group2/field1', State::MODE_DEFAULT, 1, false, false], - ['section1/group2/field1', State::MODE_DEVELOPER, 0, false, false], - ['section1/group2/field1', State::MODE_DEVELOPER, 1, false, false], - //as 'section1/group2' has neither Disable nor Hidden rule, this field should be visible - ['section1/group2/field2', State::MODE_PRODUCTION, 1, false, false], - ['section1/group2/field2', State::MODE_PRODUCTION, 0, false, false], - //exemption should be applied for section1/group1/field3 - ['section1/group1/field3', State::MODE_PRODUCTION, 1, false, false], - ['section1/group1/field3', State::MODE_PRODUCTION, 0, false, false], - //visibility of group 'section2/group1' should be applied - ['section2/group1/field1', State::MODE_PRODUCTION, 1, false, false], - ['section2/group1/field1', State::MODE_PRODUCTION, 0, false, true], - //exemption should be applied for section2/group2/field1 - ['section2/group2/field1', State::MODE_PRODUCTION, 1, false, false], - ['section2/group2/field1', State::MODE_PRODUCTION, 0, false, false], - //any rule should not be applied - ['section2/group3/field1', State::MODE_PRODUCTION, 1, false, false], - ['section2/group3/field1', State::MODE_PRODUCTION, 0, false, false], - //any rule should not be applied - ['section3/group1/field1', State::MODE_PRODUCTION, 1, false, false], - ['section3/group1/field1', State::MODE_PRODUCTION, 0, false, false], - //visibility of section 'section3' should be applied - ['section3/group1/field2', State::MODE_PRODUCTION, 1, false, false], - ['section3/group1/field2', State::MODE_PRODUCTION, 0, true, false], - //exception from 'section3/group2' should be applied - ['section3/group2/field1', State::MODE_PRODUCTION, 1, false, false], - ['section3/group2/field1', State::MODE_PRODUCTION, 0, false, false], - - ]; - } -} diff --git a/app/code/Magento/Config/etc/adminhtml/di.xml b/app/code/Magento/Config/etc/adminhtml/di.xml index c0cd92df0d8ca..d0264d27866dc 100644 --- a/app/code/Magento/Config/etc/adminhtml/di.xml +++ b/app/code/Magento/Config/etc/adminhtml/di.xml @@ -14,8 +14,9 @@ <type name="Magento\Config\Model\Config\Structure\ElementVisibilityComposite"> <arguments> <argument name="visibility" xsi:type="array"> + <item name="productionVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ConcealInProductionConfigList</item> <item name="productionVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction</item> - <item name="productionScdOnDemandVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealScdField</item> + <item name="productionScdOnDemandVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProductionWithoutScdOnDemand</item> </argument> </arguments> </type> From 7ed3d1d047dfb06b0b8771bee20fd3a6116e02cb Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Wed, 23 May 2018 16:21:36 +0300 Subject: [PATCH 175/333] MAGETWO-91934: Unlock Locales Editing when SCD on Demand Mode is Enabled - changes after review --- .../ConcealInProductionWithoutScdOnDemand.php | 53 +++---------------- ...cealInProductionWithoutScdOnDemandTest.php | 4 +- 2 files changed, 8 insertions(+), 49 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php index fcd055088b10d..29148a244dcc6 100755 --- a/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php +++ b/app/code/Magento/Config/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemand.php @@ -9,69 +9,30 @@ use Magento\Config\Model\Config\Structure\ElementVisibilityInterface; use Magento\Framework\App\DeploymentConfig; -use Magento\Framework\App\State; use Magento\Framework\Config\ConfigOptionsListConstants as Constants; /** * Defines status of visibility of form elements on Stores > Settings > Configuration page - * when Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION is enabled. + * when Constants::CONFIG_PATH_SCD_ON_DEMAND_IN_PRODUCTION is enabled + * otherwise rule from Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction is used + * @see \Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction + * * @api */ class ConcealInProductionWithoutScdOnDemand implements ElementVisibilityInterface { /** - * The list of form element paths with concrete visibility status. - * - * E.g. - * - * ```php - * [ - * 'general/locale/code' => ElementVisibilityInterface::DISABLED, - * 'general/country' => ElementVisibilityInterface::HIDDEN, - * ]; - * ``` - * - * It means that: - * - field Locale (in group Locale Options in section General) will be disabled - * - group Country Options (in section General) will be hidden - * - * @var array - */ - private $configs = []; - - /** - * - * The list of form element paths which ignore visibility status. - * - * E.g. - * - * ```php - * [ - * 'general/country/default' => '', - * ]; - * ``` - * - * It means that: - * - field 'default' in group Country Options (in section General) will be showed, even if all group(section) - * will be hidden. - * - * @var array - */ - private $exemptions = []; - - /** - * @var ConcealInProduction + * @var ConcealInProduction Element visibility rules in the Production mode */ private $concealInProduction; /** - * @var DeploymentConfig + * @var DeploymentConfig The application deployment configuration */ private $deploymentConfig; /** * @param ConcealInProductionFactory $concealInProductionFactory - * @param State $state The object that has information about the state of the system * @param DeploymentConfig $deploymentConfig Deployment configuration reader * @param array $configs The list of form element paths with concrete visibility status. * @param array $exemptions The list of form element paths which ignore visibility status. @@ -85,8 +46,6 @@ public function __construct( $this->concealInProduction = $concealInProductionFactory ->create(['configs' => $configs, 'exemptions' => $exemptions]); $this->deploymentConfig = $deploymentConfig; - $this->configs = $configs; - $this->exemptions = $exemptions; } /** diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php index 49cebb7f5ff0a..9d69a587f695d 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/ElementVisibility/ConcealInProductionWithoutScdOnDemandTest.php @@ -100,7 +100,7 @@ public function testIsDisabledScdOnDemandEnabled(): void * * @dataProvider visibilityDataProvider */ - public function testIsHiddenScdOnDemandDisabled($isHidden): void + public function testIsHiddenScdOnDemandDisabled(bool $isHidden): void { $path = 'section1/group1/field1'; $this->deploymentConfigMock->expects($this->once()) @@ -120,7 +120,7 @@ public function testIsHiddenScdOnDemandDisabled($isHidden): void * * @dataProvider visibilityDataProvider */ - public function testIsDisabledScdOnDemandDisabled($isDisabled): void + public function testIsDisabledScdOnDemandDisabled(bool $isDisabled): void { $path = 'section1/group1/field1'; $this->deploymentConfigMock->expects($this->once()) From f3c9417e0636a65326b0064540a15f210c6ba38d Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko <nsyvokonenko@magento.com> Date: Wed, 23 May 2018 16:41:39 +0300 Subject: [PATCH 176/333] MAGETWO-91934: Unlock Locales Editing when SCD on Demand Mode is Enabled - fix argument names in xml --- app/code/Magento/Config/etc/adminhtml/di.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Config/etc/adminhtml/di.xml b/app/code/Magento/Config/etc/adminhtml/di.xml index d0264d27866dc..5e54f177776ba 100644 --- a/app/code/Magento/Config/etc/adminhtml/di.xml +++ b/app/code/Magento/Config/etc/adminhtml/di.xml @@ -15,8 +15,8 @@ <arguments> <argument name="visibility" xsi:type="array"> <item name="productionVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ConcealInProductionConfigList</item> - <item name="productionVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction</item> - <item name="productionScdOnDemandVisibility" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProductionWithoutScdOnDemand</item> + <item name="concealInProduction" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProduction</item> + <item name="concealInProductionWithoutScdOnDemand" xsi:type="object">Magento\Config\Model\Config\Structure\ElementVisibility\ConcealInProductionWithoutScdOnDemand</item> </argument> </arguments> </type> From 16d903ccc91b2d0f0524324a3b268cb4532f50f8 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 23 May 2018 17:29:24 +0300 Subject: [PATCH 177/333] MAGETWO-90729: Imported products are incorrectly in stock if 'allow_backorders' enabled --- .../CatalogImportExport/Model/Import/Product.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index f618bded9655e..a93045f3bf2b0 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -15,6 +15,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem; +use Magento\Framework\Intl\DateTimeFactory; use Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor; use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface; use Magento\Framework\Stdlib\DateTime; @@ -724,6 +725,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ private $mediaProcessor; + /** + * @var DateTimeFactory + */ + private $dateTimeFactory; + /** * @param \Magento\Framework\Json\Helper\Data $jsonHelper * @param \Magento\ImportExport\Helper\Data $importExportData @@ -767,7 +773,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity * @param ImageTypeProcessor $imageTypeProcessor * @param MediaGalleryProcessor $mediaProcessor * @param StockItemImporterInterface|null $stockItemImporter - * + * @param DateTimeFactory $dateTimeFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -812,7 +818,8 @@ public function __construct( CatalogConfig $catalogConfig = null, ImageTypeProcessor $imageTypeProcessor = null, MediaGalleryProcessor $mediaProcessor = null, - StockItemImporterInterface $stockItemImporter = null + StockItemImporterInterface $stockItemImporter = null, + DateTimeFactory $dateTimeFactory = null ) { $this->_eventManager = $eventManager; $this->stockRegistry = $stockRegistry; @@ -868,6 +875,7 @@ public function __construct( ->_initSkus() ->initImagesArrayKeys(); $this->validator->init($this); + $this->dateTimeFactory = $dateTimeFactory ?? ObjectManager::getInstance()->get(DateTimeFactory::class); } /** @@ -2873,8 +2881,8 @@ private function formatStockDataForRow(array $rowData): array ? $row['is_in_stock'] : $this->stockStateProvider->verifyStock($stockItemDo); if ($this->stockStateProvider->verifyNotification($stockItemDo)) { - $row['low_stock_date'] = - gmdate(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT, (new \DateTime())->getTimestamp()); + $date = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); + $row['low_stock_date'] = $date->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT); } $row['stock_status_changed_auto'] = (int)!$this->stockStateProvider->verifyStock($stockItemDo); } else { From 001f7ebb36e2d3c71f1c44544f6d1828e9c0d425 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 23 May 2018 17:57:59 +0300 Subject: [PATCH 178/333] MAGETWO-90729: Imported products are incorrectly in stock if 'allow_backorders' enabled --- .../Magento/CatalogImportExport/Model/Import/Product.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index a93045f3bf2b0..078678af52f58 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -865,11 +865,8 @@ public function __construct( $string, $errorAggregator ); - $this->_optionEntity = isset( - $data['option_entity'] - ) ? $data['option_entity'] : $optionFactory->create( - ['data' => ['product_entity' => $this]] - ); + $this->_optionEntity = $data['option_entity'] ?? + $optionFactory->create(['data' => ['product_entity' => $this]]); $this->_initAttributeSets() ->_initTypeModels() ->_initSkus() From a9e5d85fcc2b691247a768bac2c2d2d2b01dfb3e Mon Sep 17 00:00:00 2001 From: Facundo Capua <facundocapua@gmail.com> Date: Wed, 23 May 2018 14:08:42 -0300 Subject: [PATCH 179/333] Removed unused and commented code --- .../testsuite/Magento/GraphQl/Catalog/CategoryTest.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 033b48b5ab9b5..8fa731fb0d7f2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -327,7 +327,6 @@ private function assertBaseFields($product, $actualResponse) ['response_field' => 'sku', 'expected_value' => $product->getSku()], ['response_field' => 'type_id', 'expected_value' => $product->getTypeId()], ['response_field' => 'updated_at', 'expected_value' => $product->getUpdatedAt()], -// ['response_field' => 'weight', 'expected_value' => $product->getWeight()], ]; $this->assertResponseFields($actualResponse, $assertionMap); @@ -405,13 +404,4 @@ private function assertResponseFields($actualResponse, $assertionMap) ); } } - - private function eavAttributesToGraphQlSchemaFieldTranslator($attributeCode) - { - if (isset($this->eavAttributesToGraphQlSchemaFieldMap[$attributeCode])) { - return $this->eavAttributesToGraphQlSchemaFieldMap[$attributeCode]; - } - - return $attributeCode; - } } From d56a57ba6da5c541794126e38854442b62f86474 Mon Sep 17 00:00:00 2001 From: Anshu Mishra <mishra.anshu1710@gmail.com> Date: Tue, 22 May 2018 19:34:50 +0530 Subject: [PATCH 180/333] typo correction --- .../Magento/Catalog/Model/ResourceModel/Product/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 0ce67a96a99cc..9b87515450a12 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1570,7 +1570,7 @@ protected function getEntityPkName(\Magento\Eav\Model\Entity\AbstractEntity $ent } /** - * Add requere tax percent flag for product collection + * Add require tax percent flag for product collection * * @return $this */ From 3b04ce6874141e598e465b9bfeb91e1b52e05cb6 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 24 May 2018 11:42:42 +0300 Subject: [PATCH 181/333] MAGETWO-90729: Imported products are incorrectly in stock if 'allow_backorders' enabled --- app/code/Magento/CatalogImportExport/Model/Import/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 078678af52f58..4d42330cd00bf 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2879,7 +2879,7 @@ private function formatStockDataForRow(array $rowData): array : $this->stockStateProvider->verifyStock($stockItemDo); if ($this->stockStateProvider->verifyNotification($stockItemDo)) { $date = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); - $row['low_stock_date'] = $date->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT); + $row['low_stock_date'] = $date->format(DateTime::DATETIME_PHP_FORMAT); } $row['stock_status_changed_auto'] = (int)!$this->stockStateProvider->verifyStock($stockItemDo); } else { From 7b1d1b624bd1e07cc60673b40ee996a7c0dcbd89 Mon Sep 17 00:00:00 2001 From: Vova Yatsyuk <vova.yatsyuk@gmail.com> Date: Sat, 12 May 2018 22:03:48 +0300 Subject: [PATCH 182/333] Removed mageMenu widget dependency --- .../frontend/web/js/product/breadcrumbs.js | 27 +++---------------- .../frontend/js/product/breadcrumbs.test.js | 18 ++++++------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js b/app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js index 7bf92fa2c089d..1a1ebfe55b29c 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js @@ -19,23 +19,6 @@ define([ menuContainer: '[data-action="navigation"] > ul' }, - /** @inheritdoc */ - _init: function () { - var menu, - originalInit = this._super.bind(this); - - // render breadcrumbs after navigation menu is loaded. - menu = $(this.options.menuContainer).data('mageMenu'); - - if (typeof menu === 'undefined') { - $(this.options.menuContainer).on('menucreate', function () { - originalInit(); - }); - } else { - this._super(); - } - }, - /** @inheritdoc */ _render: function () { this._appendCatalogCrumbs(); @@ -88,14 +71,10 @@ define([ * @private */ _getCategoryCrumb: function (menuItem) { - var categoryId = /(\d+)/i.exec(menuItem.attr('id'))[0], - categoryName = menuItem.text(), - categoryUrl = menuItem.attr('href'); - return { - 'name': 'category' + categoryId, - 'label': categoryName, - 'link': categoryUrl, + 'name': 'category', + 'label': menuItem.text(), + 'link': menuItem.attr('href'), 'title': '' }; }, diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/breadcrumbs.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/breadcrumbs.test.js index b0516af46bc6b..1dd83707a68c1 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/breadcrumbs.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/breadcrumbs.test.js @@ -18,7 +18,7 @@ define([ 'Magento_Theme/js/model/breadcrumb-list': jasmine.createSpyObj(['push']) }, defaultContext = require.s.contexts._, - menuItem = $('<li class="level0"><a href="http://localhost.com/cat1.html" id="ui-id-3">Cat1</a></li>')[0], + menuItem = $('<li class="level0"><a href="http://localhost.com/cat1.html">Cat1</a></li>')[0], /** * Create context object. @@ -112,14 +112,14 @@ define([ }); it('Check _getCategoryCrumb call', function () { - var item = $('<a href="http://localhost.com/cat1.html" id="ui-id-3">Cat1</a>'); + var item = $('<a href="http://localhost.com/cat1.html">Cat1</a>'); expect(widget).toBeDefined(); expect(widget).toEqual(jasmine.any(Function)); expect(widget.prototype._getCategoryCrumb).toBeDefined(); expect(widget.prototype._getCategoryCrumb(item)).toEqual(jasmine.objectContaining( { - 'name': 'category3', + 'name': 'category', 'label': 'Cat1', 'link': 'http://localhost.com/cat1.html' } @@ -228,7 +228,7 @@ define([ expect(result.length).toBe(1); expect(result[0]).toEqual(jasmine.objectContaining( { - 'name': 'category3', + 'name': 'category', 'label': 'Cat1', 'link': 'http://localhost.com/cat1.html' } @@ -239,10 +239,10 @@ define([ var result, menuItems = $( '<li class="level0 nav-1">' + - '<a href="http://localhost.com/cat1.html" id="ui-id-3">cat1</a>' + + '<a href="http://localhost.com/cat1.html">cat1</a>' + '<ul>' + '<li class="level1 nav-1-1">' + - '<a href="http://localhost.com/cat1/cat21.html" id="ui-id-9">cat21</a>' + + '<a href="http://localhost.com/cat1/cat21.html">cat21</a>' + '</li>' + '</ul>' + '</li>' @@ -258,14 +258,14 @@ define([ context = createContext(widget.prototype); getParentMenuHandler = widget.prototype._getParentMenuItem.bind(context); - result = getParentMenuHandler($('#ui-id-9')); + result = getParentMenuHandler($('[href="http://localhost.com/cat1/cat21.html"]')); expect(result).toBeDefined(); expect(result.length).toBe(1); expect(result[0].tagName.toLowerCase()).toEqual('a'); - expect(result.attr('id')).toEqual('ui-id-3'); + expect(result.attr('href')).toEqual('http://localhost.com/cat1.html'); - result = getParentMenuHandler($('#ui-id-3')); + result = getParentMenuHandler($('[href="http://localhost.com/cat1.html"]')); expect(result).toBeNull(); }); From 75960ce8d73e7d924d9f9bf820e4c82a2b0ff543 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Thu, 24 May 2018 11:54:46 +0300 Subject: [PATCH 183/333] 15210-Fixed product tier pricing pagination - Fix prevPage and nextPage method. Calling clear method was added to changePage method. --- .../Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js index 75a9c920c6531..7537107560cb4 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js @@ -764,7 +764,6 @@ define([ * Change page to next */ nextPage: function () { - this.clear(); this.currentPage(this.currentPage() + 1); }, @@ -772,7 +771,6 @@ define([ * Change page to previous */ previousPage: function () { - this.clear(); this.currentPage(this.currentPage() - 1); }, From 22746f0353a099aadf76bc1fd57547fd4e7aef70 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 18 May 2018 09:04:11 +0300 Subject: [PATCH 184/333] Prevent non-category links in breadcrumbs at product page #14994 --- app/code/Magento/Catalog/Plugin/Block/Topmenu.php | 1 + .../Catalog/view/frontend/web/js/product/breadcrumbs.js | 6 +++++- app/code/Magento/Theme/Block/Html/Topmenu.php | 4 ++++ .../Magento/Catalog/frontend/js/product/breadcrumbs.test.js | 6 +++++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Plugin/Block/Topmenu.php b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php index ceaae832fc36f..8cbe235e05f26 100644 --- a/app/code/Magento/Catalog/Plugin/Block/Topmenu.php +++ b/app/code/Magento/Catalog/Plugin/Block/Topmenu.php @@ -162,6 +162,7 @@ private function getCategoryAsArray($category, $currentCategory, $isParentActive 'url' => $this->catalogCategory->getCategoryUrl($category), 'has_active' => in_array((string)$category->getId(), explode('/', $currentCategory->getPath()), true), 'is_active' => $category->getId() == $currentCategory->getId(), + 'is_category' => true, 'is_parent_active' => $isParentActive ]; } diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js b/app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js index 7bf92fa2c089d..d3596cdd100ec 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/breadcrumbs.js @@ -16,6 +16,7 @@ define([ categoryUrlSuffix: '', useCategoryPathInUrl: false, product: '', + categoryItemSelector: '.category-item', menuContainer: '[data-action="navigation"] > ul' }, @@ -163,7 +164,10 @@ define([ categoryMenuItem = null; if (categoryUrl && menu.length) { - categoryMenuItem = menu.find('a[href="' + categoryUrl + '"]'); + categoryMenuItem = menu.find( + this.options.categoryItemSelector + + ' > a[href="' + categoryUrl + '"]' + ); } return categoryMenuItem; diff --git a/app/code/Magento/Theme/Block/Html/Topmenu.php b/app/code/Magento/Theme/Block/Html/Topmenu.php index 0aaaf34a05922..0dca0f8606a8c 100644 --- a/app/code/Magento/Theme/Block/Html/Topmenu.php +++ b/app/code/Magento/Theme/Block/Html/Topmenu.php @@ -309,6 +309,10 @@ protected function _getMenuItemClasses(\Magento\Framework\Data\Tree\Node $item) $classes[] = 'level' . $item->getLevel(); $classes[] = $item->getPositionClass(); + if ($item->getIsCategory()) { + $classes[] = 'category-item'; + } + if ($item->getIsFirst()) { $classes[] = 'first'; } diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/breadcrumbs.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/breadcrumbs.test.js index b0516af46bc6b..2b0ee8ca7b9c0 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/breadcrumbs.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/frontend/js/product/breadcrumbs.test.js @@ -18,7 +18,11 @@ define([ 'Magento_Theme/js/model/breadcrumb-list': jasmine.createSpyObj(['push']) }, defaultContext = require.s.contexts._, - menuItem = $('<li class="level0"><a href="http://localhost.com/cat1.html" id="ui-id-3">Cat1</a></li>')[0], + menuItem = $( + '<li class="level0 category-item">' + + '<a href="http://localhost.com/cat1.html" id="ui-id-3">Cat1</a>' + + '</li>' + )[0], /** * Create context object. From 9ebdcc5dfb83794b6c1b7ef66e0b3784257c3174 Mon Sep 17 00:00:00 2001 From: Leandro Ferraz Luvisotto <l.luvisotto@youwe.nl> Date: Thu, 24 May 2018 14:03:30 +0200 Subject: [PATCH 185/333] Duplicate array keys --- .../Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index 8d65153d7ba20..a370cbea13c2b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -1336,7 +1336,6 @@ public function testSaveExistingWithMediaGalleryEntries() $expectedResult = [ [ - 'value_id' => 5, 'value_id' => 5, "label" => "new_label_text", 'file' => 'filename1', From 8e1487927b2a71f907ecd4393ea16f90532872b5 Mon Sep 17 00:00:00 2001 From: Leandro Ferraz Luvisotto <l.luvisotto@youwe.nl> Date: Thu, 24 May 2018 14:16:25 +0200 Subject: [PATCH 186/333] Return tag misspelled --- .../ReleaseNotification/Ui/Renderer/NotificationRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ReleaseNotification/Ui/Renderer/NotificationRenderer.php b/app/code/Magento/ReleaseNotification/Ui/Renderer/NotificationRenderer.php index a43b33b5a8cdf..c4760bd9d28c3 100644 --- a/app/code/Magento/ReleaseNotification/Ui/Renderer/NotificationRenderer.php +++ b/app/code/Magento/ReleaseNotification/Ui/Renderer/NotificationRenderer.php @@ -174,7 +174,7 @@ private function buildFooter(array $footer) * correct HTML format. * * @param string $content - * @returns string + * @return string */ private function formatContentWithLinks($content) { From 3da1ebed472c1f112eb8af06ea82c8903f82023b Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 24 May 2018 16:27:52 +0300 Subject: [PATCH 187/333] MAGETWO-90798: Customer is redirected to 404 from Catalog page if switches to the Store with another root Category --- .../Plugin/Store/Block/Switcher.php | 86 +++++++++++++++++ .../CatalogUrlRewrite/etc/frontend/di.xml | 12 +++ .../Plugin/Store/Block/SwitcherTest.php | 56 +++++++++++ .../two_categories_per_two_store_groups.php | 93 +++++++++++++++++++ ...tegories_per_two_store_groups_rollback.php | 55 +++++++++++ 5 files changed, 302 insertions(+) create mode 100644 app/code/Magento/CatalogUrlRewrite/Plugin/Store/Block/Switcher.php create mode 100644 app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Store/Block/SwitcherTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/Store/Block/Switcher.php b/app/code/Magento/CatalogUrlRewrite/Plugin/Store/Block/Switcher.php new file mode 100644 index 0000000000000..44213c007551c --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Plugin/Store/Block/Switcher.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Plugin\Store\Block; + +use Magento\Framework\Data\Helper\PostHelper; +use Magento\Store\Api\StoreResolverInterface; +use Magento\Store\Model\Store; +use Magento\UrlRewrite\Model\UrlFinderInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\Framework\App\Request\Http as HttpRequest; + +/** + * Plugin makes connection between Store and UrlRewrite modules + * because Magento\Store\Block\Switcher should not know about UrlRewrite module functionality. + */ +class Switcher +{ + /** + * @var PostHelper + */ + private $postHelper; + + /** + * @var UrlFinderInterface + */ + private $urlFinder; + + /** + * @var HttpRequest + */ + private $request; + + /** + * @param PostHelper $postHelper + * @param UrlFinderInterface $urlFinder + * @param HttpRequest $request + */ + public function __construct( + PostHelper $postHelper, + UrlFinderInterface $urlFinder, + HttpRequest $request + ) { + $this->postHelper = $postHelper; + $this->urlFinder = $urlFinder; + $this->request = $request; + } + + /** + * @param \Magento\Store\Block\Switcher $subject + * @param string $result + * @param Store $store + * @param array $data + * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetTargetStorePostData( + \Magento\Store\Block\Switcher $subject, + string $result, + Store $store, + array $data = [] + ): string { + $data[StoreResolverInterface::PARAM_NAME] = $store->getCode(); + $currentUrl = $store->getCurrentUrl(true); + $baseUrl = $store->getBaseUrl(); + $urlPath = parse_url($currentUrl, PHP_URL_PATH); + $urlToSwitch = $currentUrl; + + //check only catalog pages + if ($this->request->getFrontName() === 'catalog') { + $currentRewrite = $this->urlFinder->findOneByData([ + UrlRewrite::REQUEST_PATH => ltrim($urlPath, '/'), + UrlRewrite::STORE_ID => $store->getId(), + ]); + if (null === $currentRewrite) { + $urlToSwitch = $baseUrl; + } + } + + return $this->postHelper->getPostData($urlToSwitch, $data); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml new file mode 100644 index 0000000000000..3a9122b2f748d --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="\Magento\Store\Block\Switcher"> + <plugin name="store_switcher_plugin" type="Magento\CatalogUrlRewrite\Plugin\Store\Block\Switcher"/> + </type> +</config> diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Store/Block/SwitcherTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Store/Block/SwitcherTest.php new file mode 100644 index 0000000000000..5b3879b592245 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Store/Block/SwitcherTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Plugin\Store\Block; + +/** + * Integration tests for Magento\CatalogUrlRewrite\Plugin\Store\Block\Switcher block. + */ +class SwitcherTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\TestFramework\ObjectManager + */ + private $objectManager; + + /** + * @var \Magento\Store\Block\Switcher + */ + private $model; + + /** + * @var \Magento\Store\Api\StoreRepositoryInterface + */ + private $storeRepository; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create(\Magento\Store\Block\Switcher::class); + $this->storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); + } + + /** + * Test that after switching from Store 1 to Store 2 with another root Category user gets correct store url. + * + * @magentoDataFixture Magento/Store/_files/store.php + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php + * @magentoAppArea frontend + * @return void + */ + public function testGetTargetStorePostData(): void + { + $storeCode = 'test'; + $store = $this->storeRepository->get($storeCode); + $result = json_decode($this->model->getTargetStorePostData($store), true); + + $this->assertContains($storeCode, $result['action']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php new file mode 100644 index 0000000000000..4036069679825 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Catalog\Model\Category $category */ +$category = $objectManager->create(\Magento\Catalog\Model\Category::class); +$category->isObjectNew(true); +$category->setId(333) + ->setCreatedAt('2014-06-23 09:50:07') + ->setName('Category 1') + ->setParentId(2) + ->setPath('1/2/3') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->setAvailableSortBy(['position']) + ->save(); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId(333) + ->setAttributeSetId(4) + ->setStoreId(1) + ->setWebsiteIds([1]) + ->setName('Simple Product Three') + ->setSku('simple333') + ->setPrice(10) + ->setWeight(18) + ->setStockData(['use_config_manage_stock' => 0]) + ->setCategoryIds([333]) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->save(); + +/** @var \Magento\Store\Model\Store $store */ +$store = $objectManager->create(\Magento\Store\Model\Store::class); + +$category->setStoreId($store->load('default')->getId()) + ->setName('category-default-store') + ->setUrlKey('category-default-store') + ->save(); + +$rootCategoryForTestStoreGroup = $objectManager->create(\Magento\Catalog\Model\Category::class); +$rootCategoryForTestStoreGroup->isObjectNew(true); +$rootCategoryForTestStoreGroup->setId(334) + ->setCreatedAt('2014-06-23 09:50:07') + ->setName('Category 2') + ->setParentId(1) + ->setPath('1/2/334') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->setAvailableSortBy(['position']) + ->save(); + +$rootCategoryForTestStoreGroup->setStoreId($store->load('test')->getId()) + ->setName('category-test-store') + ->setUrlKey('category-test-store') + ->save(); + +$storeCode = 'test'; +/** @var \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->create(\Magento\Catalog\Api\CategoryRepositoryInterface::class); +/** @var \Magento\Catalog\Api\Data\CategoryInterface $category */ +$category = $categoryRepository->get(334); +/** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ +$storeRepository = $objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); +/** @var \Magento\Store\Api\Data\StoreInterface $store */ +$store = $storeRepository->get($storeCode); + +/** @var \Magento\Store\Model\Group $storeGroup */ +$storeGroup = $objectManager->create(\Magento\Store\Model\Group::class); +$storeGroup->setWebsiteId('1'); +$storeGroup->setCode('test_store_group'); +$storeGroup->setName('Test Store Group'); +$storeGroup->setRootCategoryId($category->getId()); +$storeGroup->setDefaultStoreId($store->getId()); +$storeGroup->save(); + +$store->setGroupId($storeGroup->getId())->save(); + +/* Refresh stores memory cache */ +$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php new file mode 100644 index 0000000000000..0fe559aeed50c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Store\Model\Group; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Api\CategoryListInterface; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +// Delete product +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple333', false, null, true); + $product->delete(); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} +// Delete first category +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->addFilter('name', 'Category 1')->create(); +/** @var CategoryListInterface $categoryList */ +$categoryList = $objectManager->get(CategoryListInterface::class); +$categories = $categoryList->getList($searchCriteria)->getItems(); +/** @var CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +foreach ($categories as $category) { + $categoryRepository->delete($category); +} +// Delete second category +$searchCriteria = $searchCriteriaBuilder->addFilter('name', 'Category 2')->create(); +$categories = $categoryList->getList($searchCriteria)->getItems(); +foreach ($categories as $category) { + $categoryRepository->delete($category); +} +// Delete store group +/** @var Group $store */ +$storeGroup = $objectManager->get(Group::class); +$storeGroup->load('test_store_group', 'code'); +if ($storeGroup->getId()) { + $storeGroup->delete(); +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 949de44c98966ad0e4a3b30d1791158cffd1e02a Mon Sep 17 00:00:00 2001 From: Leandro Ferraz Luvisotto <l.luvisotto@youwe.nl> Date: Thu, 24 May 2018 15:45:02 +0200 Subject: [PATCH 188/333] Missing PHPDoc comment --- .../CatalogGraphQl/Model/Layer/CollectionProvider.php | 4 ++++ .../CollectionProcessor/ExtensibleEntityProcessor.php | 6 ++++++ .../ReportApplicationHandledExceptionToNewRelic.php | 3 +++ app/code/Magento/Sales/Setup/SalesSetup.php | 3 +++ .../Framework/Exception/AbstractAggregateException.php | 4 ++++ .../GraphQl/Query/Resolver/Argument/Filter/Operator.php | 2 +- .../Test/Unit/ServiceInputProcessor/SimpleConstructor.php | 4 ++++ .../Setup/Console/Command/GenerateFixturesCommand.php | 4 ++++ setup/src/Magento/Setup/Console/Style/MagentoStyle.php | 4 ++++ 9 files changed, 33 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Layer/CollectionProvider.php b/app/code/Magento/CatalogGraphQl/Model/Layer/CollectionProvider.php index 1e6fdf0e60e66..86645b0d36fdb 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Layer/CollectionProvider.php +++ b/app/code/Magento/CatalogGraphQl/Model/Layer/CollectionProvider.php @@ -29,6 +29,10 @@ class CollectionProvider implements \Magento\Catalog\Model\Layer\ItemCollectionP */ private $collectionProcessor; + /** + * @param \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface $collectionProcessor + * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory + */ public function __construct( \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface $collectionProcessor, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/ExtensibleEntityProcessor.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/ExtensibleEntityProcessor.php index 365d4f018ef4a..e35c8b3e20c51 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/ExtensibleEntityProcessor.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/ExtensibleEntityProcessor.php @@ -32,6 +32,12 @@ public function __construct(JoinProcessorInterface $joinProcessor) $this->joinProcessor = $joinProcessor; } + /** + * @param Collection $collection + * @param SearchCriteriaInterface $searchCriteria + * @param array $attributeNames + * @return Collection + */ public function process( Collection $collection, SearchCriteriaInterface $searchCriteria, diff --git a/app/code/Magento/NewRelicReporting/Model/Observer/ReportApplicationHandledExceptionToNewRelic.php b/app/code/Magento/NewRelicReporting/Model/Observer/ReportApplicationHandledExceptionToNewRelic.php index 724a488570207..ce7e95950c937 100644 --- a/app/code/Magento/NewRelicReporting/Model/Observer/ReportApplicationHandledExceptionToNewRelic.php +++ b/app/code/Magento/NewRelicReporting/Model/Observer/ReportApplicationHandledExceptionToNewRelic.php @@ -37,6 +37,9 @@ public function __construct( $this->newRelicWrapper = $newRelicWrapper; } + /** + * @param Observer $observer + */ public function execute(Observer $observer) { if ($this->config->isNewRelicEnabled()) { diff --git a/app/code/Magento/Sales/Setup/SalesSetup.php b/app/code/Magento/Sales/Setup/SalesSetup.php index bfc05c549ddb3..4be2b38b074e7 100644 --- a/app/code/Magento/Sales/Setup/SalesSetup.php +++ b/app/code/Magento/Sales/Setup/SalesSetup.php @@ -303,6 +303,9 @@ public function getEncryptor() return $this->encryptor; } + /** + * @return \Magento\Framework\DB\Adapter\AdapterInterface + */ public function getConnection() { return $this->getSetup()->getConnection(self::$connectionName); diff --git a/lib/internal/Magento/Framework/Exception/AbstractAggregateException.php b/lib/internal/Magento/Framework/Exception/AbstractAggregateException.php index 92a850511295a..ff142c5319006 100644 --- a/lib/internal/Magento/Framework/Exception/AbstractAggregateException.php +++ b/lib/internal/Magento/Framework/Exception/AbstractAggregateException.php @@ -78,6 +78,10 @@ public function addError(Phrase $phrase) return $this; } + /** + * @param LocalizedException $exception + * @return $this + */ public function addException(LocalizedException $exception) { $this->addErrorCalls++; diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/Filter/Operator.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/Filter/Operator.php index acd656e70ec62..ae2b8bfeabd1b 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/Filter/Operator.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/Filter/Operator.php @@ -65,7 +65,7 @@ public static function getOperators() : array return $type->getConstants(); } - /* + /** * Convert operator to string * * @return string diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php index f457a96e22a37..f875861e42b02 100644 --- a/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php +++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/ServiceInputProcessor/SimpleConstructor.php @@ -19,6 +19,10 @@ class SimpleConstructor */ private $name; + /** + * @param int $entityId + * @param string $name + */ public function __construct( int $entityId, string $name diff --git a/setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php b/setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php index e084cd8ff6cad..03b74f637a7da 100644 --- a/setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php +++ b/setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php @@ -157,6 +157,10 @@ private function clearChangelog() } } + /** + * @param \Magento\Setup\Fixtures\Fixture $fixture + * @param OutputInterface $output + */ private function executeFixture(\Magento\Setup\Fixtures\Fixture $fixture, OutputInterface $output) { $output->write('<info>' . $fixture->getActionTitle() . '... </info>'); diff --git a/setup/src/Magento/Setup/Console/Style/MagentoStyle.php b/setup/src/Magento/Setup/Console/Style/MagentoStyle.php index c3f292ce76e1e..e709d9158a049 100755 --- a/setup/src/Magento/Setup/Console/Style/MagentoStyle.php +++ b/setup/src/Magento/Setup/Console/Style/MagentoStyle.php @@ -518,6 +518,10 @@ private function autoPrependText() } } + /** + * @param $messages + * @return array + */ private function reduceBuffer($messages) { // We need to know if the two last chars are PHP_EOL From a95ef1ef246fde0995821a51b64c81ebd578eedf Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Tue, 22 May 2018 15:13:59 -0500 Subject: [PATCH 189/333] MC-62: Admin should be able to create a cart price rule of type Fixed amount discount --- .../StorefrontProductActionSection.xml | 2 +- .../AdminCreateFixedAmountDiscountTest.xml | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml index 337db47880c90..744a7237a1431 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml @@ -8,7 +8,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"> <section name="StorefrontProductActionSection"> - <element name="addToCart" type="button" selector="#product-addtocart-button"/> + <element name="addToCart" type="button" selector="#product-addtocart-button" timeout="30"/> <element name="addToCartButtonTitleIsAdding" type="text" selector="//button/span[text()='Adding...']"/> <element name="addToCartButtonTitleIsAdded" type="text" selector="//button/span[text()='Added']"/> <element name="addToCartButtonTitleIsAddToCart" type="text" selector="//button/span[text()='Add to Cart']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml new file mode 100644 index 0000000000000..36c824e5ae0c9 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminCreateFixedAmountDiscountTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Admin should be able to create a cart price rule of type Fixed amount discount"/> + <description value="Admin should be able to create a cart price rule of type Fixed amount discount"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-62"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + </after> + + <!-- Create a cart price rule for $10 Fixed amount discount --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="10" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!-- Create a product to check the storefront --> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + + <!-- Spot check the storefront --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToProductPage"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <waitForElementVisible selector="td[data-th='Discount']" stepKey="waitForDiscountElement"/> + <see selector="td[data-th='Discount']" userInput="-$10.00" stepKey="seeDiscountTotal"/> + </test> +</tests> From e5b218d661df75d8290696fd2216d3208e2b830c Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Wed, 23 May 2018 14:05:18 -0500 Subject: [PATCH 190/333] MC-62: Admin should be able to create a cart price rule of type Fixed amount discount - Made tests more robust - Clean up after better --- .../ActionGroup/AdminSalesRuleActionGroup.xml | 22 +++++++++++++++++++ .../AdminCartPriceRulesFormSection.xml | 2 ++ .../Section/AdminCartPriceRulesSection.xml | 1 + ...inCreateCartPriceRuleForCouponCodeTest.xml | 6 +++++ .../AdminCreateFixedAmountDiscountTest.xml | 8 ++++++- 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/AdminSalesRuleActionGroup.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/AdminSalesRuleActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/AdminSalesRuleActionGroup.xml new file mode 100644 index 0000000000000..93bdf29a8ba14 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/AdminSalesRuleActionGroup.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCartPriceRuleByName"> + <arguments> + <argument name="ruleName" type="string"/> + </arguments> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForPriceList"/> + <fillField selector="{{AdminCartPriceRulesSection.filterByNameInput}}" userInput="{{ruleName}}" 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/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml index fe845aad5857b..de2a2840b5a38 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml @@ -9,6 +9,8 @@ xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminCartPriceRulesFormSection"> <element name="save" type="button" selector="#save" timeout="30"/> + <element name="delete" type="button" selector="#delete" timeout="30"/> + <element name="modalAcceptButton" type="button" selector="button.action-accept" timeout="30"/> <!-- Rule Information (the main form on the page) --> <element name="ruleInformationHeader" type="button" selector="div[data-index='rule_information']" timeout="30"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesSection.xml index f96fd5dd7f4c6..882f6cd2dcf9b 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesSection.xml @@ -14,5 +14,6 @@ <element name="searchButton" type="button" selector="#promo_quote_grid button[title='Search']" timeout="30"/> <element name="nameColumns" type="text" selector="td[data-column='name']"/> <element name="rowContainingText" type="text" selector="//*[@id='promo_quote_grid_table']/tbody/tr[td//text()[contains(., '{{var1}}')]]" parameterized="true" timeout="30"/> + <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 68ae0c32b1ab5..805783f6e922f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -24,11 +24,17 @@ </before> <after> + <!-- Delete the cart price rule we made during the test --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{_defaultCoupon.code}}"/> + </actionGroup> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> <!-- Create a cart price rule based on a coupon code --> <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForPriceList"/> <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml index 36c824e5ae0c9..3457f780244ec 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml @@ -25,12 +25,17 @@ </before> <after> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <!-- Delete the cart price rule we made during the test --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{_defaultCoupon.code}}"/> + </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> <!-- Create a cart price rule for $10 Fixed amount discount --> <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> @@ -49,6 +54,7 @@ <!-- Spot check the storefront --> <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> From 28b89d042f2d1beed6e91376cf3fa3a55d6368fc Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Wed, 23 May 2018 14:50:03 -0500 Subject: [PATCH 191/333] MC-72: Admin should be able to create a cart price rule of type Percent of product price discount --- .../AdminCreatePercentOfProductPriceTest.xml | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreatePercentOfProductPriceTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreatePercentOfProductPriceTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreatePercentOfProductPriceTest.xml new file mode 100644 index 0000000000000..6587044ea4e44 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreatePercentOfProductPriceTest.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminCreatePercentOfProductPriceTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Admin should be able to create a cart price rule of type Percent of product price discount"/> + <description value="Admin should be able to create a cart price rule of type Percent of product price discount"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-72"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <!-- Delete the cart price rule we made during the test --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{_defaultCoupon.code}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a cart price rule for 50 percent of product price --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Percent of product price discount" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="50" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!-- Create a product to check the storefront --> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + + <!-- Spot check the storefront --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <waitForElementVisible selector="td[data-th='Discount']" stepKey="waitForDiscountElement"/> + <see selector="td[data-th='Discount']" userInput="-$61.50" stepKey="seeDiscountTotal"/> + </test> +</tests> From 22298af41b4ad09ea429470261955d13f4a7ac4c Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Thu, 24 May 2018 09:58:01 -0500 Subject: [PATCH 192/333] MC-91: Admin should be able to create a cart price rule of type Fixed amount discount for whole cart --- .../StorefrontProductActionSection.xml | 1 + ...CreateFixedAmountWholeCartDiscountTest.xml | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml index 744a7237a1431..f9e034aa3ae58 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml @@ -8,6 +8,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"> <section name="StorefrontProductActionSection"> + <element name="quantity" type="input" selector="#qty"/> <element name="addToCart" type="button" selector="#product-addtocart-button" timeout="30"/> <element name="addToCartButtonTitleIsAdding" type="text" selector="//button/span[text()='Adding...']"/> <element name="addToCartButtonTitleIsAdded" type="text" selector="//button/span[text()='Added']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml new file mode 100644 index 0000000000000..eb8f4d7529f8e --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminCreateFixedAmountWholeCartDiscountTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Admin should be able to create a cart price rule of type Fixed amount discount for whole cart"/> + <description value="Admin should be able to create a cart price rule of type Fixed amount discount for whole cart"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-91"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <!-- Delete the cart price rule we made during the test --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{SimpleSalesRule.name}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a cart price rule for $10 Fixed amount discount for whole cart --> + <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"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="19.99" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!-- Create a product to check the storefront --> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + + <!-- Spot check the storefront --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <!-- Use quantity 2 to verify that this discount is only applied once for the entire cart and not once per item --> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="fillQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <waitForElementVisible selector="td[data-th='Discount']" stepKey="waitForDiscountElement"/> + <see selector="td[data-th='Discount']" userInput="-$19.99" stepKey="seeDiscountTotal"/> + </test> +</tests> From bcd6828b69eccbc29fb4f133121806bd2e5b9ddd Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Thu, 24 May 2018 10:00:45 -0500 Subject: [PATCH 193/333] MC-91: Admin should be able to create a cart price rule of type Fixed amount discount for whole cart - Fix incorrect comment --- .../Test/AdminCreateFixedAmountWholeCartDiscountTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml index eb8f4d7529f8e..2d2bb24868032 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -33,7 +33,7 @@ <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> - <!-- Create a cart price rule for $10 Fixed amount discount for whole cart --> + <!-- Create a cart price rule for Fixed amount discount for whole cart --> <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> <waitForPageLoad stepKey="waitForRulesPage"/> <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> From 4c38cb8f0a6ddd7406a6b6ce1f8d8d7e25b9cff3 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Thu, 24 May 2018 10:39:45 -0500 Subject: [PATCH 194/333] MC-73: Admin should be able to create a cart price rule of type Buy X get Y free --- .../AdminCartPriceRulesFormSection.xml | 1 + .../Test/AdminCreateBuyXGetYFreeTest.xml | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateBuyXGetYFreeTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml index de2a2840b5a38..d95cdee4ef4c8 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml @@ -24,5 +24,6 @@ <element name="actionsHeader" type="button" selector="div[data-index='actions']" timeout="30"/> <element name="apply" type="select" selector="select[name='simple_action']"/> <element name="discountAmount" type="input" selector="input[name='discount_amount']"/> + <element name="discountStep" type="input" selector="input[name='discount_step']"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateBuyXGetYFreeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateBuyXGetYFreeTest.xml new file mode 100644 index 0000000000000..72ea11385e1ea --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateBuyXGetYFreeTest.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminCreateBuyXGetYFreeTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Admin should be able to create a cart price rule of type Buy X get Y free"/> + <description value="Admin should be able to create a cart price rule of type Buy X get Y free"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-73"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <!-- Delete the cart price rule we made during the test --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{_defaultCoupon.code}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a cart price rule of type Buy X get Y free --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <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="1" stepKey="fillDiscountAmount"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountStep}}" userInput="1" stepKey="fillDiscountStep"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + + <!-- Create a product to check the storefront --> + <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + + <!-- Spot check the storefront --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="fillQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <waitForElementVisible selector="td[data-th='Discount']" stepKey="waitForDiscountElement"/> + <see selector="td[data-th='Discount']" userInput="-$123.00" stepKey="seeDiscountTotal"/> + </test> +</tests> From 04b3d469f70f40f063796d965a35633abd03261e Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 24 May 2018 10:57:08 -0500 Subject: [PATCH 195/333] MAGETWO-91440: Unassigned Attribute Values Render as N/A in Product Comparison block - removing if that doesn't check the first item --- .../Magento/Catalog/Block/Product/Compare/ListCompare.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php index 3b0b0831b4034..6c54aa4e171ef 100644 --- a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php +++ b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php @@ -221,12 +221,9 @@ public function getProductAttributeValue($product, $attribute) */ public function hasAttributeValueForProducts($attribute) { - $index = 0; foreach ($this->getItems() as $item) { - if ($index++ != 0) { - if ($item->hasData($attribute->getAttributeCode())) { - return true; - } + if ($item->hasData($attribute->getAttributeCode())) { + return true; } } return false; From f9db7a02123ec24dcfe5d571ff940a0177f3960d Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Thu, 24 May 2018 14:12:11 -0500 Subject: [PATCH 196/333] MAGETWO-90358: Auto-generate ExtensionAttributes object --- .../PayPal/VaultDetailsHandlerTest.php | 8 ++++-- .../Response/VaultDetailsHandlerTest.php | 4 ++- .../Model/Paypal/Helper/QuoteUpdaterTest.php | 15 ++++++++++- .../Model/CustomOptions/CustomOptionTest.php | 27 +++++++++++++------ 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php index ebadc1703ecad..b3a7f8b9ee76a 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/PayPal/VaultDetailsHandlerTest.php @@ -88,7 +88,7 @@ protected function setUp() $this->paymentInfoMock = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() - ->setMethods(['__wakeup']) + ->setMethods(['__wakeup', 'getExtensionAttributes']) ->getMock(); $this->paymentTokenMock = $objectManager->getObject(PaymentToken::class); @@ -107,6 +107,10 @@ protected function setUp() ->setMethods(['create']) ->getMock(); + $this->paymentInfoMock->expects(self::any()) + ->method('getExtensionAttributes') + ->willReturn($this->paymentExtensionMock); + $this->subject = [ 'payment' => $this->paymentDataObjectMock, ]; @@ -184,7 +188,7 @@ public function testHandleWithoutToken() ->method('create'); $this->handler->handle($this->subject, $response); - self::assertNull($this->paymentInfoMock->getExtensionAttributes()); + self::assertNotNull($this->paymentInfoMock->getExtensionAttributes()); } /** diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php index 74592c6869ed3..c8ec52560be29 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php @@ -80,9 +80,11 @@ protected function setUp() $this->payment = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() - ->setMethods(['__wakeup']) + ->setMethods(['__wakeup', 'getExtensionAttributes']) ->getMock(); + $this->payment->expects(self::any())->method('getExtensionAttributes')->willReturn($this->paymentExtension); + $config = $this->getConfigMock(); $this->paymentHandler = new VaultDetailsHandler( diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php index 39863e6561c43..76bf5b659bda3 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php @@ -13,6 +13,7 @@ use Magento\Braintree\Observer\DataAssignObserver; use Magento\Braintree\Gateway\Config\PayPal\Config; use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater; +use Magento\Quote\Api\Data\CartExtensionInterface; /** * Class QuoteUpdaterTest @@ -281,7 +282,7 @@ private function updateQuoteStep(\PHPUnit_Framework_MockObject_MockObject $quote */ private function getQuoteMock() { - return $this->getMockBuilder(Quote::class) + $quoteMock = $this->getMockBuilder(Quote::class) ->setMethods( [ 'getIsVirtual', @@ -291,9 +292,21 @@ private function getQuoteMock() 'collectTotals', 'getShippingAddress', 'getBillingAddress', + 'getExtensionAttributes' ] )->disableOriginalConstructor() ->getMock(); + + $cartExtensionMock = $this->getMockBuilder(CartExtensionInterface::class) + ->setMethods(['setShippingAssignments']) + ->disableOriginalConstructor() + ->getMock(); + + $quoteMock->expects(self::any()) + ->method('getExtensionAttributes') + ->willReturn($cartExtensionMock); + + return $quoteMock; } /** diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php index 33b7892462f4c..7d365991076f7 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php @@ -7,6 +7,8 @@ use Magento\Catalog\Model\CustomOptions\CustomOption; use Magento\Catalog\Model\Webapi\Product\Option\Type\File\Processor as FileProcessor; +use Magento\Framework\Api\ExtensionAttributesFactory; +use Magento\Catalog\Api\Data\CustomOptionExtensionInterface; class CustomOptionTest extends \PHPUnit\Framework\TestCase { @@ -15,6 +17,13 @@ class CustomOptionTest extends \PHPUnit\Framework\TestCase */ protected $model; + /** @var \Magento\Framework\Api\ExtensionAttributesFactory | \PHPUnit_Framework_MockObject_MockObject */ + private $extensionAttributesFactoryMock; + + + /** @var \Magento\Catalog\Api\Data\CustomOptionExtensionInterface | \PHPUnit_Framework_MockObject_MockObject */ + private $extensionMock; + /** * @var FileProcessor | \PHPUnit_Framework_MockObject_MockObject */ @@ -30,7 +39,7 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $extensionAttributesFactory = $this->getMockBuilder(\Magento\Framework\Api\ExtensionAttributesFactory::class) + $this->extensionAttributesFactoryMock = $this->getMockBuilder(ExtensionAttributesFactory::class) ->disableOriginalConstructor() ->getMock(); @@ -52,10 +61,17 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->extensionMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\CustomOptionExtensionInterface::class) + ->setMethods(['getFileInfo']) + ->getMockForAbstractClass(); + + $this->extensionAttributesFactoryMock->expects(self::any()) + ->method('create')->willReturn($this->extensionMock); + $this->model = new CustomOption( $context, $registry, - $extensionAttributesFactory, + $this->extensionAttributesFactoryMock, $attributeValueFactory, $this->fileProcessor, $resource, @@ -84,14 +100,10 @@ public function testGetOptionValue() public function testGetOptionValueWithFileInfo() { - $customOption = $this->getMockBuilder(\Magento\Catalog\Api\Data\CustomOptionExtensionInterface::class) - ->setMethods(['getFileInfo']) - ->getMockForAbstractClass(); - $imageContent = $this->getMockBuilder(\Magento\Framework\Api\Data\ImageContentInterface::class) ->getMockForAbstractClass(); - $customOption->expects($this->once()) + $this->extensionMock->expects($this->once()) ->method('getFileInfo') ->willReturn($imageContent); @@ -112,7 +124,6 @@ public function testGetOptionValueWithFileInfo() ->with($imageContent) ->willReturn($imageResult); - $this->model->setExtensionAttributes($customOption); $this->model->setData(\Magento\Catalog\Api\Data\CustomOptionInterface::OPTION_VALUE, 'file'); $this->assertEquals($imageResult, $this->model->getOptionValue()); } From f9903be5ee1cbed1d6b4da3b3ffe22a1c1e93a07 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Thu, 24 May 2018 15:38:30 -0500 Subject: [PATCH 197/333] MAGETWO-90358: Auto-generate ExtensionAttributes object --- .../Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php index 7d365991076f7..fb90eaaaf1ec5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CustomOptions/CustomOptionTest.php @@ -20,7 +20,6 @@ class CustomOptionTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Api\ExtensionAttributesFactory | \PHPUnit_Framework_MockObject_MockObject */ private $extensionAttributesFactoryMock; - /** @var \Magento\Catalog\Api\Data\CustomOptionExtensionInterface | \PHPUnit_Framework_MockObject_MockObject */ private $extensionMock; From d25c9c13426415f1c725c5942d8ba68aed26a1b1 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathanjosiah@gmail.com> Date: Thu, 24 May 2018 17:40:08 -0500 Subject: [PATCH 198/333] MAGETWO-89736: Move mftf tests to Module/Tests - Exclude xml static tests for Module/Test/* directories --- .../static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php index 15d948b600ea2..8048925a4e542 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Xml/SchemaTest.php @@ -68,7 +68,7 @@ public function getXmlFiles() $componentRegistrar = new ComponentRegistrar(); $codeXml = []; foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $modulePath) { - $codeXml = array_merge($codeXml, $this->_getFiles($modulePath, '*.xml', '/.\/Test\/Unit/')); + $codeXml = array_merge($codeXml, $this->_getFiles($modulePath, '*.xml', '/.\/Test\/./')); } $this->_filterSpecialCases($codeXml); $designXml = []; From 2dabd7d626ffaa6c075e83f03d72ced684b8c113 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Tue, 22 May 2018 10:57:29 +0530 Subject: [PATCH 199/333] Updated font-size variable and standardize #ToDo UI --- .../backend/web/app/setup/styles/less/lib/_buttons.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_buttons.less b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_buttons.less index d337514224b87..ec7509cba6dda 100644 --- a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_buttons.less +++ b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_buttons.less @@ -99,8 +99,8 @@ background-color: transparent; border: none; color: @link__color; - font-family: @btn__base__font-size; - font-size: 1.5rem; // ToDo UI: Check font-size and standardize + font-family: @font-family__base; + font-size: @base__font-size; &:hover, &:active, From 8680ad9e87cf96b64058bc49f8224dd3b247a5c4 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 25 May 2018 13:59:35 +0300 Subject: [PATCH 200/333] MAGETWO-90798: Customer is redirected to 404 from Catalog page if switches to the Store with another root Category --- .../two_categories_per_two_store_groups.php | 39 +++++-------------- ...tegories_per_two_store_groups_rollback.php | 8 ---- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php index 4036069679825..6a0db9bd16d41 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php @@ -10,8 +10,7 @@ /** @var \Magento\Catalog\Model\Category $category */ $category = $objectManager->create(\Magento\Catalog\Model\Category::class); $category->isObjectNew(true); -$category->setId(333) - ->setCreatedAt('2014-06-23 09:50:07') +$category->setCreatedAt('2014-06-23 09:50:07') ->setName('Category 1') ->setParentId(2) ->setPath('1/2/3') @@ -23,23 +22,6 @@ ->setAvailableSortBy(['position']) ->save(); -/** @var $product \Magento\Catalog\Model\Product */ -$product = $objectManager->create(\Magento\Catalog\Model\Product::class); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) - ->setId(333) - ->setAttributeSetId(4) - ->setStoreId(1) - ->setWebsiteIds([1]) - ->setName('Simple Product Three') - ->setSku('simple333') - ->setPrice(10) - ->setWeight(18) - ->setStockData(['use_config_manage_stock' => 0]) - ->setCategoryIds([333]) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->save(); - /** @var \Magento\Store\Model\Store $store */ $store = $objectManager->create(\Magento\Store\Model\Store::class); @@ -50,8 +32,7 @@ $rootCategoryForTestStoreGroup = $objectManager->create(\Magento\Catalog\Model\Category::class); $rootCategoryForTestStoreGroup->isObjectNew(true); -$rootCategoryForTestStoreGroup->setId(334) - ->setCreatedAt('2014-06-23 09:50:07') +$rootCategoryForTestStoreGroup->setCreatedAt('2014-06-23 09:50:07') ->setName('Category 2') ->setParentId(1) ->setPath('1/2/334') @@ -72,20 +53,20 @@ /** @var \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository */ $categoryRepository = $objectManager->create(\Magento\Catalog\Api\CategoryRepositoryInterface::class); /** @var \Magento\Catalog\Api\Data\CategoryInterface $category */ -$category = $categoryRepository->get(334); +$category = $categoryRepository->get(2); /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ $storeRepository = $objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); /** @var \Magento\Store\Api\Data\StoreInterface $store */ $store = $storeRepository->get($storeCode); /** @var \Magento\Store\Model\Group $storeGroup */ -$storeGroup = $objectManager->create(\Magento\Store\Model\Group::class); -$storeGroup->setWebsiteId('1'); -$storeGroup->setCode('test_store_group'); -$storeGroup->setName('Test Store Group'); -$storeGroup->setRootCategoryId($category->getId()); -$storeGroup->setDefaultStoreId($store->getId()); -$storeGroup->save(); +$storeGroup = $objectManager->create(\Magento\Store\Model\Group::class) + ->setWebsiteId('1') + ->setCode('test_store_group') + ->setName('Test Store Group') + ->setRootCategoryId($category->getId()) + ->setDefaultStoreId($store->getId()) + ->save(); $store->setGroupId($storeGroup->getId())->save(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php index 0fe559aeed50c..9592e9d0e69b4 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups_rollback.php @@ -18,14 +18,6 @@ $registry = $objectManager->get(Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); -// Delete product -$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); -try { - $product = $productRepository->get('simple333', false, null, true); - $product->delete(); -} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { - //Product already removed -} // Delete first category /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ $searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); From cde14421a20e93d63dee1eaea436cec9823a3212 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 25 May 2018 14:57:40 +0300 Subject: [PATCH 201/333] MAGETWO-90798: Customer is redirected to 404 from Catalog page if switches to the Store with another root Category --- .../_files/two_categories_per_two_store_groups.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php index 6a0db9bd16d41..90a74351d8200 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/two_categories_per_two_store_groups.php @@ -6,13 +6,13 @@ declare(strict_types=1); $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - +$defaultCategory = $objectManager->create(\Magento\Catalog\Helper\DefaultCategory::class); /** @var \Magento\Catalog\Model\Category $category */ $category = $objectManager->create(\Magento\Catalog\Model\Category::class); $category->isObjectNew(true); $category->setCreatedAt('2014-06-23 09:50:07') ->setName('Category 1') - ->setParentId(2) + ->setParentId($defaultCategory->getId()) ->setPath('1/2/3') ->setLevel(2) ->setAvailableSortBy('name') @@ -34,7 +34,7 @@ $rootCategoryForTestStoreGroup->isObjectNew(true); $rootCategoryForTestStoreGroup->setCreatedAt('2014-06-23 09:50:07') ->setName('Category 2') - ->setParentId(1) + ->setParentId(\Magento\Catalog\Model\Category::TREE_ROOT_ID) ->setPath('1/2/334') ->setLevel(2) ->setAvailableSortBy('name') @@ -50,10 +50,6 @@ ->save(); $storeCode = 'test'; -/** @var \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository */ -$categoryRepository = $objectManager->create(\Magento\Catalog\Api\CategoryRepositoryInterface::class); -/** @var \Magento\Catalog\Api\Data\CategoryInterface $category */ -$category = $categoryRepository->get(2); /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ $storeRepository = $objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); /** @var \Magento\Store\Api\Data\StoreInterface $store */ @@ -64,7 +60,7 @@ ->setWebsiteId('1') ->setCode('test_store_group') ->setName('Test Store Group') - ->setRootCategoryId($category->getId()) + ->setRootCategoryId($rootCategoryForTestStoreGroup->getId()) ->setDefaultStoreId($store->getId()) ->save(); From 2925aff177907a920a010b5e853cf02c5e0bee95 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 25 May 2018 15:31:40 +0300 Subject: [PATCH 202/333] MAGETWO-90784: Catalog Rule is not applied --- .../Indexer/Product/Price/AbstractAction.php | 10 +- .../Indexer/Product/Price/Action/Full.php | 2 +- .../Product/Indexer/Price/DefaultPrice.php | 87 ++++++++- .../Indexer/Price/IndexTableStructure.php | 181 ++++++++++++++++++ .../Indexer/Price/PriceModifierInterface.php | 23 +++ .../Indexer/ProductPriceIndexModifier.php | 75 ++++++++ app/code/Magento/CatalogRule/Model/Rule.php | 5 +- app/code/Magento/CatalogRule/etc/di.xml | 7 + app/code/Magento/CatalogRule/etc/indexer.xml | 5 + .../Product/Indexer/Price/Configurable.php | 89 ++------- .../Model/ResourceModel/Indexer/Price.php | 5 +- .../Model/Indexer/Product/PriceTest.php | 89 +++++++++ .../Model/Indexer/RuleProductTest.php | 2 +- .../_files/product_with_attribute.php | 108 +++++++++++ .../product_with_attribute_rollback.php | 33 ++++ 15 files changed, 634 insertions(+), 87 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/IndexTableStructure.php create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php create mode 100644 app/code/Magento/CatalogRule/Model/Indexer/ProductPriceIndexModifier.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php index cfa5ec91a2e1b..7aed842713f5d 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php @@ -336,9 +336,10 @@ protected function _reindexRows($changedIds = []) if (!empty($notCompositeIds)) { $parentProductsTypes = $this->getParentProductsTypes($notCompositeIds); $productsTypes = array_merge_recursive($productsTypes, $parentProductsTypes); - $parentProductsIds = array_keys($parentProductsTypes); - $compositeIds = $compositeIds + array_combine($parentProductsIds, $parentProductsIds); - $changedIds = array_merge($changedIds, $parentProductsIds); + foreach ($parentProductsTypes as $parentProductsIds) { + $compositeIds = $compositeIds + $parentProductsIds; + $changedIds = array_merge($changedIds, $parentProductsIds); + } } if (!empty($compositeIds)) { @@ -370,7 +371,8 @@ protected function _copyRelationIndexData($parentIds, $excludeIds = null) ['child_id'] )->join( ['e' => $this->_defaultIndexerResource->getTable('catalog_product_entity')], - 'e.' . $linkField . ' = parent_id' + 'e.' . $linkField . ' = parent_id', + [] )->where( 'e.entity_id IN(?)', $parentIds diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php index eb15833a7d0b2..ba04af8ec1f41 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php @@ -109,7 +109,7 @@ public function execute($ids = null) // Prepare replica table for indexation. $this->_defaultIndexerResource->getConnection()->truncateTable($replicaTable); - /** @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\AbstractIndexer $indexer */ + /** @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $indexer */ foreach ($this->getTypeIndexers() as $indexer) { $indexer->getTableStrategy()->setUseIdxTable(false); $connection = $indexer->getConnection(); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php index 591a26efbf615..5149fddfc080a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php @@ -52,6 +52,16 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface */ private $hasEntity = null; + /** + * @var IndexTableStructureFactory + */ + private $indexTableStructureFactory; + + /** + * @var PriceModifierInterface[] + */ + private $priceModifiers = []; + /** * DefaultPrice constructor. * @@ -61,7 +71,8 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Framework\Module\Manager $moduleManager * @param string|null $connectionName - * @param null|\Magento\Indexer\Model\Indexer\StateFactory $stateFactory + * @param IndexTableStructureFactory $indexTableStructureFactory + * @param PriceModifierInterface[] $priceModifiers */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, @@ -69,11 +80,25 @@ public function __construct( \Magento\Eav\Model\Config $eavConfig, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Framework\Module\Manager $moduleManager, - $connectionName = null + $connectionName = null, + IndexTableStructureFactory $indexTableStructureFactory = null, + array $priceModifiers = [] ) { $this->_eventManager = $eventManager; $this->moduleManager = $moduleManager; parent::__construct($context, $tableStrategy, $eavConfig, $connectionName); + + $this->indexTableStructureFactory = $indexTableStructureFactory ?: + \Magento\Framework\App\ObjectManager::getInstance()->get(IndexTableStructureFactory::class); + foreach ($priceModifiers as $priceModifier) { + if (!($priceModifier instanceof PriceModifierInterface)) { + throw new \InvalidArgumentException( + 'Argument \'priceModifiers\' must be of the type ' . PriceModifierInterface::class . '[]' + ); + } + + $this->priceModifiers[] = $priceModifier; + } } /** @@ -209,6 +234,8 @@ protected function _getDefaultFinalPriceTable() * Prepare final price temporary index table * * @return $this + * @deprecated + * @see prepareFinalPriceTable() */ protected function _prepareDefaultFinalPriceTable() { @@ -216,6 +243,32 @@ protected function _prepareDefaultFinalPriceTable() return $this; } + /** + * Create (if needed), clean and return structure of final price table + * + * @return IndexTableStructure + */ + private function prepareFinalPriceTable() + { + $tableName = $this->_getDefaultFinalPriceTable(); + $this->getConnection()->delete($tableName); + + $finalPriceTable = $this->indexTableStructureFactory->create([ + 'tableName' => $tableName, + 'entityField' => 'entity_id', + 'customerGroupField' => 'customer_group_id', + 'websiteField' => 'website_id', + 'taxClassField' => 'tax_class_id', + 'originalPriceField' => 'orig_price', + 'finalPriceField' => 'price', + 'minPriceField' => 'min_price', + 'maxPriceField' => 'max_price', + 'tierPriceField' => 'tier_price', + ]); + + return $finalPriceTable; + } + /** * Retrieve website current dates table name * @@ -248,11 +301,14 @@ protected function _prepareFinalPriceData($entityIds = null) */ protected function prepareFinalPriceDataForType($entityIds, $type) { - $this->_prepareDefaultFinalPriceTable(); + $finalPriceTable = $this->prepareFinalPriceTable(); $select = $this->getSelect($entityIds, $type); - $query = $select->insertFromSelect($this->_getDefaultFinalPriceTable(), [], false); + $query = $select->insertFromSelect($finalPriceTable->getTableName(), [], false); $this->getConnection()->query($query); + + $this->applyDiscountPrices($finalPriceTable); + return $this; } @@ -359,7 +415,7 @@ protected function getSelect($entityIds = null, $type = null) 'e.' . $metadata->getLinkField(), 'cs.store_id' ); - $currentDate = $connection->getDatePartSql('cwd.website_date'); + $currentDate = 'cwd.website_date'; $maxUnsignedBigint = '~0'; $specialFromDate = $connection->getDatePartSql($specialFrom); @@ -409,6 +465,7 @@ protected function getSelect($entityIds = null, $type = null) 'store_field' => new \Zend_Db_Expr('cs.store_id'), ] ); + return $select; } @@ -454,6 +511,19 @@ protected function _prepareCustomOptionPriceTable() return $this; } + /** + * Apply discount prices to final price index table. + * + * @param IndexTableStructure $finalPriceTable + * @return void + */ + private function applyDiscountPrices(IndexTableStructure $finalPriceTable) : void + { + foreach ($this->priceModifiers as $priceModifier) { + $priceModifier->modifyPrice($finalPriceTable); + } + } + /** * Apply custom option minimal and maximal price to temporary final price index table * @@ -463,6 +533,7 @@ protected function _prepareCustomOptionPriceTable() protected function _applyCustomOption() { $connection = $this->getConnection(); + $finalPriceTable = $this->_getDefaultFinalPriceTable(); $coaTable = $this->_getCustomOptionAggregateTable(); $copTable = $this->_getCustomOptionPriceTable(); @@ -470,7 +541,7 @@ protected function _applyCustomOption() $this->_prepareCustomOptionPriceTable(); $select = $connection->select()->from( - ['i' => $this->_getDefaultFinalPriceTable()], + ['i' => $finalPriceTable], ['entity_id', 'customer_group_id', 'website_id'] )->join( ['cw' => $this->getTable('store_website')], @@ -537,7 +608,7 @@ protected function _applyCustomOption() $connection->query($query); $select = $connection->select()->from( - ['i' => $this->_getDefaultFinalPriceTable()], + ['i' => $finalPriceTable], ['entity_id', 'customer_group_id', 'website_id'] )->join( ['cw' => $this->getTable('store_website')], @@ -606,7 +677,7 @@ protected function _applyCustomOption() $query = $select->insertFromSelect($copTable); $connection->query($query); - $table = ['i' => $this->_getDefaultFinalPriceTable()]; + $table = ['i' => $finalPriceTable]; $select = $connection->select()->join( ['io' => $copTable], 'i.entity_id = io.entity_id AND i.customer_group_id = io.customer_group_id' . diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/IndexTableStructure.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/IndexTableStructure.php new file mode 100644 index 0000000000000..fb3eef2bf38eb --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/IndexTableStructure.php @@ -0,0 +1,181 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price; + +/** + * Wrapper for structure of price index table. + */ +class IndexTableStructure +{ + /** + * @var string + */ + private $tableName; + + /** + * @var string + */ + private $entityField; + + /** + * @var string + */ + private $customerGroupField; + + /** + * @var string + */ + private $websiteField; + + /** + * @var string + */ + private $taxClassField; + + /** + * @var string + */ + private $originalPriceField; + + /** + * @var string + */ + private $finalPriceField; + + /** + * @var string + */ + private $minPriceField; + + /** + * @var string + */ + private $maxPriceField; + + /** + * @var string + */ + private $tierPriceField; + + /** + * @param string $tableName + * @param string $entityField + * @param string $customerGroupField + * @param string $websiteField + * @param string $taxClassField + * @param string $originalPriceField + * @param string $finalPriceField + * @param string $minPriceField + * @param string $maxPriceField + * @param string $tierPriceField + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + string $tableName, + string $entityField, + string $customerGroupField, + string $websiteField, + string $taxClassField, + string $originalPriceField, + string $finalPriceField, + string $minPriceField, + string $maxPriceField, + string $tierPriceField + ) { + $this->tableName = $tableName; + $this->entityField = $entityField; + $this->customerGroupField = $customerGroupField; + $this->websiteField = $websiteField; + $this->taxClassField = $taxClassField; + $this->originalPriceField = $originalPriceField; + $this->finalPriceField = $finalPriceField; + $this->minPriceField = $minPriceField; + $this->maxPriceField = $maxPriceField; + $this->tierPriceField = $tierPriceField; + } + + /** + * @return string + */ + public function getTableName(): string + { + return $this->tableName; + } + + /** + * @return string + */ + public function getEntityField(): string + { + return $this->entityField; + } + + /** + * @return string + */ + public function getCustomerGroupField(): string + { + return $this->customerGroupField; + } + + /** + * @return string + */ + public function getWebsiteField(): string + { + return $this->websiteField; + } + + /** + * @return string + */ + public function getTaxClassField(): string + { + return $this->taxClassField; + } + + /** + * @return string + */ + public function getOriginalPriceField(): string + { + return $this->originalPriceField; + } + + /** + * @return string + */ + public function getFinalPriceField(): string + { + return $this->finalPriceField; + } + + /** + * @return string + */ + public function getMinPriceField(): string + { + return $this->minPriceField; + } + + /** + * @return string + */ + public function getMaxPriceField(): string + { + return $this->maxPriceField; + } + + /** + * @return string + */ + public function getTierPriceField(): string + { + return $this->tierPriceField; + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php new file mode 100644 index 0000000000000..6ecb6aba89933 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/PriceModifierInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ResourceModel\Product\Indexer\Price; + +/** + * Interface for modifying price data in price index table. + */ +interface PriceModifierInterface +{ + /** + * Modify price data. + * + * @param IndexTableStructure $priceTable + * @param array $entityIds + * @return void + */ + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void; +} diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceIndexModifier.php b/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceIndexModifier.php new file mode 100644 index 0000000000000..a60b05dc7c9bc --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceIndexModifier.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogRule\Model\Indexer; + +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceModifierInterface; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; +use Magento\CatalogRule\Model\ResourceModel\Rule\Product\Price; + +/** + * Class for adding catalog rule prices to price index table. + */ +class ProductPriceIndexModifier implements PriceModifierInterface +{ + /** + * @var Price + */ + private $priceResourceModel; + + /** + * @param Price $priceResourceModel + */ + public function __construct(Price $priceResourceModel) + { + $this->priceResourceModel = $priceResourceModel; + } + + /** + * @inheritdoc + */ + public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void + { + $connection = $this->priceResourceModel->getConnection(); + $select = $connection->select(); + + $select->join( + ['cpiw' => $this->priceResourceModel->getTable('catalog_product_index_website')], + 'cpiw.website_id = i.' . $priceTable->getWebsiteField(), + [] + ); + $select->join( + ['cpp' => $this->priceResourceModel->getMainTable()], + 'cpp.product_id = i.' . $priceTable->getEntityField() + . ' AND cpp.customer_group_id = i.' . $priceTable->getCustomerGroupField() + . ' AND cpp.website_id = i.' . $priceTable->getWebsiteField() + . ' AND cpp.rule_date = cpiw.website_date', + [] + ); + if ($entityIds) { + $select->where('i.entity_id IN (?)', $entityIds); + } + + $finalPrice = $priceTable->getFinalPriceField(); + $finalPriceExpr = $select->getConnection()->getLeastSql([ + $priceTable->getFinalPriceField(), + $select->getConnection()->getIfNullSql('cpp.rule_price', 'i.' . $finalPrice), + ]); + $minPrice = $priceTable->getMinPriceField(); + $minPriceExpr = $select->getConnection()->getLeastSql([ + $priceTable->getMinPriceField(), + $select->getConnection()->getIfNullSql('cpp.rule_price', 'i.' . $minPrice), + ]); + $select->columns([ + $finalPrice => $finalPriceExpr, + $minPrice => $minPriceExpr, + ]); + + $query = $connection->updateFromSelect($select, ['i' => $priceTable->getTableName()]); + $connection->query($query); + } +} diff --git a/app/code/Magento/CatalogRule/Model/Rule.php b/app/code/Magento/CatalogRule/Model/Rule.php index 7696569cb26da..d927d6f4d0c82 100644 --- a/app/code/Magento/CatalogRule/Model/Rule.php +++ b/app/code/Magento/CatalogRule/Model/Rule.php @@ -606,7 +606,10 @@ public function afterSave() */ public function reindex() { - $this->_ruleProductProcessor->reindexList($this->_productIds); + $productIds = $this->_productIds ? array_keys(array_filter($this->_productIds, function (array $data) { + return array_filter($data); + })) : []; + $this->_ruleProductProcessor->reindexList($productIds); } /** diff --git a/app/code/Magento/CatalogRule/etc/di.xml b/app/code/Magento/CatalogRule/etc/di.xml index 40893592c3d0f..8ed88dd4f3fdb 100644 --- a/app/code/Magento/CatalogRule/etc/di.xml +++ b/app/code/Magento/CatalogRule/etc/di.xml @@ -150,4 +150,11 @@ </arguments> </type> <preference for="Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface" type="Magento\CatalogRule\Model\Indexer\IndexerTableSwapper" /> + <type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface"> + <arguments> + <argument name="priceModifiers" xsi:type="array"> + <item name="catalogRulePriceModifier" xsi:type="object">Magento\CatalogRule\Model\Indexer\ProductPriceIndexModifier</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogRule/etc/indexer.xml b/app/code/Magento/CatalogRule/etc/indexer.xml index 08ed456457bfe..e648ea567631c 100644 --- a/app/code/Magento/CatalogRule/etc/indexer.xml +++ b/app/code/Magento/CatalogRule/etc/indexer.xml @@ -14,4 +14,9 @@ <title translate="true">Catalog Product Rule Indexed product/rule association + + + + + diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php index 326310cc3c802..087931ebe5dcc 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Indexer/Price/Configurable.php @@ -1,7 +1,5 @@ storeResolver = $storeResolver ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - StoreResolverInterface::class - ); - } - /** * @param null|int|array $entityIds * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable @@ -58,6 +25,7 @@ protected function reindex($entityIds = null) $this->_applyConfigurableOption($entityIds); $this->_movePriceDataToIndexTable($entityIds); } + return $this; } @@ -109,67 +77,49 @@ protected function _prepareConfigurableOptionPriceTable() * * @param array|null $entityIds * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Indexer\Price\Configurable - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _applyConfigurableOption($entityIds = null) { $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class); $connection = $this->getConnection(); - $coaTable = $this->_getConfigurableOptionAggregateTable(); $copTable = $this->_getConfigurableOptionPriceTable(); + $finalPriceTable = $this->_getDefaultFinalPriceTable(); $linkField = $metadata->getLinkField(); - $this->_prepareConfigurableOptionAggregateTable(); $this->_prepareConfigurableOptionPriceTable(); - $subSelect = $this->getSelect(); - $subSelect->join( + $select = $connection->select()->from( + ['i' => $this->getIdxTable()], + [] + )->join( ['l' => $this->getTable('catalog_product_super_link')], - 'l.product_id = e.entity_id', + 'l.product_id = i.entity_id', [] )->join( ['le' => $this->getTable('catalog_product_entity')], 'le.' . $linkField . ' = l.parent_id', - ['parent_id' => 'entity_id'] - ); - - if ($entityIds !== null) { - $subSelect->where('le.entity_id IN (?)', $entityIds); - } - - $select = $connection->select(); - $select - ->from(['sub' => new \Zend_Db_Expr('(' . (string)$subSelect . ')')], '') - ->columns([ - 'sub.parent_id', - 'sub.entity_id', - 'sub.customer_group_id', - 'sub.website_id', - 'sub.price', - 'sub.tier_price' - ]); - - $query = $select->insertFromSelect($coaTable); - $connection->query($query); - - $select = $connection->select()->from( - [$coaTable], + [] + )->columns( [ - 'parent_id', + 'le.entity_id', 'customer_group_id', 'website_id', - 'MIN(price)', - 'MAX(price)', + 'MIN(final_price)', + 'MAX(final_price)', 'MIN(tier_price)', + ] )->group( - ['parent_id', 'customer_group_id', 'website_id'] + ['le.entity_id', 'customer_group_id', 'website_id'] ); + if ($entityIds !== null) { + $select->where('le.entity_id IN (?)', $entityIds); + } $query = $select->insertFromSelect($copTable); $connection->query($query); - $table = ['i' => $this->_getDefaultFinalPriceTable()]; + $table = ['i' => $finalPriceTable]; $select = $connection->select()->join( ['io' => $copTable], 'i.entity_id = io.entity_id AND i.customer_group_id = io.customer_group_id' . @@ -188,7 +138,6 @@ protected function _applyConfigurableOption($entityIds = null) $query = $select->crossUpdateFromSelect($table); $connection->query($query); - $connection->delete($coaTable); $connection->delete($copTable); return $this; diff --git a/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php index dd83c28b43dff..855fac5041b21 100644 --- a/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Downloadable/Model/ResourceModel/Indexer/Price.php @@ -62,6 +62,7 @@ protected function _applyDownloadableLink() { $connection = $this->getConnection(); $table = $this->_getDownloadableLinkPriceTable(); + $finalPriceTable = $this->_getDefaultFinalPriceTable(); $this->_prepareDownloadableLinkPriceTable(); @@ -71,7 +72,7 @@ protected function _applyDownloadableLink() $ifPrice = $connection->getIfNullSql('dlpw.price_id', 'dlpd.price'); $select = $connection->select()->from( - ['i' => $this->_getDefaultFinalPriceTable()], + ['i' => $finalPriceTable], ['entity_id', 'customer_group_id', 'website_id'] )->join( ['dl' => $dlType->getBackend()->getTable()], @@ -119,7 +120,7 @@ protected function _applyDownloadableLink() ] ); - $query = $select->crossUpdateFromSelect(['i' => $this->_getDefaultFinalPriceTable()]); + $query = $select->crossUpdateFromSelect(['i' => $finalPriceTable]); $connection->query($query); $connection->delete($table); 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 new file mode 100644 index 0000000000000..ad580cfb3aeb5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php @@ -0,0 +1,89 @@ +resourceRule = Bootstrap::getObjectManager()->get(Rule::class); + } + + /** + * @return void + */ + public function testPriceApplying() : void + { + $customerGroupId = 1; + $websiteId = 1; + + $simpleProductId = 1; + $collection = Bootstrap::getObjectManager()->create(Collection::class); + $collection->addIdFilter($simpleProductId); + $collection->addPriceData($customerGroupId, $websiteId); + $collection->load(); + /** @var \Magento\Catalog\Model\Product $simpleProduct */ + $simpleProduct = $collection->getFirstItem(); + $simpleProduct->setPriceCalculation(false); + + $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), $websiteId, $customerGroupId, $simpleProductId); + $this->assertEquals($rulePrice, $simpleProduct->getFinalPrice()); + + $confProductId = 666; + $collection = Bootstrap::getObjectManager()->create(Collection::class); + $collection->addIdFilter($confProductId); + $collection->addPriceData($customerGroupId, $websiteId); + $collection->load(); + /** @var \Magento\Catalog\Model\Product $confProduct */ + $confProduct = $collection->getFirstItem(); + + $this->assertEquals($simpleProduct->getMinimalPrice(), $confProduct->getMinimalPrice()); + } + + /** + * @magentoAppArea frontend + * + * @return void + */ + public function testSortByPrice() : void + { + $searchCriteria = Bootstrap::getObjectManager()->create(SearchCriteriaInterface::class); + $sortOrder = Bootstrap::getObjectManager()->create(SortOrder::class); + $sortOrder->setField('price')->setDirection(SortOrder::SORT_ASC); + $searchCriteria->setSortOrders([$sortOrder]); + $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + $searchResults = $productRepository->getList($searchCriteria); + $products = $searchResults->getItems(); + + /** @var \Magento\Catalog\Model\Product $product1 */ + $product1 = array_values($products)[0]; + $product1->setPriceCalculation(false); + $this->assertEquals('simple1', $product1->getSku()); + $rulePrice = $this->resourceRule->getRulePrice(new \DateTime(), 1, 1, $product1->getId()); + $this->assertEquals($rulePrice, $product1->getFinalPrice()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/RuleProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/RuleProductTest.php index c8c0726de7ebe..a4a99918fe052 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/RuleProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/RuleProductTest.php @@ -40,7 +40,7 @@ protected function setUp() public function testReindexAfterRuleCreation() { /** @var \Magento\Catalog\Model\ProductRepository $productRepository */ - $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Catalog\Model\ProductRepository::class ); $product = $productRepository->get('simple'); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php new file mode 100644 index 0000000000000..071f5d7d9fd00 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute.php @@ -0,0 +1,108 @@ +get(\Magento\Store\Model\StoreManager::class); +$store = $storeManager->getStore('default'); + +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +$installer = $objectManager->get(\Magento\Catalog\Setup\CategorySetup::class); +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); +$attributeValues = []; +$associatedProductIds = []; + +/** @var Magento\Eav\Model\Entity\Attribute\Option[] $options */ +$options = $attribute->getOptions(); +array_shift($options); //remove the first option which is empty + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(1) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Simple Product 1') + ->setSku('simple1') + ->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, + ]); +$option = array_shift($options); +$product->setTestConfigurable($option->getValue()); +$productRepository->save($product); +$attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), +]; +$associatedProductIds[] = $product->getId(); +$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class); +$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId()); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(2) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Simple Product 2') + ->setSku('simple2') + ->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, + ]); +$option = array_shift($options); +$product->setTestConfigurable($option->getValue()); +$productRepository->save($product); +$attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), +]; +$associatedProductIds[] = $product->getId(); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('configurable') + ->setId(666) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'is_in_stock' => 0, + ]); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$optionsFactory = $objectManager->get(\Magento\ConfigurableProduct\Helper\Product\Options\Factory::class); +$configurableOptions = $optionsFactory->create($configurableAttributesData); +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php new file mode 100644 index 0000000000000..0ce909a3f9ecd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/product_with_attribute_rollback.php @@ -0,0 +1,33 @@ +getInstance()->reinitialize(); + +/** @var $objectManager \Magento\TestFramework\ObjectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +foreach (['simple1', 'simple2', 'configurable'] as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Nothing to delete + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php'; From 9787807667f17e493cce1c91eea427e7621adf86 Mon Sep 17 00:00:00 2001 From: Rebecca Brocton Date: Wed, 23 May 2018 13:11:14 +0100 Subject: [PATCH 203/333] Improvements to the CONTRIBUTING.md document, including wording changes & link updates. --- .github/CONTRIBUTING.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2839ac5ee9d32..dae954a0970b7 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,34 +1,34 @@ # Contributing to Magento 2 code Contributions to the Magento 2 codebase are done using the fork & pull model. -This contribution model has contributors maintaining their own copy of the forked codebase (which can easily be synced with the main copy). The forked repository is then used to submit a request to the base repository to “pull” a set of changes (hence the phrase “pull request”). +This contribution model has contributors maintaining their own copy of the forked codebase (which can easily be synced with the main copy). The forked repository is then used to submit a request to the base repository to “pull” a set of changes. For more information on pull requests please refer to [GitHub Help](https://help.github.com/articles/about-pull-requests/). -Contributions can take the form of new components/features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations or just good suggestions. +Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes or optimizations. -The Magento 2 development team will review all issues and contributions submitted by the community of developers in the first in, first out order. During the review we might require clarifications from the contributor. If there is no response from the contributor for two weeks, the issue is closed. +The Magento 2 development team will review all issues and contributions submitted by the community of developers in the first in, first out order. During the review we might require clarifications from the contributor. If there is no response from the contributor within two weeks, the pull request will be closed. ## Contribution requirements -1. Contributions must adhere to [Magento coding standards](http://devdocs.magento.com/guides/v2.0/coding-standards/bk-coding-standards.html). -2. Pull requests (PRs) must be accompanied by a meaningful description of their purpose. Comprehensive descriptions increase the chances of a pull request to be merged quickly and without additional clarification requests. -3. Commits must be accompanied by meaningful commit messages. -4. PRs which include bug fixing, must be accompanied with step-by-step description of how to reproduce the bug. +1. Contributions must adhere to the [Magento coding standards](https://devdocs.magento.com/guides/v2.2/coding-standards/bk-coding-standards.html). +2. Pull requests (PRs) must be accompanied by a meaningful description of their purpose. Comprehensive descriptions increase the chances of a pull request being merged quickly and without additional clarification requests. +3. Commits must be accompanied by meaningful commit messages. Please see the [Magento Pull Request Template](https://github.com/magento/magento2/blob/2.2-develop/.github/PULL_REQUEST_TEMPLATE.md) for more information. +4. PRs which include bug fixes must be accompanied with a step-by-step description of how to reproduce the bug. 3. PRs which include new logic or new features must be submitted along with: -* Unit/integration test coverage (we will be releasing more information on writing test coverage in the near future). -* Proposed [documentation](http://devdocs.magento.com) update. Documentation contributions can be submitted [here](https://github.com/magento/devdocs). -4. For large features or changes, please [open an issue](https://github.com/magento/magento2/issues) and discuss first. This may prevent duplicate or unnecessary effort, and it may gain you some additional contributors. -5. All automated tests are passed successfully (all builds on [Travis CI](https://travis-ci.org/magento/magento2) must be green). +* Unit/integration test coverage +* Proposed [documentation](http://devdocs.magento.com) updates. Documentation contributions can be submitted via the [devdocs GitHub](https://github.com/magento/devdocs). +4. For larger features or changes, please [open an issue](https://github.com/magento/magento2/issues) to discuss the proposed changes prior to development. This may prevent duplicate or unnecessary effort and allow other contributors to provide input. +5. All automated tests must pass (all builds on [Travis CI](https://travis-ci.org/magento/magento2) must be green). ## Contribution process -If you are a new GitHub user, we recommend that you create your own [free github account](https://github.com/signup/free). By doing that, you will be able to collaborate with the Magento 2 development team, “fork” the Magento 2 project and be able to easily send “pull requests”. +If you are a new GitHub user, we recommend that you create your own [free github account](https://github.com/signup/free). This will allow you to collaborate with the Magento 2 development team, fork the Magento 2 project and send pull requests. 1. Search current [listed issues](https://github.com/magento/magento2/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution. 2. Review the [Contributor License Agreement](https://magento.com/legaldocuments/mca) if this is your first time contributing. 3. Create and test your work. -4. Fork the Magento 2 repository according to [Fork a repository instructions](http://devdocs.magento.com/guides/v2.0/contributor-guide/contributing.html#fork) and when you are ready to send us a pull request – follow [Create a pull request instructions](http://devdocs.magento.com/guides/v2.0/contributor-guide/contributing.html#pull_request). -5. Once your contribution is received, Magento 2 development team will review the contribution and collaborate with you as needed to improve the quality of the contribution. +4. Fork the Magento 2 repository according to the [Fork A Repository instructions](http://devdocs.magento.com/guides/v2.2/contributor-guide/contributing.html#fork) and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](http://devdocs.magento.com/guides/v2.2/contributor-guide/contributing.html#pull_request). +5. Once your contribution is received the Magento 2 development team will review the contribution and collaborate with you as needed. ## Code of Conduct From 530592b8c956c8d1ac2bbcf8ac0a29b7f25b668c Mon Sep 17 00:00:00 2001 From: vgelani Date: Tue, 22 May 2018 10:27:46 +0530 Subject: [PATCH 204/333] Moved css from media #TODO --- .../Magento_Theme/web/css/source/_module.less | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) 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 b5742b86cf9f1..94f34cd0cf1e1 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 @@ -62,17 +62,16 @@ // Common // _____________________________________________ -& when (@media-common = true) { - body { +.page-wrapper { + .ie9 & { .lib-css(background-color, @page__background-color); + min-height: 0; } +} - // ToDo UI: move it directly to .page-wrapper (now it doesn't appear in mediaquery cuz of size of css) - .page-wrapper { - .ie9 & { - .lib-css(background-color, @page__background-color); - min-height: 0; - } +& when (@media-common = true) { + body { + .lib-css(background-color, @page__background-color); } // From 5b7e6c3ede3ca2980a2c1222bac64e03a385f34d Mon Sep 17 00:00:00 2001 From: DianaRusin Date: Fri, 25 May 2018 16:31:02 +0300 Subject: [PATCH 205/333] MAGETWO-90789: [Magento cloud] Custom options should be cleared if custom options in import file is empty --- .../Model/Import/Product/Option.php | 118 +++++++++++++----- .../Model/Import/ProductTest.php | 93 ++++++++------ .../_files/product_without_custom_options.csv | 2 + 3 files changed, 139 insertions(+), 74 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_without_custom_options.csv diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php index cbaf401f32982..245553ea78169 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php @@ -1223,6 +1223,7 @@ protected function _importData() $typeTitles = []; $parentCount = []; $childCount = []; + $optionsToRemove = []; foreach ($bunch as $rowNumber => $rowData) { if (isset($optionId, $valueId) && empty($rowData[PRODUCT::COL_STORE_VIEW_CODE])) { @@ -1232,14 +1233,18 @@ protected function _importData() $optionId = $nextOptionId; $valueId = $nextValueId; $multiRowData = $this->_getMultiRowFormat($rowData); - + if (!empty($rowData[self::COLUMN_SKU]) && isset($this->_productsSkuToId[$rowData[self::COLUMN_SKU]])) { + $this->_rowProductId = $this->_productsSkuToId[$rowData[self::COLUMN_SKU]]; + if (array_key_exists('custom_options', $rowData) && trim($rowData['custom_options']) === "") { + $optionsToRemove[] = $this->_rowProductId; + } + } foreach ($multiRowData as $optionData) { $combinedData = array_merge($rowData, $optionData); - if (!$this->isRowAllowedToImport($combinedData, $rowNumber)) { - continue; - } - if (!$this->_parseRequiredData($combinedData)) { + if (!$this->isRowAllowedToImport($combinedData, $rowNumber) + || !$this->_parseRequiredData($combinedData) + ) { continue; } $optionData = $this->_collectOptionMainData( @@ -1266,38 +1271,42 @@ protected function _importData() } } - // Save prepared custom options data !!! - if ($this->getBehavior() != \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) { - $this->_deleteEntities(array_keys($products)); - } - - if ($this->_isReadyForSaving($options, $titles, $typeValues)) { - if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) { - $this->_compareOptionsWithExisting($options, $titles, $prices, $typeValues); - $this->restoreOriginalOptionTypeIds($typeValues, $typePrices, $typeTitles); - } + $this->removeExistingOptions($products, $optionsToRemove); - $this->_saveOptions( - $options - )->_saveTitles( - $titles - )->_savePrices( - $prices - )->_saveSpecificTypeValues( - $typeValues - )->_saveSpecificTypePrices( - $typePrices - )->_saveSpecificTypeTitles( - $typeTitles - )->_updateProducts( - $products - ); - } + //Save prepared custom options data. + $this->savePreparedCustomOptions( + $products, + $options, + $titles, + $prices, + $typeValues, + $typePrices, + $typeTitles + ); } return true; } + /** + * Remove all existing options if import behaviour is APPEND + * in other case remove options for products with empty "custom_options" row only. + * + * @param array $products + * @param array $optionsToRemove + * + * @return void + */ + private function removeExistingOptions(array $products, array $optionsToRemove): void + { + if ($this->getBehavior() != \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) { + $this->_deleteEntities(array_keys($products)); + } elseif (!empty($optionsToRemove)) { + // Remove options for products with empty "custom_options" row + $this->_deleteEntities($optionsToRemove); + } + } + /** * Load data of existed products * @@ -1537,9 +1546,7 @@ private function getExistingOptionTypeId($optionId, $storeId, $optionTypeTitle) */ protected function _parseRequiredData(array $rowData) { - if ($rowData[self::COLUMN_SKU] != '' && isset($this->_productsSkuToId[$rowData[self::COLUMN_SKU]])) { - $this->_rowProductId = $this->_productsSkuToId[$rowData[self::COLUMN_SKU]]; - } elseif (!isset($this->_rowProductId)) { + if (!isset($this->_rowProductId)) { return false; } @@ -1991,4 +1998,47 @@ private function getProductIdentifierField() } return $this->productEntityIdentifierField; } + + /** + * Save prepared custom options. + * + * @param array $products + * @param array $options + * @param array $titles + * @param array $prices + * @param array $typeValues + * @param array $typePrices + * @param array $typeTitles + * + * @return void + */ + private function savePreparedCustomOptions( + array $products, + array $options, + array $titles, + array $prices, + array $typeValues, + array $typePrices, + array $typeTitles + ): void { + if ($this->_isReadyForSaving($options, $titles, $typeValues)) { + $types = [ + 'values' => $typeValues, + 'prices' => $typePrices, + 'titles' => $typeTitles, + ]; + if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) { + $this->_compareOptionsWithExisting($options, $titles, $prices, $types['values']); + $this->restoreOriginalOptionTypeIds($types['values'], $types['prices'], $types['titles']); + } + + $this->_saveOptions($options) + ->_saveTitles($titles) + ->_savePrices($prices) + ->_saveSpecificTypeValues($types['values']) + ->_saveSpecificTypePrices($types['prices']) + ->_saveSpecificTypeTitles($types['titles']) + ->_updateProducts($products); + } + } } 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 27638f4b092dd..0d5bbf9eed2fd 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -271,15 +271,18 @@ public function testStockState() } /** - * Tests adding of custom options with existing and new product + * Tests adding of custom options with existing and new product. * * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @dataProvider getBehaviorDataProvider * @param string $importFile * @param string $sku + * @param int $expectedOptionsQty * @magentoAppIsolation enabled + * + * @return void */ - public function testSaveCustomOptions($importFile, $sku) + public function testSaveCustomOptions(string $importFile, string $sku, int $expectedOptionsQty): void { $pathToFile = __DIR__ . '/_files/' . $importFile; $importModel = $this->createImportModel($pathToFile); @@ -312,6 +315,7 @@ public function testSaveCustomOptions($importFile, $sku) // assert of options data $this->assertCount(count($expectedData['data']), $actualData['data']); $this->assertCount(count($expectedData['values']), $actualData['values']); + $this->assertCount($expectedOptionsQty, $actualData['options']); foreach ($expectedData['options'] as $expectedId => $expectedOption) { $elementExist = false; // find value in actual options and values @@ -411,17 +415,24 @@ public function testSaveCustomOptionsWithMultipleStoreViews() /** * @return array */ - public function getBehaviorDataProvider() + public function getBehaviorDataProvider(): array { return [ 'Append behavior with existing product' => [ - '$importFile' => 'product_with_custom_options.csv', - '$sku' => 'simple', + 'importFile' => 'product_with_custom_options.csv', + 'sku' => 'simple', + 'expectedOptionsQty' => 6, + ], + 'Append behavior with existing product and without options in import file' => [ + 'importFile' => 'product_without_custom_options.csv', + 'sku' => 'simple', + 'expectedOptionsQty' => 0, ], 'Append behavior with new product' => [ - '$importFile' => 'product_with_custom_options_new.csv', - '$sku' => 'simple_new', - ] + 'importFile' => 'product_with_custom_options_new.csv', + 'sku' => 'simple_new', + 'expectedOptionsQty' => 4, + ], ]; } @@ -571,43 +582,45 @@ protected function getExpectedOptionsData(string $pathToFile, string $storeCode break; } } - foreach (explode('|', $productData['data'][$storeRowId]['custom_options']) as $optionData) { - $option = array_values( - array_map( - function ($input) { - $data = explode('=', $input); - return [$data[0] => $data[1]]; - }, - explode(',', $optionData) - ) - ); - $option = array_merge(...$option); - - if (!empty($option['type']) && !empty($option['name'])) { - $lastOptionKey = $option['type'] . '|' . $option['name']; - if (!isset($expectedOptions[$expectedOptionId]) - || $expectedOptions[$expectedOptionId] != $lastOptionKey) { - $expectedOptionId++; - $expectedOptions[$expectedOptionId] = $lastOptionKey; - $expectedData[$expectedOptionId] = []; - foreach ($this->_assertOptions as $assertKey => $assertFieldName) { - if (array_key_exists($assertFieldName, $option) - && !(($assertFieldName == 'price' || $assertFieldName == 'sku') - && in_array($option['type'], $this->specificTypes)) - ) { - $expectedData[$expectedOptionId][$assertKey] = $option[$assertFieldName]; + if (!empty($productData['data'][$storeRowId]['custom_options'])) { + foreach (explode('|', $productData['data'][$storeRowId]['custom_options']) as $optionData) { + $option = array_values( + array_map( + function ($input) { + $data = explode('=', $input); + return [$data[0] => $data[1]]; + }, + explode(',', $optionData) + ) + ); + $option = array_merge(...$option); + + if (!empty($option['type']) && !empty($option['name'])) { + $lastOptionKey = $option['type'] . '|' . $option['name']; + if (!isset($expectedOptions[$expectedOptionId]) + || $expectedOptions[$expectedOptionId] != $lastOptionKey) { + $expectedOptionId++; + $expectedOptions[$expectedOptionId] = $lastOptionKey; + $expectedData[$expectedOptionId] = []; + foreach ($this->_assertOptions as $assertKey => $assertFieldName) { + if (array_key_exists($assertFieldName, $option) + && !(($assertFieldName == 'price' || $assertFieldName == 'sku') + && in_array($option['type'], $this->specificTypes)) + ) { + $expectedData[$expectedOptionId][$assertKey] = $option[$assertFieldName]; + } } } } - } - $optionValue = []; - if (!empty($option['name']) && !empty($option['option_title'])) { - foreach ($this->_assertOptionValues as $assertKey => $assertFieldName) { - if (isset($option[$assertFieldName])) { - $optionValue[$assertKey] = $option[$assertFieldName]; + $optionValue = []; + if (!empty($option['name']) && !empty($option['option_title'])) { + foreach ($this->_assertOptionValues as $assertKey => $assertFieldName) { + if (isset($option[$assertFieldName])) { + $optionValue[$assertKey] = $option[$assertFieldName]; + } } + $expectedValues[$expectedOptionId][] = $optionValue; } - $expectedValues[$expectedOptionId][] = $optionValue; } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_without_custom_options.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_without_custom_options.csv new file mode 100644 index 0000000000000..17bffb427a0e9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/product_without_custom_options.csv @@ -0,0 +1,2 @@ +sku,website_code,store_view_code,attribute_set_code,product_type,name,description,short_description,weight,product_online,visibility,product_websites,categories,price,special_price,special_price_from_date,special_price_to_date,tax_class_name,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,additional_images,additional_image_labels,configurable_variation_labels,configurable_variations,bundle_price_type,bundle_sku_type,bundle_weight_type,bundle_values,downloadble_samples,downloadble_links,associated_skus,related_skus,crosssell_skus,upsell_skus,custom_options,additional_attributes,manage_stock,is_in_stock,qty,out_of_stock_qty,is_qty_decimal,allow_backorders,min_cart_qty,max_cart_qty,notify_on_stock_below,qty_increments,enable_qty_increments,is_decimal_divided,new_from_date,new_to_date,gift_message_available,created_at,updated_at,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_price,msrp_display_actual_price_type,map_enabled +simple,base,,Default,simple,New Product,,,9,1,"Catalog, Search",base,,10,,,,Taxable Goods,new-product,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,999,0,0,0,1,10000,1,1,0,0,,,,,,,,,,,Block after Info Column,,, From 609d1f1d65c75030dfa02cc84350856478ca2026 Mon Sep 17 00:00:00 2001 From: Tom Reece Date: Fri, 25 May 2018 11:17:04 -0500 Subject: [PATCH 206/333] MC-62: Admin should be able to create a cart price rule of type Fixed amount discount - Fix code review feedback --- .../Section/CheckoutCartSummarySection.xml | 1 + .../StorefrontSalesRuleActionGroup.xml | 16 ++++++++++++++ .../Test/AdminCreateBuyXGetYFreeTest.xml | 13 +++++------ ...inCreateCartPriceRuleForCouponCodeTest.xml | 22 ++++++++++++++++++- .../AdminCreateFixedAmountDiscountTest.xml | 12 +++++----- ...CreateFixedAmountWholeCartDiscountTest.xml | 14 +++++------- .../AdminCreatePercentOfProductPriceTest.xml | 12 +++++----- 7 files changed, 58 insertions(+), 32 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartSummarySection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartSummarySection.xml index df236692d0d68..32a57a14cd9da 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartSummarySection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartSummarySection.xml @@ -14,5 +14,6 @@ + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml index 65b40abb72fd8..bba7a81a7c884 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml @@ -38,4 +38,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateBuyXGetYFreeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateBuyXGetYFreeTest.xml index 72ea11385e1ea..e8b1ebfa99be0 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateBuyXGetYFreeTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateBuyXGetYFreeTest.xml @@ -54,13 +54,10 @@ - - - - - - - - + + + + + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 805783f6e922f..33703d3a97072 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -20,6 +20,7 @@ + @@ -28,7 +29,7 @@ - + @@ -62,5 +63,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml index 3457f780244ec..b9fbf38236c11 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountDiscountTest.xml @@ -53,12 +53,10 @@ - - - - - - - + + + + + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml index 2d2bb24868032..6c6a1ece40444 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -53,14 +53,10 @@ - - - - - - - - - + + + + + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreatePercentOfProductPriceTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreatePercentOfProductPriceTest.xml index 6587044ea4e44..3086a7855d647 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreatePercentOfProductPriceTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreatePercentOfProductPriceTest.xml @@ -53,12 +53,10 @@ - - - - - - - + + + + + From c9207b63d2ce997002c2e66c5156d05bdbbeb376 Mon Sep 17 00:00:00 2001 From: Dan Mooney Date: Fri, 25 May 2018 13:08:03 -0500 Subject: [PATCH 207/333] MAGETWO-90347: Close Icon displays for attribute set on catalog product edit form Set isRemoveSelectedIcon to false by default in ui-select, and set it to true in url inputs --- app/code/Magento/Catalog/Ui/Component/UrlInput/Category.php | 1 + app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php | 1 + app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/Component/UrlInput/Category.php b/app/code/Magento/Catalog/Ui/Component/UrlInput/Category.php index 836bc5058777d..01d93de577927 100644 --- a/app/code/Magento/Catalog/Ui/Component/UrlInput/Category.php +++ b/app/code/Magento/Catalog/Ui/Component/UrlInput/Category.php @@ -46,6 +46,7 @@ public function getConfig(): array 'sortOrder' => 30, 'missingValuePlaceholder' => __('Category with ID: %s doesn\'t exist'), 'isDisplayMissingValuePlaceholder' => true, + 'isRemoveSelectedIcon' => true, ]; } } diff --git a/app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php b/app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php index efa8417e4686a..be73940237db4 100644 --- a/app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php +++ b/app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php @@ -50,6 +50,7 @@ public function getConfig(): array 'emptyOptionsHtml' => __('Start typing to find products'), 'missingValuePlaceholder' => __('Product with ID: %s doesn\'t exist'), 'isDisplayMissingValuePlaceholder' => true, + 'isRemoveSelectedIcon' => true, 'validationUrl' => $this->urlBuilder->getUrl('catalog/product/getSelected'), ]; } diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js index 5ed9199604c25..dba0992c5ba52 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js @@ -176,7 +176,7 @@ define([ pageLimit: 50, deviation: 30, validationLoading: false, - isRemoveSelectedIcon: true, + isRemoveSelectedIcon: false, debounce: 300, missingValuePlaceholder: $t('Entity with ID: %s doesn\'t exist'), isDisplayMissingValuePlaceholder: false, From 7fe4eaec58676ae3119b1b5fe5a2d66dc48da96d Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Fri, 25 May 2018 14:05:08 -0500 Subject: [PATCH 208/333] MC-188: Admin should be able to add images for category MC-212: Admin should be able to remove images from category - added mftf tests --- .../Backend/ActionGroup/LogoutActionGroup.xml | 2 +- .../Backend/Page/AdminLogoutPage.xml | 4 +- .../ActionGroup/AdminCategoryActionGroup.xml | 64 +++++++++++++++++++ .../Catalog/Page/AdminCategoryPage.xml | 1 + .../Section/AdminCategoryContentSection.xml | 19 ++++++ .../Section/StorefrontCategoryMainSection.xml | 1 + .../Test/AdminAddImageForCategoryTest.xml | 48 ++++++++++++++ ...minRemoveDefaultImageSimpleProductTest.xml | 1 - .../Test/AdminRemoveImageFromCategoryTest.xml | 51 +++++++++++++++ 9 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminCategoryContentSection.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddImageForCategoryTest.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveImageFromCategoryTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Backend/ActionGroup/LogoutActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Backend/ActionGroup/LogoutActionGroup.xml index 1e7914c32c939..4a5bed2ed0a68 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Backend/ActionGroup/LogoutActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Backend/ActionGroup/LogoutActionGroup.xml @@ -9,6 +9,6 @@ - + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Backend/Page/AdminLogoutPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Backend/Page/AdminLogoutPage.xml index 5660df619c73d..6eb02dfb3b7fb 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Backend/Page/AdminLogoutPage.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Backend/Page/AdminLogoutPage.xml @@ -8,7 +8,5 @@ - -
- + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminCategoryActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminCategoryActionGroup.xml index 19c9ca94377f2..96e40e348a6f2 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminCategoryActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminCategoryActionGroup.xml @@ -25,6 +25,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Page/AdminCategoryPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Page/AdminCategoryPage.xml index f51d2e8a28939..7d0e7f4f99e42 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Page/AdminCategoryPage.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Page/AdminCategoryPage.xml @@ -18,5 +18,6 @@
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminCategoryContentSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminCategoryContentSection.xml new file mode 100644 index 0000000000000..d0e129e1bb441 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminCategoryContentSection.xml @@ -0,0 +1,19 @@ + + + + +
+ + + + + + +
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontCategoryMainSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontCategoryMainSection.xml index 39275d61485a8..45a85386fe726 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontCategoryMainSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontCategoryMainSection.xml @@ -23,5 +23,6 @@ +
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddImageForCategoryTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddImageForCategoryTest.xml new file mode 100644 index 0000000000000..ba90296d12380 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminAddImageForCategoryTest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + <description value="Admin should be able to add image to a Category"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-188"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="DeleteCategory" 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"> + <argument name="categoryEntity" value="SimpleSubCategory"/> + </actionGroup> + <actionGroup ref="addCategoryImage" stepKey="addCategoryImage"/> + <actionGroup ref="saveCategoryForm" stepKey="saveCategoryForm"/> + + <!-- Verify category with image in admin --> + <actionGroup ref="checkCategoryImageInAdmin" stepKey="checkCategoryImageInAdmin"/> + + <!-- Verify category with image in storefront --> + <actionGroup ref="CheckCategoryOnStorefront" stepKey="CheckCategoryOnStorefront"> + <argument name="categoryEntity" value="SimpleSubCategory"/> + </actionGroup> + <seeElement selector="{{StorefrontCategoryMainSection.imageSource(ImageUpload.filename)}}" stepKey="seeImage"/> + </test> +</tests> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageSimpleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageSimpleProductTest.xml index 36b2c5e9ffac5..4623f2ad4a90c 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageSimpleProductTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveDefaultImageSimpleProductTest.xml @@ -17,7 +17,6 @@ <severity value="MAJOR"/> <testCaseId value="MC-195"/> <group value="Catalog"/> - <group value="ji"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveImageFromCategoryTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveImageFromCategoryTest.xml new file mode 100644 index 0000000000000..11b29b9dd33e7 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminRemoveImageFromCategoryTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminRemoveImageFromCategoryTest"> + <annotations> + <features value="Catalog"/> + <stories value="Add/remove images and videos for all product types and category"/> + <title value="Admin should be able to remove image from a Category"/> + <description value="Admin should be able to remove image from a Category"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-212"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="DeleteCategory" 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"> + <argument name="categoryEntity" value="SimpleSubCategory"/> + </actionGroup> + <actionGroup ref="addCategoryImage" stepKey="addCategoryImage"/> + <actionGroup ref="saveCategoryForm" stepKey="saveCategoryForm"/> + <actionGroup ref="checkCategoryImageInAdmin" stepKey="checkCategoryImageInAdmin"/> + + <!-- Remove image from category --> + <actionGroup ref="removeCategoryImage" stepKey="removeCategoryImage"/> + <actionGroup ref="saveCategoryForm" stepKey="saveCategoryFormAfterRemove"/> + + <actionGroup ref="CheckCategoryOnStorefront" 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 From 8b02118b7af1c8edcae46b59fa556406ff53351e Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Fri, 25 May 2018 14:59:23 -0500 Subject: [PATCH 209/333] MC-75: Admin should be able to create a cart price rule for auto generated coupon codes --- .../AdminCartPriceRulesFormSection.xml | 9 +++ ...ateCartPriceRuleForGeneratedCouponTest.xml | 79 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml index d95cdee4ef4c8..3912a6ab31e8d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/AdminCartPriceRulesFormSection.xml @@ -9,6 +9,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminCartPriceRulesFormSection"> <element name="save" type="button" selector="#save" timeout="30"/> + <element name="saveAndContinue" type="button" selector="#save_and_continue" timeout="30"/> <element name="delete" type="button" selector="#delete" timeout="30"/> <element name="modalAcceptButton" type="button" selector="button.action-accept" timeout="30"/> @@ -19,11 +20,19 @@ <element name="customerGroups" type="multiselect" selector="select[name='customer_group_ids']"/> <element name="coupon" type="select" selector="select[name='coupon_type']"/> <element name="couponCode" type="input" selector="input[name='coupon_code']"/> + <element name="useAutoGeneration" type="checkbox" selector="input[name='use_auto_generation']"/> <!-- Actions sub-form --> <element name="actionsHeader" type="button" selector="div[data-index='actions']" timeout="30"/> <element name="apply" type="select" selector="select[name='simple_action']"/> <element name="discountAmount" type="input" selector="input[name='discount_amount']"/> <element name="discountStep" type="input" selector="input[name='discount_step']"/> + + <!-- Manage Coupon Codes sub-form --> + <element name="manageCouponCodesHeader" type="button" selector="div[data-index='manage_coupon_codes']" timeout="30"/> + <element name="successMessage" type="text" selector="div.message.message-success.success"/> + <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"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml new file mode 100644 index 0000000000000..b5b30ce3c265a --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminCreateCartPriceRuleForGeneratedCouponTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Admin should be able to create a cart price rule for auto generated coupon codes"/> + <description value="Admin should be able to create a cart price rule for auto generated coupon codes"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-75"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <!-- Delete the cart price rule we made during the test --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{_defaultCoupon.code}}"/> + </actionGroup> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Create a cart price rule --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForPriceList"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> + <checkOption selector="{{AdminCartPriceRulesFormSection.useAutoGeneration}}" stepKey="tickAutoGeneration"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="0.99" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.saveAndContinue}}" stepKey="clickSaveAndContinueButton"/> + + <!-- Generate some coupon codes --> + <click selector="{{AdminCartPriceRulesFormSection.manageCouponCodesHeader}}" stepKey="expandCouponSection"/> + <fillField selector="{{AdminCartPriceRulesFormSection.couponQty}}" userInput="10" stepKey="fillCouponQty"/> + <click selector="{{AdminCartPriceRulesFormSection.generateCouponsButton}}" stepKey="clickGenerate"/> + <see selector="{{AdminCartPriceRulesFormSection.successMessage}}" userInput="10 coupon(s) have been generated." stepKey="seeGenerationSuccess"/> + + <!-- 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"> + <argument name="category" value="$$createPreReqCategory$$"/> + <argument name="simpleProduct" value="_defaultProduct"/> + </actionGroup> + + <!-- Spot check the storefront --> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <conditionalClick selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" dependentSelector="{{StorefrontSalesRuleCartCouponSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader"/> + <waitForElementVisible selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="waitForCouponField" /> + <fillField selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" userInput="{$grabCouponCode}" stepKey="fillCouponField"/> + <click selector="{{StorefrontSalesRuleCartCouponSection.applyButton}}" stepKey="clickApplyButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$0.99" stepKey="seeDiscountTotal"/> + </test> +</tests> From f36db11b7ebe50f91dcef2de2c562dd04fad5a15 Mon Sep 17 00:00:00 2001 From: Tommy Wiebell <twiebell@magento.com> Date: Fri, 25 May 2018 16:06:07 -0500 Subject: [PATCH 210/333] MAGETWO-92219: Incorrect types of extensions for attributes - Move attribute type fix from Catalog to abstract attribute implementation --- .../Model/ResourceModel/Eav/Attribute.php | 14 -------------- .../Entity/Attribute/AbstractAttribute.php | 19 +++++++++++++++++-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php index d882ad078b97f..8f8e9f6bfedfa 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php @@ -895,18 +895,4 @@ public function setIsFilterableInGrid($isFilterableInGrid) $this->setData(self::IS_FILTERABLE_IN_GRID, $isFilterableInGrid); return $this; } - - /** - * @return \Magento\Eav\Api\Data\AttributeExtensionInterface - */ - public function getExtensionAttributes() - { - $extensionAttributes = $this->_getExtensionAttributes(); - if (null === $extensionAttributes) { - /** @var \Magento\Eav\Api\Data\AttributeExtensionInterface $extensionAttributes */ - $extensionAttributes = $this->eavAttributeFactory->create(); - $this->setExtensionAttributes($extensionAttributes); - } - return $extensionAttributes; - } } diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index a7170888f8a01..f5c7d88919f3c 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -141,6 +141,11 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens 'static', ]; + /** + * @var \Magento\Eav\Api\Data\AttributeExtensionFactory + */ + private $eavExtensionFactory; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -157,6 +162,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param \Magento\Eav\Api\Data\AttributeExtensionFactory|null $eavExtensionFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @codeCoverageIgnore */ @@ -175,7 +181,8 @@ public function __construct( \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + \Magento\Eav\Api\Data\AttributeExtensionFactory $eavExtensionFactory = null ) { parent::__construct( $context, @@ -194,6 +201,8 @@ public function __construct( $this->optionDataFactory = $optionDataFactory; $this->dataObjectProcessor = $dataObjectProcessor; $this->dataObjectHelper = $dataObjectHelper; + $this->eavExtensionFactory = $eavExtensionFactory ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Eav\Api\Data\AttributeExtensionFactory::class); } /** @@ -1314,7 +1323,13 @@ public function setValidationRules(array $validationRules = null) */ public function getExtensionAttributes() { - return $this->_getExtensionAttributes(); + $extensionAttributes = $this->_getExtensionAttributes(); + if (!($extensionAttributes instanceof \Magento\Eav\Api\Data\AttributeExtensionInterface)) { + /** @var \Magento\Eav\Api\Data\AttributeExtensionInterface $extensionAttributes */ + $extensionAttributes = $this->eavExtensionFactory->create(); + $this->setExtensionAttributes($extensionAttributes); + } + return $extensionAttributes; } /** From b5feecc4b3886c570094f79ed47ec03a76d0b580 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Sat, 26 May 2018 11:21:58 +0530 Subject: [PATCH 211/333] Added language translation for message string --- .../Controller/Adminhtml/System/Message/ListAction.php | 6 ++++-- app/code/Magento/AdminNotification/i18n/en_US.csv | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/System/Message/ListAction.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/System/Message/ListAction.php index 303675b968256..d58a7ec31f77d 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/System/Message/ListAction.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/System/Message/ListAction.php @@ -62,8 +62,10 @@ public function execute() if (empty($result)) { $result[] = [ 'severity' => (string)\Magento\Framework\Notification\MessageInterface::SEVERITY_NOTICE, - 'text' => 'You have viewed and resolved all recent system notices. ' - . 'Please refresh the web page to clear the notice alert.', + 'text' => __( + 'You have viewed and resolved all recent system notices. ' + . 'Please refresh the web page to clear the notice alert.' + ) ]; } /** @var \Magento\Framework\Controller\Result\Json $resultJson */ diff --git a/app/code/Magento/AdminNotification/i18n/en_US.csv b/app/code/Magento/AdminNotification/i18n/en_US.csv index 16c5abb9db0d2..db5a4c9254814 100644 --- a/app/code/Magento/AdminNotification/i18n/en_US.csv +++ b/app/code/Magento/AdminNotification/i18n/en_US.csv @@ -48,3 +48,4 @@ Severity,Severity "Date Added","Date Added" Message,Message Actions,Actions +"You have viewed and resolved all recent system notices. Please refresh the web page to clear the notice alert.","You have viewed and resolved all recent system notices. Please refresh the web page to clear the notice alert." From 60bc9a814ccd0268dd3649e798a7ec3e0bb01350 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Sat, 19 May 2018 15:22:57 +0530 Subject: [PATCH 212/333] Added language translation for comment tag --- app/code/Magento/Signifyd/etc/adminhtml/system.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Signifyd/etc/adminhtml/system.xml b/app/code/Magento/Signifyd/etc/adminhtml/system.xml index d9ba2f7ffdff2..71f5916ca5325 100644 --- a/app/code/Magento/Signifyd/etc/adminhtml/system.xml +++ b/app/code/Magento/Signifyd/etc/adminhtml/system.xml @@ -13,7 +13,7 @@ <resource>Magento_Sales::fraud_protection</resource> <group id="signifyd" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> <fieldset_css>signifyd-logo-header</fieldset_css> - <group id="about" translate="label" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="about" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Fieldset\Info</frontend_model> <fieldset_css>signifyd-about-header</fieldset_css> <label><![CDATA[Protect your store from fraud with Guaranteed Fraud Protection by Signifyd.]]></label> @@ -26,12 +26,12 @@ </comment> <more_url>https://www.signifyd.com/magento-guaranteed-fraud-protection</more_url> </group> - <group id="config" translate="label" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> <fieldset_css>signifyd-about-header</fieldset_css> <label>Configuration</label> <comment><![CDATA[<a href="https://www.signifyd.com/resources/manual/magento-2/signifyd-on-magento-integration-guide/" target="_blank">View our setup guide</a> for step-by-step instructions on how to integrate Signifyd with Magento.<br />For support contact <a href="mailto:support@signifyd.com">support@signifyd.com</a>.]]> </comment> - <field id="active" translate="label comment" type="select" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="active" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Enable this Solution</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>fraud_protection/signifyd/active</config_path> From cf05a84f73a36b2fabd08d630e077b6e3ea922f5 Mon Sep 17 00:00:00 2001 From: Hirokazu Nishi <nishi@principle-works.jp> Date: Sat, 26 May 2018 23:01:53 +0900 Subject: [PATCH 213/333] fix for Wrong price amount on product page #11717 --- lib/internal/Magento/Framework/Locale/Format.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index 7bb4cb4b7d432..8efedf2eaa825 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -99,7 +99,7 @@ public function getPriceFormat($localeCode = null, $currencyCode = null) $currency = $this->_scopeResolver->getScope()->getCurrentCurrency(); } - $formatter = new \NumberFormatter($localeCode, \NumberFormatter::CURRENCY); + $formatter = new \NumberFormatter($localeCode . '@currency=' . $currency->getCode(), \NumberFormatter::CURRENCY); $format = $formatter->getPattern(); $decimalSymbol = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); $groupSymbol = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); From 973639634fac2107f41e8c799bc8fac62621c02f Mon Sep 17 00:00:00 2001 From: vitaliyboyko <vitaliyboyko@i.ua> Date: Sun, 27 May 2018 16:11:24 +0300 Subject: [PATCH 214/333] Fix typos in Multishipping module --- .../Multishipping/Block/Checkout/AbstractMultishipping.php | 2 +- app/code/Magento/Multishipping/Block/Checkout/Shipping.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php b/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php index 0de66ccd505a4..d3c17a8d7c8de 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php +++ b/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php @@ -5,7 +5,7 @@ */ /** - * Mustishipping checkout base abstract block + * Multishipping checkout base abstract block * * @author Magento Core Team <core@magentocommerce.com> */ diff --git a/app/code/Magento/Multishipping/Block/Checkout/Shipping.php b/app/code/Magento/Multishipping/Block/Checkout/Shipping.php index 77981c736b9e9..ef1aa6301b23d 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/Shipping.php +++ b/app/code/Magento/Multishipping/Block/Checkout/Shipping.php @@ -9,7 +9,7 @@ use Magento\Quote\Model\Quote\Address; /** - * Mustishipping checkout shipping + * Multishipping checkout shipping * * @api * @author Magento Core Team <core@magentocommerce.com> From 67a203869a0cff6495bf4953659f0a720cc0641d Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Mon, 28 May 2018 12:06:40 +0530 Subject: [PATCH 215/333] Fixed line exceeds issue. --- lib/internal/Magento/Framework/Locale/Format.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index 8efedf2eaa825..00379c87daaf9 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -99,7 +99,10 @@ public function getPriceFormat($localeCode = null, $currencyCode = null) $currency = $this->_scopeResolver->getScope()->getCurrentCurrency(); } - $formatter = new \NumberFormatter($localeCode . '@currency=' . $currency->getCode(), \NumberFormatter::CURRENCY); + $formatter = new \NumberFormatter( + $localeCode . '@currency=' . $currency->getCode(), + \NumberFormatter::CURRENCY + ); $format = $formatter->getPattern(); $decimalSymbol = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); $groupSymbol = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL); From 4a9562d391fcddfc6ac3437aad9d3837ba8fd460 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 28 May 2018 11:18:10 +0200 Subject: [PATCH 216/333] [fix] getConfiguration() invoked with 1 parameter, 0 required --- app/code/Magento/Ui/Component/Bookmark.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/Component/Bookmark.php b/app/code/Magento/Ui/Component/Bookmark.php index aa1d7a9fb5c0a..db824f11bd4b1 100644 --- a/app/code/Magento/Ui/Component/Bookmark.php +++ b/app/code/Magento/Ui/Component/Bookmark.php @@ -82,11 +82,11 @@ public function prepare() } } - $this->setData('config', array_replace_recursive($config, $this->getConfiguration($this))); + $this->setData('config', array_replace_recursive($config, $this->getConfiguration())); parent::prepare(); - $jsConfig = $this->getConfiguration($this); + $jsConfig = $this->getConfiguration(); $this->getContext()->addComponentDefinition($this->getComponentName(), $jsConfig); } } From b6cb73ca775b92ec38cb708e180ab823e36e80a5 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 28 May 2018 11:19:26 +0200 Subject: [PATCH 217/333] [fix] Class Magento\Ui\Component\Control\ActionPool constructor invoked with 3 parameters, 2 required. --- .../Magento/Ui/Test/Unit/Component/Control/ActionPoolTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/Test/Unit/Component/Control/ActionPoolTest.php b/app/code/Magento/Ui/Test/Unit/Component/Control/ActionPoolTest.php index e18155cd08c53..85226b780aac3 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Control/ActionPoolTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Control/ActionPoolTest.php @@ -83,8 +83,7 @@ protected function setUp() $this->items[$this->key] = $this->createPartialMock(\Magento\Ui\Component\Control\Item::class, ['setData']); $this->actionPool = new ActionPool( $this->contextMock, - $this->itemFactoryMock, - $this->toolbarBlockMock + $this->itemFactoryMock ); } From a0c157e7b9470dd23901404c1583ad61d1adee8c Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 28 May 2018 13:17:23 +0200 Subject: [PATCH 218/333] [fix] add missing properties --- app/code/Magento/Widget/Model/Widget/Instance.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/Widget/Model/Widget/Instance.php b/app/code/Magento/Widget/Model/Widget/Instance.php index f21609cb1ef5c..594ec5c0a2289 100644 --- a/app/code/Magento/Widget/Model/Widget/Instance.php +++ b/app/code/Magento/Widget/Model/Widget/Instance.php @@ -96,6 +96,16 @@ class Instance extends \Magento\Framework\Model\AbstractModel */ protected $_relatedCacheTypes; + /** + * @var \Magento\Catalog\Model\Product\Type + */ + protected $_productType; + + /** + * @var \Magento\Widget\Model\Config\Reader + */ + protected $_reader; + /** * @var \Magento\Framework\Escaper */ From 7e69251f88fa793542313487e6cd0564f2b59a79 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 28 May 2018 13:24:53 +0200 Subject: [PATCH 219/333] [fix] reference to invalid class Magento\Backend\Block\Catalog\Product\Widget\Chooser --- .../Magento/Widget/Test/Unit/Model/_files/widget_config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Widget/Test/Unit/Model/_files/widget_config.php b/app/code/Magento/Widget/Test/Unit/Model/_files/widget_config.php index 098fb14c9cec9..697e56cbcbb38 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/_files/widget_config.php +++ b/app/code/Magento/Widget/Test/Unit/Model/_files/widget_config.php @@ -48,7 +48,7 @@ 'type' => 'label', '@' => ['type' => 'complex'], 'helper_block' => [ - 'type' => \Magento\Backend\Block\Catalog\Product\Widget\Chooser::class, + 'type' => \Magento\Catalog\Block\Adminhtml\Product\Widget\Chooser::class, 'data' => ['button' => ['open' => 'Select Product...']], ], 'visible' => '1', From e1c0fa95b264de509e7a25f4bf4d66ba15b222e7 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 28 May 2018 13:29:39 +0200 Subject: [PATCH 220/333] [fix] widget.xml to match fixed class reference --- app/code/Magento/Widget/Test/Unit/Model/_files/widget.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Widget/Test/Unit/Model/_files/widget.xml b/app/code/Magento/Widget/Test/Unit/Model/_files/widget.xml index 02e65707d322f..6deddeca59ef9 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/_files/widget.xml +++ b/app/code/Magento/Widget/Test/Unit/Model/_files/widget.xml @@ -41,7 +41,7 @@ </parameter> <parameter name="id_path" xsi:type="block" visible="true" required="true" sort_order="10"> <label translate="true">Product</label> - <block class="Magento\Backend\Block\Catalog\Product\Widget\Chooser"> + <block class="Magento\Catalog\Block\Adminhtml\Product\Widget\Chooser"> <data> <item name="button" xsi:type="array"> <item name="open" xsi:type="string" translate="true">Select Product...</item> From 65d72bb598e8dba58231f19e3d3990cbbc5a3215 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 28 May 2018 20:04:43 +0200 Subject: [PATCH 221/333] [task] add SuppressWarnings(PHPMD.TooManyFields) --- app/code/Magento/Widget/Model/Widget/Instance.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Widget/Model/Widget/Instance.php b/app/code/Magento/Widget/Model/Widget/Instance.php index 594ec5c0a2289..afe7ef3766f7c 100644 --- a/app/code/Magento/Widget/Model/Widget/Instance.php +++ b/app/code/Magento/Widget/Model/Widget/Instance.php @@ -22,6 +22,7 @@ * @method int getThemeId() * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) * @since 100.0.2 */ class Instance extends \Magento\Framework\Model\AbstractModel From b39774c9eeb2f8a3a5942c036c82967d7186eb59 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 29 May 2018 09:53:26 +0300 Subject: [PATCH 222/333] MAGETWO-90789: [Magento cloud] Custom options should be cleared if custom options in import file is empty --- .../Model/Import/Product/Option.php | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php index 245553ea78169..adb660dd118f9 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php @@ -1235,7 +1235,7 @@ protected function _importData() $multiRowData = $this->_getMultiRowFormat($rowData); if (!empty($rowData[self::COLUMN_SKU]) && isset($this->_productsSkuToId[$rowData[self::COLUMN_SKU]])) { $this->_rowProductId = $this->_productsSkuToId[$rowData[self::COLUMN_SKU]]; - if (array_key_exists('custom_options', $rowData) && trim($rowData['custom_options']) === "") { + if (array_key_exists('custom_options', $rowData) && trim($rowData['custom_options']) === '') { $optionsToRemove[] = $this->_rowProductId; } } @@ -1273,15 +1273,18 @@ protected function _importData() $this->removeExistingOptions($products, $optionsToRemove); + $types = [ + 'values' => $typeValues, + 'prices' => $typePrices, + 'titles' => $typeTitles, + ]; //Save prepared custom options data. $this->savePreparedCustomOptions( $products, $options, $titles, $prices, - $typeValues, - $typePrices, - $typeTitles + $types ); } @@ -1546,7 +1549,7 @@ private function getExistingOptionTypeId($optionId, $storeId, $optionTypeTitle) */ protected function _parseRequiredData(array $rowData) { - if (!isset($this->_rowProductId)) { + if ($this->_rowProductId === null) { return false; } @@ -2006,9 +2009,7 @@ private function getProductIdentifierField() * @param array $options * @param array $titles * @param array $prices - * @param array $typeValues - * @param array $typePrices - * @param array $typeTitles + * @param array $types * * @return void */ @@ -2017,16 +2018,9 @@ private function savePreparedCustomOptions( array $options, array $titles, array $prices, - array $typeValues, - array $typePrices, - array $typeTitles + array $types ): void { - if ($this->_isReadyForSaving($options, $titles, $typeValues)) { - $types = [ - 'values' => $typeValues, - 'prices' => $typePrices, - 'titles' => $typeTitles, - ]; + if ($this->_isReadyForSaving($options, $titles, $types['values'])) { if ($this->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND) { $this->_compareOptionsWithExisting($options, $titles, $prices, $types['values']); $this->restoreOriginalOptionTypeIds($types['values'], $types['prices'], $types['titles']); From 2f82461e0f6af7d49d71c302b68da4b742b991c4 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 29 May 2018 10:36:09 +0300 Subject: [PATCH 223/333] MAGETWO-90784: Catalog Rule is not applied --- .../Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php | 2 +- .../Magento/CatalogRule/Model/Indexer/Product/PriceTest.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php index 5149fddfc080a..285e1781e2f95 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php @@ -71,7 +71,7 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Framework\Module\Manager $moduleManager * @param string|null $connectionName - * @param IndexTableStructureFactory $indexTableStructureFactory + * @param null|IndexTableStructureFactory $indexTableStructureFactory * @param PriceModifierInterface[] $priceModifiers */ public function __construct( 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 ad580cfb3aeb5..b1a10c894f83a 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 @@ -28,6 +28,9 @@ class PriceTest extends \PHPUnit\Framework\TestCase */ private $resourceRule; + /** + * {@inheritdoc} + */ protected function setUp() { $this->resourceRule = Bootstrap::getObjectManager()->get(Rule::class); From 2a20abd1ffa8d7fef1dfaa0eb1063ac45586d4c2 Mon Sep 17 00:00:00 2001 From: Leandro Ferraz Luvisotto <l.luvisotto@youwe.nl> Date: Tue, 29 May 2018 10:39:15 +0200 Subject: [PATCH 224/333] Missing PHPDoc comment --- .../CollectionProcessor/ExtensibleEntityProcessor.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/ExtensibleEntityProcessor.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/ExtensibleEntityProcessor.php index e35c8b3e20c51..365d4f018ef4a 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/ExtensibleEntityProcessor.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/ExtensibleEntityProcessor.php @@ -32,12 +32,6 @@ public function __construct(JoinProcessorInterface $joinProcessor) $this->joinProcessor = $joinProcessor; } - /** - * @param Collection $collection - * @param SearchCriteriaInterface $searchCriteria - * @param array $attributeNames - * @return Collection - */ public function process( Collection $collection, SearchCriteriaInterface $searchCriteria, From 12f2e936d3fa433b0872886894745c41418f39b6 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 29 May 2018 14:30:00 +0300 Subject: [PATCH 225/333] MAGETWO-89407: [2.3] PayPal Payments Pro settings gets from wrong store --- .../Payflow/Service/Request/SecureToken.php | 2 +- app/code/Magento/Paypal/Model/Payflowlink.php | 3 + .../Service/Request/SecureTokenTest.php | 42 +++- .../Test/Unit/Model/PayflowlinkTest.php | 11 +- .../default_payment_configuration.php | 30 +++ ...default_payment_configuration_rollback.php | 24 ++ .../Paypal/Fixtures/process_config_data.php | 22 ++ .../Fixtures/store_payment_configuration.php | 32 +++ .../store_payment_configuration_rollback.php | 30 +++ .../website_payment_configuration.php | 31 +++ ...website_payment_configuration_rollback.php | 30 +++ .../Service/Request/SecureTokenTest.php | 214 ++++++++++++++++++ .../Paypal/_files/fixed_discount_rollback.php | 26 +++ .../Paypal/_files/quote_payflowpro.php | 16 +- .../_files/quote_payflowpro_rollback.php | 43 ++++ 15 files changed, 535 insertions(+), 21 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Fixtures/default_payment_configuration.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Fixtures/default_payment_configuration_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Fixtures/process_config_data.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Fixtures/store_payment_configuration.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Fixtures/store_payment_configuration_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Fixtures/website_payment_configuration.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Fixtures/website_payment_configuration_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/_files/fixed_discount_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php b/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php index 9d215ca6cbe17..da5599984b701 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php @@ -11,7 +11,6 @@ use Magento\Paypal\Model\Payflow\Transparent; use Magento\Paypal\Model\Payflowpro; use Magento\Quote\Model\Quote; -use Magento\Sales\Model\Order\Payment; /** * Class SecureToken @@ -59,6 +58,7 @@ public function __construct( */ public function requestToken(Quote $quote) { + $this->transparent->setStore($quote->getStoreId()); $request = $this->transparent->buildBasicRequest(); $request->setTrxtype(Payflowpro::TRXTYPE_AUTH_ONLY); diff --git a/app/code/Magento/Paypal/Model/Payflowlink.php b/app/code/Magento/Paypal/Model/Payflowlink.php index 792309bd76cf9..1955ef3c67661 100644 --- a/app/code/Magento/Paypal/Model/Payflowlink.php +++ b/app/code/Magento/Paypal/Model/Payflowlink.php @@ -10,6 +10,7 @@ use Magento\Payment\Model\Method\ConfigInterfaceFactory; use Magento\Paypal\Model\Payflow\Service\Response\Handler\HandlerInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Email\Sender\OrderSender; /** @@ -239,11 +240,13 @@ public function initialize($paymentAction, $stateObject) case \Magento\Paypal\Model\Config::PAYMENT_ACTION_AUTH: case \Magento\Paypal\Model\Config::PAYMENT_ACTION_SALE: $payment = $this->getInfoInstance(); + /** @var Order $order */ $order = $payment->getOrder(); $order->setCanSendNewEmailFlag(false); $payment->setAmountAuthorized($order->getTotalDue()); $payment->setBaseAmountAuthorized($order->getBaseTotalDue()); $this->_generateSecureSilentPostHash($payment); + $this->setStore($order->getStoreId()); $request = $this->_buildTokenRequest($payment); $response = $this->postRequest($request, $this->getConfig()); $this->_processTokenErrors($response, $payment); diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Request/SecureTokenTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Request/SecureTokenTest.php index d4a7db25cae89..d8e54ad28fcc8 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Request/SecureTokenTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/Request/SecureTokenTest.php @@ -10,6 +10,9 @@ use Magento\Framework\UrlInterface; use Magento\Paypal\Model\Payflow\Service\Request\SecureToken; use Magento\Paypal\Model\Payflow\Transparent; +use Magento\Paypal\Model\PayflowConfig; +use Magento\Quote\Model\Quote; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Test class for \Magento\Paypal\Model\Payflow\Service\Request\SecureToken @@ -19,23 +22,26 @@ class SecureTokenTest extends \PHPUnit\Framework\TestCase /** * @var SecureToken */ - protected $model; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject|Transparent + * @var Transparent|MockObject */ - protected $transparent; + private $transparent; /** - * @var \PHPUnit_Framework_MockObject_MockObject|Random + * @var Random|MockObject */ - protected $mathRandom; + private $mathRandom; /** - * @var \PHPUnit_Framework_MockObject_MockObject|UrlInterface + * @var UrlInterface|MockObject */ - protected $url; + private $url; + /** + * @inheritdoc + */ protected function setUp() { $this->url = $this->createMock(\Magento\Framework\UrlInterface::class); @@ -52,11 +58,29 @@ protected function setUp() public function testRequestToken() { $request = new DataObject(); + $storeId = 1; $secureTokenID = 'Sdj46hDokds09c8k2klaGJdKLl032ekR'; + $response = new DataObject([ + 'result' => '0', + 'respmsg' => 'Approved', + 'securetoken' => '80IgSbabyj0CtBDWHZZeQN3', + 'securetokenid' => $secureTokenID, + 'result_code' => '0', + ]); + + $quote = $this->getMockBuilder(Quote::class) + ->disableOriginalConstructor() + ->getMock(); + $quote->expects($this->once()) + ->method('getStoreId') + ->willReturn($storeId); $this->transparent->expects($this->once()) ->method('buildBasicRequest') ->willReturn($request); + $this->transparent->expects($this->once()) + ->method('setStore') + ->with($storeId); $this->transparent->expects($this->once()) ->method('fillCustomerContacts'); $this->transparent->expects($this->once()) @@ -64,7 +88,7 @@ public function testRequestToken() ->willReturn($this->createMock(\Magento\Paypal\Model\PayflowConfig::class)); $this->transparent->expects($this->once()) ->method('postRequest') - ->willReturn(new DataObject()); + ->willReturn($response); $this->mathRandom->expects($this->once()) ->method('getUniqueHash') @@ -73,8 +97,6 @@ public function testRequestToken() $this->url->expects($this->exactly(3)) ->method('getUrl'); - $quote = $this->createMock(\Magento\Quote\Model\Quote::class); - $this->model->requestToken($quote); $this->assertEquals($secureTokenID, $request->getSecuretokenid()); diff --git a/app/code/Magento/Paypal/Test/Unit/Model/PayflowlinkTest.php b/app/code/Magento/Paypal/Test/Unit/Model/PayflowlinkTest.php index 362615e965d1b..80c8194e07654 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/PayflowlinkTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/PayflowlinkTest.php @@ -101,16 +101,20 @@ protected function setUp() public function testInitialize() { + $storeId = 1; $order = $this->createMock(\Magento\Sales\Model\Order::class); + $order->expects($this->exactly(2)) + ->method('getStoreId') + ->willReturn($storeId); $this->infoInstance->expects($this->any()) ->method('getOrder') - ->will($this->returnValue($order)); + ->willReturn($order); $this->infoInstance->expects($this->any()) ->method('setAdditionalInformation') - ->will($this->returnSelf()); + ->willReturnSelf(); $this->paypalConfig->expects($this->once()) ->method('getBuildNotationCode') - ->will($this->returnValue('build notation code')); + ->willReturn('build notation code'); $response = new \Magento\Framework\DataObject( [ @@ -148,6 +152,7 @@ public function testInitialize() $stateObject = new \Magento\Framework\DataObject(); $this->model->initialize(\Magento\Paypal\Model\Config::PAYMENT_ACTION_AUTH, $stateObject); + self::assertEquals($storeId, $this->model->getStore(), '{Store} should be set'); } /** diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/default_payment_configuration.php b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/default_payment_configuration.php new file mode 100644 index 0000000000000..2c1614b9317c5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/default_payment_configuration.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Config\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/process_config_data.php'; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var EncryptorInterface $encryptor */ +$encryptor = $objectManager->get(EncryptorInterface::class); + +// save payment configuration for the default scope +$configData = [ + 'payment/payflowpro/partner' => 'def_partner', + 'payment/payflowpro/vendor' => 'def_vendor', + 'payment/payflowpro/user' => $encryptor->encrypt('def_user'), + 'payment/payflowpro/pwd' => $encryptor->encrypt('def_pwd'), +]; +/** @var Config $defConfig */ +$defConfig = $objectManager->create(Config::class); +$defConfig->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); +$processConfigData($defConfig, $configData); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/default_payment_configuration_rollback.php b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/default_payment_configuration_rollback.php new file mode 100644 index 0000000000000..b9cf1707c7b6c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/default_payment_configuration_rollback.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/process_config_data.php'; + +$objectManager = Bootstrap::getObjectManager(); + +$configData = [ + 'payment/payflowpro/partner', + 'payment/payflowpro/vendor', + 'payment/payflowpro/user', + 'payment/payflowpro/pwd', +]; +/** @var WriterInterface $configWriter */ +$configWriter = $objectManager->get(WriterInterface::class); +$deleteConfigData($configWriter, $configData, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/process_config_data.php b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/process_config_data.php new file mode 100644 index 0000000000000..2c672378fb832 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/process_config_data.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Config\Model\Config; +use Magento\Framework\App\Config\Storage\WriterInterface; + +$processConfigData = function (Config $config, array $data) { + foreach ($data as $key => $value) { + $config->setDataByPath($key, $value); + $config->save(); + } +}; + +$deleteConfigData = function (WriterInterface $writer, array $configData, string $scope, int $scopeId) { + foreach ($configData as $path) { + $writer->delete($path, $scope, $scopeId); + } +}; diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/store_payment_configuration.php b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/store_payment_configuration.php new file mode 100644 index 0000000000000..e54349e118bb4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/store_payment_configuration.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Config\Model\Config; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\TestFramework\Helper\Bootstrap; + +// save payment configuration per store +require __DIR__ . '/process_config_data.php'; +require __DIR__ . '/../../Store/_files/store.php'; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var EncryptorInterface $encryptor */ +$encryptor = $objectManager->get(EncryptorInterface::class); + +$storeConfigData = [ + 'payment/payflowpro/partner' => 'store_partner', + 'payment/payflowpro/vendor' => 'store_vendor', + 'payment/payflowpro/user' => $encryptor->encrypt('store_user'), + 'payment/payflowpro/pwd' => $encryptor->encrypt('store_pwd'), +]; +/** @var Config $storeConfig */ +$storeConfig = $objectManager->create(Config::class); +$storeConfig->setScope(ScopeInterface::SCOPE_STORES); +$storeConfig->setStore('test'); +$processConfigData($storeConfig, $storeConfigData); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/store_payment_configuration_rollback.php b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/store_payment_configuration_rollback.php new file mode 100644 index 0000000000000..08fe9ca7049f2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/store_payment_configuration_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/process_config_data.php'; + +$objectManager = Bootstrap::getObjectManager(); + +$configData = [ + 'payment/payflowpro/partner', + 'payment/payflowpro/vendor', + 'payment/payflowpro/user', + 'payment/payflowpro/pwd', +]; +/** @var WriterInterface $configWriter */ +$configWriter = $objectManager->get(WriterInterface::class); + +/** @var StoreRepositoryInterface $storeRepository */ +$storeRepository = $objectManager->get(StoreRepositoryInterface::class); +$store = $storeRepository->get('test'); +$deleteConfigData($configWriter, $configData, ScopeInterface::SCOPE_STORES, (int)$store->getId()); +require __DIR__ . '/../../Store/_files/store_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/website_payment_configuration.php b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/website_payment_configuration.php new file mode 100644 index 0000000000000..90359ccf4fe1c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/website_payment_configuration.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Config\Model\Config; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\TestFramework\Helper\Bootstrap; + +// save payment website config data +require __DIR__ . '/../../Store/_files/second_website_with_two_stores.php'; +require __DIR__ . '/process_config_data.php'; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var EncryptorInterface $encryptor */ +$encryptor = $objectManager->get(EncryptorInterface::class); +$websiteConfigData = [ + 'payment/payflowpro/partner' => 'website_partner', + 'payment/payflowpro/vendor' => 'website_vendor', + 'payment/payflowpro/user' => $encryptor->encrypt('website_user'), + 'payment/payflowpro/pwd' => $encryptor->encrypt('website_pwd'), +]; +/** @var Config $websiteConfig */ +$websiteConfig = $objectManager->create(Config::class); +$websiteConfig->setScope(ScopeInterface::SCOPE_WEBSITES); +$websiteConfig->setWebsite($websiteId); +$processConfigData($websiteConfig, $websiteConfigData); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/website_payment_configuration_rollback.php b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/website_payment_configuration_rollback.php new file mode 100644 index 0000000000000..1558d78e2504f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Fixtures/website_payment_configuration_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/process_config_data.php'; + +$objectManager = Bootstrap::getObjectManager(); + +$configData = [ + 'payment/payflowpro/partner', + 'payment/payflowpro/vendor', + 'payment/payflowpro/user', + 'payment/payflowpro/pwd', +]; +/** @var WriterInterface $configWriter */ +$configWriter = $objectManager->get(WriterInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$website = $websiteRepository->get('test'); +$deleteConfigData($configWriter, $configData, ScopeInterface::SCOPE_WEBSITES, (int)$website->getId()); + +require __DIR__ . '/../../Store/_files/second_website_with_two_stores_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php new file mode 100644 index 0000000000000..df6a1aae3fdb8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php @@ -0,0 +1,214 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Model\Payflow\Service\Request; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\DataObject; +use Magento\Framework\Math\Random; +use Magento\Paypal\Model\Payflow\Service\Gateway; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * @magentoAppIsolation enabled + */ +class SecureTokenTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var SecureToken + */ + private $service; + + /** + * @var Gateway|MockObject + */ + private $gateway; + + /** + * @var CartRepositoryInterface + */ + private $quoteRepository; + + /** + * @var Random|MockObject; + */ + private $mathRandom; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $this->gateway = $this->getMockBuilder(Gateway::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $this->objectManager->addSharedInstance($this->gateway, Gateway::class); + + $this->mathRandom = $this->getMockBuilder(Random::class) + ->getMock(); + + $this->service = $this->objectManager->create( + SecureToken::class, + [ + 'mathRandom' => $this->mathRandom + ] + ); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->objectManager->removeSharedInstance(Gateway::class); + } + + /** + * Checks a case when secure token can be obtained with credentials for the default scope. + * + * @magentoDataFixture Magento/Paypal/_files/quote_payflowpro.php + * @magentoDataFixture Magento/Paypal/Fixtures/default_payment_configuration.php + * @magentoAppArea adminhtml + */ + public function testRequestToken() + { + $quote = $this->getQuote('100000015'); + $quote->setStoreId(null); + $this->execute($quote, 'def_partner', 'def_vendor', 'def_user', 'def_pwd'); + } + + /** + * Checks a case when secure token can be obtained with credentials specified per store. + * + * @magentoDataFixture Magento/Paypal/_files/quote_payflowpro.php + * @magentoDataFixture Magento/Paypal/Fixtures/store_payment_configuration.php + * @magentoAppArea adminhtml + */ + public function testRequestTokenWithStoreConfiguration() + { + $quote = $this->getQuote('100000015'); + $store = $this->getStore('test'); + $quote->setStoreId($store->getId()); + $this->execute($quote, 'store_partner', 'store_vendor', 'store_user', 'store_pwd'); + } + + /** + * Checks a case when secure token can be obtained with credentials specified per website. + * + * @magentoDataFixture Magento/Paypal/_files/quote_payflowpro.php + * @magentoDataFixture Magento/Paypal/Fixtures/website_payment_configuration.php + * @magentoAppArea adminhtml + */ + public function testRequestTokenWithWebsiteConfiguration() + { + $quote = $this->getQuote('100000015'); + $store = $this->getStore('fixture_second_store'); + $quote->setStoreId($store->getId()); + $this->execute($quote, 'website_partner', 'website_vendor', 'website_user', 'website_pwd'); + } + + /** + * Retrieves secure token and perform test assertions. + * + * @param Quote $quote + * @param string $expPartner + * @param string $expVendor + * @param string $expUser + * @param string $expPwd + */ + private function execute(Quote $quote, string $expPartner, string $expVendor, string $expUser, string $expPwd) + { + $secureTokenId = '31f2a7c8d257c70b1c9eb9051b90e0'; + $token = '80IgSbabyj0CtBDWHZZeQN3'; + + $this->mathRandom->method('getUniqueHash') + ->willReturn($secureTokenId); + + $response = new DataObject([ + 'result' => '0', + 'respmsg' => 'Approved', + 'securetoken' => $token, + 'securetokenid' => $secureTokenId, + 'result_code' => '0', + ]); + $self = $this; + $this->gateway->method('postRequest') + /** @var DataObject $request */ + ->with(self::callback(function ($request) use ($self, $expPartner, $expVendor, $expUser, $expPwd) { + $self->performAssertion($expPartner, $request->getPartner(), '{Partner}'); + $self->performAssertion($expVendor, $request->getVendor(), '{Vendor}'); + $self->performAssertion($expUser, $request->getUser(), '{User}'); + $self->performAssertion($expPwd, $request->getPwd(), '{Password}'); + + return true; + })) + ->willReturn($response); + + $response = $this->service->requestToken($quote); + $this->performAssertion($token, $response->getData('securetoken'), '{Secure Token}'); + } + + /** + * Perform assertions test assertions. + * + * @param string $expected + * @param string $actual + * @param string $property + */ + private function performAssertion(string $expected, string $actual, string $property) + { + self::assertEquals($expected, $actual, "$property should match."); + } + + /** + * Loads quote by order increment id. + * + * @param string $orderIncrementId + * @return Quote + */ + private function getQuote(string $orderIncrementId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $orderIncrementId) + ->create(); + + $items = $this->quoteRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Loads store by provided code. + * + * @param string $code + * @return StoreInterface + */ + private function getStore(string $code): StoreInterface + { + /** @var StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->get(StoreRepositoryInterface::class); + return $storeRepository->get($code); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/fixed_discount_rollback.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/fixed_discount_rollback.php new file mode 100644 index 0000000000000..55ce3be29f7c7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/fixed_discount_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\ObjectManagerInterface; +use Magento\SalesRule\Api\Data\RuleInterface; +use Magento\SalesRule\Api\RuleRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var RuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->get(RuleRepositoryInterface::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->addFilter('name', '10$ discount')->create(); +$items = $ruleRepository->getList($searchCriteria)->getItems(); + +/** @var RuleInterface $item */ +foreach ($items as $item) { + $ruleRepository->deleteById($item->getRuleId()); +} diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro.php index 9b7e296d12c7d..c95f9df151608 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro.php @@ -5,6 +5,7 @@ */ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address; @@ -55,15 +56,16 @@ ->setName('Simple ' . $i) ->setSku('simple' . $i) ->setAttributeSetId(4) - ->setStockData( - [ - 'qty' => 10, - 'is_in_stock' => 10, - 'manage_stock' => 1, - ] - ) ->setPrice(5.69 + $i * 2) ->setWeight(1); + + /** @var StockItemInterface $stockItem */ + $stockItem = $objectManager->create(StockItemInterface::class); + $stockItem->setQty(10) + ->setIsInStock(true); + $extensionAttributes = $product->getExtensionAttributes(); + $extensionAttributes->setStockItem($stockItem); + $item = $productRepository->save($product); $quote->addProduct($item, $i); } diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php new file mode 100644 index 0000000000000..42c93f4a4c1e7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\ObjectManagerInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require 'fixed_discount_rollback.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var SearchCriteriaBuilder $productSearchCriteriaBuilder */ +$productSearchCriteriaBuilder = $objectManager->create(SearchCriteriaBuilder::class); +$searchCriteria = $productSearchCriteriaBuilder->addFilter('sku', ['simple1', 'simple2', 'simple3'], 'in') + ->create(); +$productList = $productRepository->getList($searchCriteria) + ->getItems(); +if (!empty($productList)) { + foreach ($productList as $product) { + $productRepository->delete($product); + } +} + +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->create(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', '100000015')->create(); +$items = $quoteRepository->getList($searchCriteria)->getItems(); + +if (!empty($items)) { + $quote = array_pop($items); + $quoteRepository->delete($quote); +} From d1446bf8de1e9f0c622e9f388bbc22c10a34ce27 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 29 May 2018 15:15:49 +0300 Subject: [PATCH 226/333] MAGETWO-89407: [2.3] PayPal Payments Pro settings gets from wrong store --- .../Magento/Paypal/_files/quote_payflowpro_rollback.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php index 42c93f4a4c1e7..6eded10e5c555 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php @@ -22,8 +22,12 @@ $productSearchCriteriaBuilder = $objectManager->create(SearchCriteriaBuilder::class); $searchCriteria = $productSearchCriteriaBuilder->addFilter('sku', ['simple1', 'simple2', 'simple3'], 'in') ->create(); -$productList = $productRepository->getList($searchCriteria) - ->getItems(); +$productList = $productRepository->getList($searchCriteria)->getItems(); + +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + if (!empty($productList)) { foreach ($productList as $product) { $productRepository->delete($product); From 769352fef70f3b0ce8c8c96ea79bfe0c6cb5a27a Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Tue, 29 May 2018 15:54:04 +0300 Subject: [PATCH 227/333] MAGETWO-92134: [2.2.x] Payment Failed Email is not generated --- .../Magento/Authorizenet/Model/Directpost.php | 69 +++- .../Test/Unit/Model/DirectpostTest.php | 51 ++- app/code/Magento/Checkout/Helper/Data.php | 137 +------- .../Checkout/Test/Unit/Helper/DataTest.php | 228 ++++---------- .../Express/AbstractExpress/PlaceOrder.php | 18 +- .../Magento/Paypal/Controller/Payflow.php | 19 +- .../Controller/Transparent/Response.php | 21 +- app/code/Magento/Paypal/Model/Payflowpro.php | 5 +- .../Unit/Controller/Payflow/ReturnUrlTest.php | 19 ++ .../Controller/Transparent/ResponseTest.php | 37 ++- .../Sales/Api/PaymentFailuresInterface.php | 28 ++ .../Sales/Model/Service/OrderService.php | 15 +- .../Model/Service/PaymentFailuresService.php | 295 ++++++++++++++++++ app/code/Magento/Sales/etc/di.xml | 1 + .../Controller/Payflow/SilentPostTest.php | 4 +- .../Magento/Paypal/Controller/PayflowTest.php | 86 +++-- .../Paypal/Controller/PayflowadvancedTest.php | 84 +++-- .../Service/PaymentFailuresServiceTest.php | 98 ++++++ .../testsuite/Magento/Sales/_files/order.php | 94 +++--- .../quote_with_two_products_and_customer.php | 31 ++ 20 files changed, 931 insertions(+), 409 deletions(-) create mode 100644 app/code/Magento/Sales/Api/PaymentFailuresInterface.php create mode 100644 app/code/Magento/Sales/Model/Service/PaymentFailuresService.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_two_products_and_customer.php diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php index 0f10fd633cb5b..de567a8895f7e 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ b/app/code/Magento/Authorizenet/Model/Directpost.php @@ -5,10 +5,9 @@ */ namespace Magento\Authorizenet\Model; -use Magento\Framework\HTTP\ZendClientFactory; +use Magento\Framework\App\ObjectManager; use Magento\Payment\Model\Method\ConfigInterface; use Magento\Payment\Model\Method\TransparentInterface; -use Magento\Sales\Model\Order\Email\Sender\OrderSender; /** * Authorize.net DirectPost payment method model. @@ -102,7 +101,7 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra protected $response; /** - * @var OrderSender + * @var \Magento\Sales\Model\Order\Email\Sender\OrderSender */ protected $orderSender; @@ -123,6 +122,16 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra */ private $psrLogger; + /** + * @var \Magento\Sales\Api\PaymentFailuresInterface + */ + private $paymentFailures; + + /** + * @var \Magento\Sales\Model\Order + */ + private $order; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -141,11 +150,12 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra * @param \Magento\Sales\Model\OrderFactory $orderFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository - * @param OrderSender $orderSender + * @param \Magento\Sales\Model\Order\Email\Sender\OrderSender $orderSender * @param \Magento\Sales\Api\TransactionRepositoryInterface $transactionRepository * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -162,7 +172,7 @@ public function __construct( \Magento\Authorizenet\Model\Directpost\Request\Factory $requestFactory, \Magento\Authorizenet\Model\Directpost\Response\Factory $responseFactory, TransactionService $transactionService, - ZendClientFactory $httpClientFactory, + \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory, \Magento\Sales\Model\OrderFactory $orderFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, @@ -170,7 +180,8 @@ public function __construct( \Magento\Sales\Api\TransactionRepositoryInterface $transactionRepository, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null ) { $this->orderFactory = $orderFactory; $this->storeManager = $storeManager; @@ -179,6 +190,8 @@ public function __construct( $this->orderSender = $orderSender; $this->transactionRepository = $transactionRepository; $this->_code = static::METHOD_CODE; + $this->paymentFailures = $paymentFailures ? : ObjectManager::getInstance() + ->get(\Magento\Sales\Api\PaymentFailuresInterface::class); parent::__construct( $context, @@ -561,13 +574,10 @@ public function process(array $responseData) $this->validateResponse(); $response = $this->getResponse(); - //operate with order - $orderIncrementId = $response->getXInvoiceNum(); $responseText = $this->dataHelper->wrapGatewayError($response->getXResponseReasonText()); $isError = false; - if ($orderIncrementId) { - /* @var $order \Magento\Sales\Model\Order */ - $order = $this->orderFactory->create()->loadByIncrementId($orderIncrementId); + if ($this->getOrderIncrementId()) { + $order = $this->getOrderFromResponse(); //check payment method $payment = $order->getPayment(); if (!$payment || $payment->getMethod() != $this->getCode()) { @@ -632,9 +642,10 @@ public function checkResponseCode() return true; case self::RESPONSE_CODE_DECLINED: case self::RESPONSE_CODE_ERROR: - throw new \Magento\Framework\Exception\LocalizedException( - $this->dataHelper->wrapGatewayError($this->getResponse()->getXResponseReasonText()) - ); + $errorMessage = $this->dataHelper->wrapGatewayError($this->getResponse()->getXResponseReasonText()); + $order = $this->getOrderFromResponse(); + $this->paymentFailures->handle((int)$order->getQuoteId(), $errorMessage); + throw new \Magento\Framework\Exception\LocalizedException($errorMessage); default: throw new \Magento\Framework\Exception\LocalizedException( __('There was a payment authorization error.') @@ -988,12 +999,40 @@ protected function getTransactionResponse($transactionId) private function getPsrLogger() { if (null === $this->psrLogger) { - $this->psrLogger = \Magento\Framework\App\ObjectManager::getInstance() + $this->psrLogger = ObjectManager::getInstance() ->get(\Psr\Log\LoggerInterface::class); } return $this->psrLogger; } + /** + * Fetch order by increment id from response. + * + * @return \Magento\Sales\Model\Order + */ + private function getOrderFromResponse(): \Magento\Sales\Model\Order + { + if (!$this->order) { + $this->order = $this->orderFactory->create(); + + if ($incrementId = $this->getOrderIncrementId()) { + $this->order = $this->order->loadByIncrementId($incrementId); + } + } + + return $this->order; + } + + /** + * Fetch order increment id from response. + * + * @return string + */ + private function getOrderIncrementId(): string + { + return $this->getResponse()->getXInvoiceNum(); + } + /** * Checks if filter action is Report Only. Transactions that trigger this filter are processed as normal, * but are also reported in the Merchant Interface as triggering this filter. diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php index dbb6ac8333c14..95c67f67852da 100644 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php +++ b/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Authorizenet\Test\Unit\Model; +use Magento\Sales\Api\PaymentFailuresInterface; use Magento\Framework\Simplexml\Element; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Authorizenet\Model\Directpost; @@ -74,6 +75,14 @@ class DirectpostTest extends \PHPUnit\Framework\TestCase */ protected $requestFactory; + /** + * @var PaymentFailuresInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $paymentFailures; + + /** + * @inheritdoc + */ protected function setUp() { $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) @@ -104,6 +113,12 @@ protected function setUp() ->setMethods(['getTransactionDetails']) ->getMock(); + $this->paymentFailures = $this->getMockBuilder( + PaymentFailuresInterface::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->requestFactory = $this->getRequestFactoryMock(); $httpClientFactoryMock = $this->getHttpClientFactoryMock(); @@ -117,7 +132,8 @@ protected function setUp() 'responseFactory' => $this->responseFactoryMock, 'transactionRepository' => $this->transactionRepositoryMock, 'transactionService' => $this->transactionServiceMock, - 'httpClientFactory' => $httpClientFactoryMock + 'httpClientFactory' => $httpClientFactoryMock, + 'paymentFailures' => $this->paymentFailures, ] ); } @@ -313,12 +329,16 @@ public function checkResponseCodeSuccessDataProvider() } /** - * @param bool $responseCode + * Checks response failures behaviour. + * + * @param int $responseCode + * @param int $failuresHandlerCalls + * @return void * * @expectedException \Magento\Framework\Exception\LocalizedException * @dataProvider checkResponseCodeFailureDataProvider */ - public function testCheckResponseCodeFailure($responseCode) + public function testCheckResponseCodeFailure(int $responseCode, int $failuresHandlerCalls): void { $reasonText = 'reason text'; @@ -333,18 +353,35 @@ public function testCheckResponseCodeFailure($responseCode) ->with($reasonText) ->willReturn(__('Gateway error: %1', $reasonText)); + $orderMock = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + + $orderMock->expects($this->exactly($failuresHandlerCalls)) + ->method('getQuoteId') + ->willReturn(1); + + $this->paymentFailures->expects($this->exactly($failuresHandlerCalls)) + ->method('handle') + ->with(1); + + $reflection = new \ReflectionClass($this->directpost); + $order = $reflection->getProperty('order'); + $order->setAccessible(true); + $order->setValue($this->directpost, $orderMock); + $this->directpost->checkResponseCode(); } /** * @return array */ - public function checkResponseCodeFailureDataProvider() + public function checkResponseCodeFailureDataProvider(): array { return [ - ['responseCode' => Directpost::RESPONSE_CODE_DECLINED], - ['responseCode' => Directpost::RESPONSE_CODE_ERROR], - ['responseCode' => 999999] + ['responseCode' => Directpost::RESPONSE_CODE_DECLINED, 1], + ['responseCode' => Directpost::RESPONSE_CODE_ERROR, 1], + ['responseCode' => 999999, 0], ]; } diff --git a/app/code/Magento/Checkout/Helper/Data.php b/app/code/Magento/Checkout/Helper/Data.php index b3c2e17e5d678..636d4aaca21f0 100644 --- a/app/code/Magento/Checkout/Helper/Data.php +++ b/app/code/Magento/Checkout/Helper/Data.php @@ -9,6 +9,7 @@ use Magento\Quote\Model\Quote\Item\AbstractItem; use Magento\Store\Model\Store; use Magento\Store\Model\ScopeInterface; +use Magento\Sales\Api\PaymentFailuresInterface; /** * Checkout default helper @@ -52,6 +53,11 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper */ protected $priceCurrency; + /** + * @var PaymentFailuresInterface + */ + private $paymentFailures; + /** * @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -60,6 +66,7 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper * @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder * @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation * @param PriceCurrencyInterface $priceCurrency + * @param PaymentFailuresInterface|null $paymentFailures * @codeCoverageIgnore */ public function __construct( @@ -69,7 +76,8 @@ public function __construct( \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder, \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation, - PriceCurrencyInterface $priceCurrency + PriceCurrencyInterface $priceCurrency, + PaymentFailuresInterface $paymentFailures = null ) { $this->_storeManager = $storeManager; $this->_checkoutSession = $checkoutSession; @@ -77,6 +85,8 @@ public function __construct( $this->_transportBuilder = $transportBuilder; $this->inlineTranslation = $inlineTranslation; $this->priceCurrency = $priceCurrency; + $this->paymentFailures = $paymentFailures ? : \Magento\Framework\App\ObjectManager::getInstance() + ->get(PaymentFailuresInterface::class); parent::__construct($context); } @@ -202,126 +212,13 @@ public function getBaseSubtotalInclTax($item) * @param string $message * @param string $checkoutType * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function sendPaymentFailedEmail($checkout, $message, $checkoutType = 'onepage') - { - $this->inlineTranslation->suspend(); - - $template = $this->scopeConfig->getValue( - 'checkout/payment_failed/template', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $checkout->getStoreId() - ); - - $copyTo = $this->_getEmails('checkout/payment_failed/copy_to', $checkout->getStoreId()); - $copyMethod = $this->scopeConfig->getValue( - 'checkout/payment_failed/copy_method', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $checkout->getStoreId() - ); - $bcc = []; - if ($copyTo && $copyMethod == 'bcc') { - $bcc = $copyTo; - } - - $_receiver = $this->scopeConfig->getValue( - 'checkout/payment_failed/receiver', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $checkout->getStoreId() - ); - $sendTo = [ - [ - 'email' => $this->scopeConfig->getValue( - 'trans_email/ident_' . $_receiver . '/email', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $checkout->getStoreId() - ), - 'name' => $this->scopeConfig->getValue( - 'trans_email/ident_' . $_receiver . '/name', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $checkout->getStoreId() - ), - ], - ]; - - if ($copyTo && $copyMethod == 'copy') { - foreach ($copyTo as $email) { - $sendTo[] = ['email' => $email, 'name' => null]; - } - } - $shippingMethod = ''; - if ($shippingInfo = $checkout->getShippingAddress()->getShippingMethod()) { - $data = explode('_', $shippingInfo); - $shippingMethod = $data[0]; - } - - $paymentMethod = ''; - if ($paymentInfo = $checkout->getPayment()) { - $paymentMethod = $paymentInfo->getMethod(); - } - - $items = ''; - foreach ($checkout->getAllVisibleItems() as $_item) { - /* @var $_item \Magento\Quote\Model\Quote\Item */ - $items .= - $_item->getProduct()->getName() . ' x ' . $_item->getQty() . ' ' . $checkout->getStoreCurrencyCode() - . ' ' . $_item->getProduct()->getFinalPrice( - $_item->getQty() - ) . "\n"; - } - $total = $checkout->getStoreCurrencyCode() . ' ' . $checkout->getGrandTotal(); - - foreach ($sendTo as $recipient) { - $transport = $this->_transportBuilder->setTemplateIdentifier( - $template - )->setTemplateOptions( - [ - 'area' => \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE, - 'store' => Store::DEFAULT_STORE_ID - ] - )->setTemplateVars( - [ - 'reason' => $message, - 'checkoutType' => $checkoutType, - 'dateAndTime' => $this->_localeDate->formatDateTime( - new \DateTime(), - \IntlDateFormatter::MEDIUM, - \IntlDateFormatter::MEDIUM - ), - 'customer' => $checkout->getCustomerFirstname() . ' ' . $checkout->getCustomerLastname(), - 'customerEmail' => $checkout->getCustomerEmail(), - 'billingAddress' => $checkout->getBillingAddress(), - 'shippingAddress' => $checkout->getShippingAddress(), - 'shippingMethod' => $this->scopeConfig->getValue( - 'carriers/' . $shippingMethod . '/title', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ), - 'paymentMethod' => $this->scopeConfig->getValue( - 'payment/' . $paymentMethod . '/title', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ), - 'items' => nl2br($items), - 'total' => $total, - ] - )->setFrom( - $this->scopeConfig->getValue( - 'checkout/payment_failed/identity', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $checkout->getStoreId() - ) - )->addTo( - $recipient['email'], - $recipient['name'] - )->addBcc( - $bcc - )->getTransport(); - - $transport->sendMessage(); - } - - $this->inlineTranslation->resume(); + public function sendPaymentFailedEmail( + \Magento\Quote\Model\Quote $checkout, + string $message, + string $checkoutType = 'onepage' + ): \Magento\Checkout\Helper\Data { + $this->paymentFailures->handle((int)$checkout->getId(), $message, $checkoutType); return $this; } diff --git a/app/code/Magento/Checkout/Test/Unit/Helper/DataTest.php b/app/code/Magento/Checkout/Test/Unit/Helper/DataTest.php index c403156dc13e9..f6f9ff78c1cb2 100644 --- a/app/code/Magento/Checkout/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Helper/DataTest.php @@ -6,8 +6,7 @@ namespace Magento\Checkout\Test\Unit\Helper; -use \Magento\Checkout\Helper\Data; - +use Magento\Checkout\Helper\Data; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Model\ScopeInterface; @@ -39,23 +38,21 @@ class DataTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $_checkoutSession; + private $_checkoutSession; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $_scopeConfig; + private $_scopeConfig; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $_collectionFactory; + private $_eventManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @inheritdoc */ - protected $_eventManager; - protected function setUp() { $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -68,59 +65,57 @@ protected function setUp() $this->_scopeConfig = $context->getScopeConfig(); $this->_scopeConfig->expects($this->any()) ->method('getValue') - ->will( - $this->returnValueMap( + ->willReturnMap( + [ + [ + 'checkout/payment_failed/template', + ScopeInterface::SCOPE_STORE, + 8, + 'fixture_email_template_payment_failed', + ], + [ + 'checkout/payment_failed/receiver', + ScopeInterface::SCOPE_STORE, + 8, + 'sysadmin', + ], + [ + 'trans_email/ident_sysadmin/email', + ScopeInterface::SCOPE_STORE, + 8, + 'sysadmin@example.com', + ], + [ + 'trans_email/ident_sysadmin/name', + ScopeInterface::SCOPE_STORE, + 8, + 'System Administrator', + ], + [ + 'checkout/payment_failed/identity', + ScopeInterface::SCOPE_STORE, + 8, + 'noreply@example.com', + ], + [ + 'carriers/ground/title', + ScopeInterface::SCOPE_STORE, + null, + 'Ground Shipping', + ], [ - [ - 'checkout/payment_failed/template', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - 8, - 'fixture_email_template_payment_failed' - ], - [ - 'checkout/payment_failed/receiver', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - 8, - 'sysadmin' - ], - [ - 'trans_email/ident_sysadmin/email', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - 8, - 'sysadmin@example.com' - ], - [ - 'trans_email/ident_sysadmin/name', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - 8, - 'System Administrator' - ], - [ - 'checkout/payment_failed/identity', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - 8, - 'noreply@example.com' - ], - [ - 'carriers/ground/title', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - null, - 'Ground Shipping' - ], - [ - 'payment/fixture-payment-method/title', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - null, - 'Check Money Order' - ], - [ - 'checkout/options/onepage_checkout_enabled', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - null, - 'One Page Checkout' - ] - ] - ) + 'payment/fixture-payment-method/title', + ScopeInterface::SCOPE_STORE, + null, + 'Check Money Order', + ], + [ + 'checkout/options/onepage_checkout_enabled', + ScopeInterface::SCOPE_STORE, + null, + 'One Page Checkout', + ], + ] ); $this->_checkoutSession = $arguments['checkoutSession']; @@ -137,117 +132,16 @@ protected function setUp() /** * @return void - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testSendPaymentFailedEmail() { - $shippingAddress = new \Magento\Framework\DataObject(['shipping_method' => 'ground_transportation']); - $billingAddress = new \Magento\Framework\DataObject(['street' => 'Fixture St']); - - $this->_transportBuilder->expects( - $this->once() - )->method( - 'setTemplateOptions' - )->with( - [ - 'area' => \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE, - 'store' => \Magento\Store\Model\Store::DEFAULT_STORE_ID, - ] - )->will( - $this->returnSelf() - ); - - $this->_transportBuilder->expects( - $this->once() - )->method( - 'setTemplateIdentifier' - )->with( - 'fixture_email_template_payment_failed' - )->will( - $this->returnSelf() - ); + $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) + ->setMethods(['getId']) + ->disableOriginalConstructor() + ->getMock(); + $quoteMock->expects($this->any())->method('getId')->willReturn(1); - $this->_transportBuilder->expects( - $this->once() - )->method( - 'setFrom' - )->with( - 'noreply@example.com' - )->will( - $this->returnSelf() - ); - - $this->_transportBuilder->expects( - $this->once() - )->method( - 'addTo' - )->with( - 'sysadmin@example.com', - 'System Administrator' - )->will( - $this->returnSelf() - ); - - $this->_transportBuilder->expects( - $this->once() - )->method( - 'setTemplateVars' - )->with( - [ - 'reason' => 'test message', - 'checkoutType' => 'onepage', - 'dateAndTime' => 'Oct 02, 2013', - 'customer' => 'John Doe', - 'customerEmail' => 'john.doe@example.com', - 'billingAddress' => $billingAddress, - 'shippingAddress' => $shippingAddress, - 'shippingMethod' => 'Ground Shipping', - 'paymentMethod' => 'Check Money Order', - 'items' => "Product One x 2 USD 10<br />\nProduct Two x 3 USD 60<br />\n", - 'total' => 'USD 70' - ] - )->will( - $this->returnSelf() - ); - - $this->_transportBuilder->expects($this->once())->method('addBcc')->will($this->returnSelf()); - $this->_transportBuilder->expects( - $this->once() - )->method( - 'getTransport' - )->will( - $this->returnValue($this->createMock(\Magento\Framework\Mail\TransportInterface::class)) - ); - - $this->_translator->expects($this->at(1))->method('suspend'); - $this->_translator->expects($this->at(1))->method('resume'); - - $productOne = $this->createMock(\Magento\Catalog\Model\Product::class); - $productOne->expects($this->once())->method('getName')->will($this->returnValue('Product One')); - $productOne->expects($this->once())->method('getFinalPrice')->with(2)->will($this->returnValue(10)); - - $productTwo = $this->createMock(\Magento\Catalog\Model\Product::class); - $productTwo->expects($this->once())->method('getName')->will($this->returnValue('Product Two')); - $productTwo->expects($this->once())->method('getFinalPrice')->with(3)->will($this->returnValue(60)); - - $quote = new \Magento\Framework\DataObject( - [ - 'store_id' => 8, - 'store_currency_code' => 'USD', - 'grand_total' => 70, - 'customer_firstname' => 'John', - 'customer_lastname' => 'Doe', - 'customer_email' => 'john.doe@example.com', - 'billing_address' => $billingAddress, - 'shipping_address' => $shippingAddress, - 'payment' => new \Magento\Framework\DataObject(['method' => 'fixture-payment-method']), - 'all_visible_items' => [ - new \Magento\Framework\DataObject(['product' => $productOne, 'qty' => 2]), - new \Magento\Framework\DataObject(['product' => $productTwo, 'qty' => 3]) - ] - ] - ); - $this->assertSame($this->_helper, $this->_helper->sendPaymentFailedEmail($quote, 'test message')); + $this->assertSame($this->_helper, $this->_helper->sendPaymentFailedEmail($quoteMock, 'test message')); } /** diff --git a/app/code/Magento/Paypal/Controller/Express/AbstractExpress/PlaceOrder.php b/app/code/Magento/Paypal/Controller/Express/AbstractExpress/PlaceOrder.php index f0fce97da512a..055af4162d5f3 100644 --- a/app/code/Magento/Paypal/Controller/Express/AbstractExpress/PlaceOrder.php +++ b/app/code/Magento/Paypal/Controller/Express/AbstractExpress/PlaceOrder.php @@ -21,6 +21,11 @@ class PlaceOrder extends \Magento\Paypal\Controller\Express\AbstractExpress */ protected $agreementsValidator; + /** + * @var \Magento\Sales\Api\PaymentFailuresInterface + */ + private $paymentFailures; + /** * @param \Magento\Framework\App\Action\Context $context * @param \Magento\Customer\Model\Session $customerSession @@ -31,6 +36,8 @@ class PlaceOrder extends \Magento\Paypal\Controller\Express\AbstractExpress * @param \Magento\Framework\Url\Helper\Data $urlHelper * @param \Magento\Customer\Model\Url $customerUrl * @param \Magento\Checkout\Api\AgreementsValidatorInterface $agreementValidator + * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\App\Action\Context $context, @@ -41,9 +48,9 @@ public function __construct( \Magento\Framework\Session\Generic $paypalSession, \Magento\Framework\Url\Helper\Data $urlHelper, \Magento\Customer\Model\Url $customerUrl, - \Magento\Checkout\Api\AgreementsValidatorInterface $agreementValidator + \Magento\Checkout\Api\AgreementsValidatorInterface $agreementValidator, + \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null ) { - $this->agreementsValidator = $agreementValidator; parent::__construct( $context, $customerSession, @@ -54,6 +61,11 @@ public function __construct( $urlHelper, $customerUrl ); + + $this->agreementsValidator = $agreementValidator; + $this->paymentFailures = $paymentFailures ? : $this->_objectManager->get( + \Magento\Sales\Api\PaymentFailuresInterface::class + ); } /** @@ -148,6 +160,8 @@ private function processException(\Exception $exception, string $message): void */ protected function _processPaypalApiError($exception) { + $this->paymentFailures->handle((int)$this->_getCheckoutSession()->getQuoteId(), $exception->getMessage()); + switch ($exception->getCode()) { case ApiProcessableException::API_MAX_PAYMENT_ATTEMPTS_EXCEEDED: case ApiProcessableException::API_TRANSACTION_EXPIRED: diff --git a/app/code/Magento/Paypal/Controller/Payflow.php b/app/code/Magento/Paypal/Controller/Payflow.php index ab21986bde3ba..78c0536e393ac 100644 --- a/app/code/Magento/Paypal/Controller/Payflow.php +++ b/app/code/Magento/Paypal/Controller/Payflow.php @@ -41,6 +41,11 @@ abstract class Payflow extends \Magento\Framework\App\Action\Action */ protected $_redirectBlockName = 'payflow.link.iframe'; + /** + * @var \Magento\Sales\Api\PaymentFailuresInterface + */ + private $paymentFailures; + /** * @param \Magento\Framework\App\Action\Context $context * @param \Magento\Checkout\Model\Session $checkoutSession @@ -48,6 +53,7 @@ abstract class Payflow extends \Magento\Framework\App\Action\Action * @param \Magento\Paypal\Model\PayflowlinkFactory $payflowModelFactory * @param \Magento\Paypal\Helper\Checkout $checkoutHelper * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures */ public function __construct( \Magento\Framework\App\Action\Context $context, @@ -55,14 +61,19 @@ public function __construct( \Magento\Sales\Model\OrderFactory $orderFactory, \Magento\Paypal\Model\PayflowlinkFactory $payflowModelFactory, \Magento\Paypal\Helper\Checkout $checkoutHelper, - \Psr\Log\LoggerInterface $logger + \Psr\Log\LoggerInterface $logger, + \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null ) { + parent::__construct($context); + $this->_checkoutSession = $checkoutSession; $this->_orderFactory = $orderFactory; $this->_logger = $logger; $this->_payflowModelFactory = $payflowModelFactory; $this->_checkoutHelper = $checkoutHelper; - parent::__construct($context); + $this->paymentFailures = $paymentFailures ?: $this->_objectManager->get( + \Magento\Sales\Api\PaymentFailuresInterface::class + ); } /** @@ -74,6 +85,10 @@ public function __construct( protected function _cancelPayment($errorMsg = '') { $errorMsg = trim(strip_tags($errorMsg)); + $order = $this->_checkoutSession->getLastRealOrder(); + if ($order->getId()) { + $this->paymentFailures->handle((int)$order->getQuoteId(), $errorMsg); + } $gotoSection = false; $this->_checkoutHelper->cancelCurrentOrder($errorMsg); diff --git a/app/code/Magento/Paypal/Controller/Transparent/Response.php b/app/code/Magento/Paypal/Controller/Transparent/Response.php index 23ac20ca8c87b..c54dd529588b9 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/Response.php +++ b/app/code/Magento/Paypal/Controller/Transparent/Response.php @@ -14,6 +14,8 @@ use Magento\Paypal\Model\Payflow\Service\Response\Transaction; use Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator; use Magento\Paypal\Model\Payflow\Transparent; +use Magento\Sales\Api\PaymentFailuresInterface; +use Magento\Framework\Session\Generic as Session; /** * Class Response @@ -47,6 +49,16 @@ class Response extends \Magento\Framework\App\Action\Action */ private $transparent; + /** + * @var PaymentFailuresInterface + */ + private $paymentFailures; + + /** + * @var Session + */ + private $sessionTransparent; + /** * Constructor * @@ -56,6 +68,8 @@ class Response extends \Magento\Framework\App\Action\Action * @param ResponseValidator $responseValidator * @param LayoutFactory $resultLayoutFactory * @param Transparent $transparent + * @param Session|null $sessionTransparent + * @param PaymentFailuresInterface|null $paymentFailures */ public function __construct( Context $context, @@ -63,7 +77,9 @@ public function __construct( Transaction $transaction, ResponseValidator $responseValidator, LayoutFactory $resultLayoutFactory, - Transparent $transparent + Transparent $transparent, + Session $sessionTransparent = null, + PaymentFailuresInterface $paymentFailures = null ) { parent::__construct($context); $this->coreRegistry = $coreRegistry; @@ -71,6 +87,8 @@ public function __construct( $this->responseValidator = $responseValidator; $this->resultLayoutFactory = $resultLayoutFactory; $this->transparent = $transparent; + $this->sessionTransparent = $sessionTransparent ?: $this->_objectManager->get(Session::class); + $this->paymentFailures = $paymentFailures ?: $this->_objectManager->get(PaymentFailuresInterface::class); } /** @@ -86,6 +104,7 @@ public function execute() } catch (LocalizedException $exception) { $parameters['error'] = true; $parameters['error_msg'] = $exception->getMessage(); + $this->paymentFailures->handle((int)$this->sessionTransparent->getQuoteId(), $parameters['error_msg']); } $this->coreRegistry->register(Iframe::REGISTRY_KEY, $parameters); diff --git a/app/code/Magento/Paypal/Model/Payflowpro.php b/app/code/Magento/Paypal/Model/Payflowpro.php index 125aa0f6e65a7..16ba4efc90d6a 100644 --- a/app/code/Magento/Paypal/Model/Payflowpro.php +++ b/app/code/Magento/Paypal/Model/Payflowpro.php @@ -648,6 +648,7 @@ public function buildBasicRequest() * @param DataObject $response * @return void * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Payment\Gateway\Command\CommandException * @throws \Magento\Framework\Exception\State\InvalidTransitionException */ public function processErrors(DataObject $response) @@ -659,9 +660,9 @@ public function processErrors(DataObject $response) } elseif ($response->getResultCode() != self::RESPONSE_CODE_APPROVED && $response->getResultCode() != self::RESPONSE_CODE_FRAUDSERVICE_FILTER ) { - throw new \Magento\Framework\Exception\LocalizedException(__($response->getRespmsg())); + throw new \Magento\Payment\Gateway\Command\CommandException(__($response->getRespmsg())); } elseif ($response->getOrigresult() == self::RESPONSE_CODE_DECLINED_BY_FILTER) { - throw new \Magento\Framework\Exception\LocalizedException(__($response->getRespmsg())); + throw new \Magento\Payment\Gateway\Command\CommandException(__($response->getRespmsg())); } } diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php index e25864bbc2f3c..bd4da25cb84d0 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Paypal\Test\Unit\Controller\Payflow; +use Magento\Sales\Api\PaymentFailuresInterface; use Magento\Checkout\Block\Onepage\Success; use Magento\Checkout\Model\Session; use Magento\Framework\App\Action\Context; @@ -90,6 +91,11 @@ class ReturnUrlTest extends \PHPUnit\Framework\TestCase */ private $objectManager; + /** + * @var PaymentFailuresInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $paymentFailures; + /** * @inheritdoc */ @@ -138,6 +144,17 @@ protected function setUp() ->setMethods(['getLastRealOrderId', 'getLastRealOrder', 'restoreQuote']) ->getMock(); + $this->quote = $this->getMockBuilder(CartInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->context->expects($this->any())->method('getView')->willReturn($this->view); + $this->context->expects($this->any())->method('getRequest')->willReturn($this->request); + + $this->paymentFailures = $this->getMockBuilder(PaymentFailuresInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->context->method('getView') ->willReturn($this->view); $this->context->method('getRequest') @@ -148,6 +165,7 @@ protected function setUp() 'checkoutSession' => $this->checkoutSession, 'orderFactory' => $this->orderFactory, 'checkoutHelper' => $this->checkoutHelper, + 'paymentFailures' => $this->paymentFailures, ]); } @@ -321,6 +339,7 @@ public function testCheckAdvancedAcceptingByPaymentMethod() 'checkoutSession' => $this->checkoutSession, 'orderFactory' => $this->orderFactory, 'checkoutHelper' => $this->checkoutHelper, + 'paymentFailures' => $this->paymentFailures, ]); $returnUrl->execute(); diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/ResponseTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/ResponseTest.php index a10d103860c65..acefebb779200 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/ResponseTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/ResponseTest.php @@ -8,16 +8,16 @@ use Magento\Framework\App\Action\Context; use Magento\Framework\App\RequestInterface; use Magento\Framework\Registry; +use Magento\Framework\Session\Generic as Session; use Magento\Framework\View\Result\Layout; use Magento\Framework\View\Result\LayoutFactory; use Magento\Paypal\Controller\Transparent\Response; use Magento\Paypal\Model\Payflow\Service\Response\Transaction; use Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator; use Magento\Paypal\Model\Payflow\Transparent; +use Magento\Sales\Api\PaymentFailuresInterface; /** - * Class ResponseTest - * * Test for class \Magento\Paypal\Controller\Transparent\Response * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -53,6 +53,19 @@ class ResponseTest extends \PHPUnit\Framework\TestCase */ private $payflowFacade; + /** + * @var PaymentFailuresInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $paymentFailures; + + /** + * @var Session|\PHPUnit_Framework_MockObject_MockObject + */ + private $sessionTransparent; + + /** + * @inheritdoc + */ protected function setUp() { $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) @@ -97,6 +110,14 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods([]) ->getMock(); + $this->paymentFailures = $this->getMockBuilder(PaymentFailuresInterface::class) + ->disableOriginalConstructor() + ->setMethods(['handle']) + ->getMock(); + $this->sessionTransparent = $this->getMockBuilder(Session::class) + ->disableOriginalConstructor() + ->setMethods(['getQuoteId']) + ->getMock(); $this->object = new Response( $this->contextMock, @@ -104,7 +125,9 @@ protected function setUp() $this->transactionMock, $this->responseValidatorMock, $this->resultLayoutFactoryMock, - $this->payflowFacade + $this->payflowFacade, + $this->sessionTransparent, + $this->paymentFailures ); } @@ -131,6 +154,8 @@ public function testExecute() $this->resultLayoutMock->expects($this->once()) ->method('getLayout') ->willReturn($this->getLayoutMock()); + $this->paymentFailures->expects($this->never()) + ->method('handle'); $this->assertInstanceOf(\Magento\Framework\Controller\ResultInterface::class, $this->object->execute()); } @@ -156,6 +181,12 @@ public function testExecuteWithException() $this->resultLayoutMock->expects($this->once()) ->method('getLayout') ->willReturn($this->getLayoutMock()); + $this->sessionTransparent->method('getQuoteId') + ->willReturn(1); + $this->paymentFailures->expects($this->once()) + ->method('handle') + ->with(1) + ->willReturnSelf(); $this->assertInstanceOf(\Magento\Framework\Controller\ResultInterface::class, $this->object->execute()); } diff --git a/app/code/Magento/Sales/Api/PaymentFailuresInterface.php b/app/code/Magento/Sales/Api/PaymentFailuresInterface.php new file mode 100644 index 0000000000000..485ff1ffbe248 --- /dev/null +++ b/app/code/Magento/Sales/Api/PaymentFailuresInterface.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Api; + +/** + * Interface for managing payment gateway failures. + */ +interface PaymentFailuresInterface +{ + /** + * Handles payment gateway failures. + * + * @param int $cartId + * @param string $errorMessage + * @param string $checkoutType + * @return PaymentFailuresInterface + */ + public function handle( + int $cartId, + string $errorMessage, + string $checkoutType = 'onepage' + ): PaymentFailuresInterface; +} diff --git a/app/code/Magento/Sales/Model/Service/OrderService.php b/app/code/Magento/Sales/Model/Service/OrderService.php index 1eb3fad11278f..e4a71f028cc82 100644 --- a/app/code/Magento/Sales/Model/Service/OrderService.php +++ b/app/code/Magento/Sales/Model/Service/OrderService.php @@ -6,6 +6,7 @@ namespace Magento\Sales\Model\Service; use Magento\Sales\Api\OrderManagementInterface; +use Magento\Payment\Gateway\Command\CommandException; /** * Class OrderService @@ -49,6 +50,11 @@ class OrderService implements OrderManagementInterface */ protected $orderCommentSender; + /** + * @var \Magento\Sales\Api\PaymentFailuresInterface + */ + private $paymentFailures; + /** * Constructor * @@ -59,6 +65,7 @@ class OrderService implements OrderManagementInterface * @param \Magento\Sales\Model\OrderNotifier $notifier * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender + * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures */ public function __construct( \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, @@ -67,7 +74,8 @@ public function __construct( \Magento\Framework\Api\FilterBuilder $filterBuilder, \Magento\Sales\Model\OrderNotifier $notifier, \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender + \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender, + \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null ) { $this->orderRepository = $orderRepository; $this->historyRepository = $historyRepository; @@ -76,6 +84,8 @@ public function __construct( $this->notifier = $notifier; $this->eventManager = $eventManager; $this->orderCommentSender = $orderCommentSender; + $this->paymentFailures = $paymentFailures ? : \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Sales\Api\PaymentFailuresInterface::class); } /** @@ -192,6 +202,9 @@ public function place(\Magento\Sales\Api\Data\OrderInterface $order) return $this->orderRepository->save($order); //commit } catch (\Exception $e) { + if ($e instanceof CommandException) { + $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); + } throw $e; //rollback; } diff --git a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php new file mode 100644 index 0000000000000..2e8da0dadd5b1 --- /dev/null +++ b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php @@ -0,0 +1,295 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\Service; + +use Magento\Backend\App\Area\FrontNameResolver; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\Translate\Inline\StateInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface as Quote; +use Magento\Sales\Api\PaymentFailuresInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; + +/** + * Service is responsible for handling failed payment transactions. + * + * It depends on Stores > Configuration > Sales > Checkout > Payment Failed Emails configuration. + */ +class PaymentFailuresService implements PaymentFailuresInterface +{ + /** + * Store config + * + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var StateInterface + */ + private $inlineTranslation; + + /** + * @var TransportBuilder + */ + private $transportBuilder; + + /** + * @var TimezoneInterface + */ + private $localeDate; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @param ScopeConfigInterface $scopeConfig + * @param StateInterface $inlineTranslation + * @param TransportBuilder $transportBuilder + * @param TimezoneInterface $localeDate + * @param CartRepositoryInterface $cartRepository + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + StateInterface $inlineTranslation, + TransportBuilder $transportBuilder, + TimezoneInterface $localeDate, + CartRepositoryInterface $cartRepository + ) { + $this->scopeConfig = $scopeConfig; + $this->inlineTranslation = $inlineTranslation; + $this->transportBuilder = $transportBuilder; + $this->localeDate = $localeDate; + $this->cartRepository = $cartRepository; + } + + /** + * Sends an email about failed transaction. + * + * @param int $cartId + * @param string $message + * @param string $checkoutType + * @return PaymentFailuresInterface + */ + public function handle( + int $cartId, + string $message, + string $checkoutType = 'onepage' + ): PaymentFailuresInterface { + $this->inlineTranslation->suspend(); + $quote = $this->cartRepository->get($cartId); + + $template = $this->getConfigValue('checkout/payment_failed/template', $quote); + $receiver = $this->getConfigValue('checkout/payment_failed/receiver', $quote); + $sendTo = [ + [ + 'email' => $this->getConfigValue('trans_email/ident_' . $receiver . '/email', $quote), + 'name' => $this->getConfigValue('trans_email/ident_' . $receiver . '/name', $quote), + ], + ]; + + $copyMethod = $this->getConfigValue('checkout/payment_failed/copy_method', $quote); + $copyTo = $this->getConfigEmails($quote); + + $bcc = []; + if (!empty($copyTo)) { + switch ($copyMethod) { + case 'bcc': + $bcc = $copyTo; + break; + case 'copy': + foreach ($copyTo as $email) { + $sendTo[] = ['email' => $email, 'name' => null]; + } + break; + } + } + + foreach ($sendTo as $recipient) { + $transport = $this->transportBuilder + ->setTemplateIdentifier($template) + ->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']) + ->addBcc($bcc) + ->getTransport(); + + $transport->sendMessage(); + } + + $this->inlineTranslation->resume(); + + return $this; + } + + /** + * Returns mail template variables. + * + * @param Quote $quote + * @param string $message + * @param string $checkoutType + * @return array + */ + private function getTemplateVars(Quote $quote, string $message, string $checkoutType): array + { + return [ + 'reason' => $message, + 'checkoutType' => $checkoutType, + 'dateAndTime' => $this->getLocaleDate(), + 'customer' => $this->getCustomerName($quote), + 'customerEmail' => $quote->getBillingAddress()->getEmail(), + 'billingAddress' => $quote->getBillingAddress(), + 'shippingAddress' => $quote->getShippingAddress(), + 'shippingMethod' => $this->getConfigValue( + 'carriers/' . $this->getShippingMethod($quote) . '/title', + $quote + ), + 'paymentMethod' => $this->getConfigValue( + 'payment/' . $this->getPaymentMethod($quote) . '/title', + $quote + ), + 'items' => implode('<br />', $this->getQuoteItems($quote)), + 'total' => $quote->getCurrency()->getStoreCurrencyCode() . ' ' . $quote->getGrandTotal(), + ]; + } + + /** + * Returns scope config value by config path. + * + * @param string $configPath + * @param Quote $quote + * @return mixed + */ + private function getConfigValue(string $configPath, Quote $quote) + { + return $this->scopeConfig->getValue( + $configPath, + ScopeInterface::SCOPE_STORE, + $quote->getStoreId() + ); + } + + /** + * Returns shipping method from quote. + * + * @param Quote $quote + * @return string + */ + private function getShippingMethod(Quote $quote) + { + $shippingMethod = ''; + if ($shippingInfo = $quote->getShippingAddress()->getShippingMethod()) { + $data = explode('_', $shippingInfo); + $shippingMethod = $data[0]; + } + + return $shippingMethod; + } + + /** + * Returns payment method title from quote. + * + * @param Quote $quote + * @return string + */ + private function getPaymentMethod(Quote $quote) + { + $paymentMethod = ''; + if ($paymentInfo = $quote->getPayment()) { + $paymentMethod = $paymentInfo->getMethod(); + } + + return $paymentMethod; + } + + /** + * Returns quote visible items. + * + * @param Quote $quote + * @return array + */ + private function getQuoteItems(Quote $quote): array + { + $items = []; + foreach ($quote->getAllVisibleItems() as $item) { + $itemData = $item->getProduct()->getName() . ' x ' . $item->getQty() . ' ' . + $quote->getCurrency()->getStoreCurrencyCode() . ' ' . + $item->getProduct()->getFinalPrice($item->getQty()); + $items[] = $itemData; + } + + return $items; + } + + /** + * Gets email values by configuration path. + * + * @param Quote $quote + * @return array|false + */ + private function getConfigEmails(Quote $quote) + { + $configData = $this->getConfigValue('checkout/payment_failed/copy_to', $quote); + if (!empty($configData)) { + return explode(',', $configData); + } + + return false; + } + + /** + * Returns sender identity. + * + * @param Quote $quote + * @return string + */ + private function getSendFrom(Quote $quote): string + { + return $this->getConfigValue('checkout/payment_failed/identity', $quote); + } + + /** + * Returns current locale date and time + * + * @return string + */ + private function getLocaleDate(): string + { + return $this->localeDate->formatDateTime( + new \DateTime(), + \IntlDateFormatter::MEDIUM, + \IntlDateFormatter::MEDIUM + ); + } + + /** + * Returns customer name. + * + * @param Quote $quote + * @return string + */ + private function getCustomerName(Quote $quote): string + { + $customer = __('Guest'); + if (!$quote->getCustomerIsGuest()) { + $customer = $quote->getCustomer()->getFirstname() . ' ' . + $quote->getCustomer()->getLastname(); + } + + return $customer; + } +} diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index ce2948983edbe..ac25cdab22f56 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -65,6 +65,7 @@ <preference for="Magento\Sales\Api\OrderRepositoryInterface" type="Magento\Sales\Model\OrderRepository"/> <preference for="Magento\Sales\Api\OrderManagementInterface" type="Magento\Sales\Model\Service\OrderService"/> <preference for="Magento\Sales\Api\OrderStatusHistoryRepositoryInterface" type="Magento\Sales\Model\Order\Status\HistoryRepository"/> + <preference for="Magento\Sales\Api\PaymentFailuresInterface" type="Magento\Sales\Model\Service\PaymentFailuresService" /> <preference for="Magento\Sales\Api\ShipmentCommentRepositoryInterface" type="Magento\Sales\Model\Order\Shipment\CommentRepository"/> <preference for="Magento\Sales\Api\ShipmentItemRepositoryInterface" type="Magento\Sales\Model\Order\Shipment\ItemRepository"/> <preference for="Magento\Sales\Api\ShipmentRepositoryInterface" type="Magento\Sales\Model\Order\ShipmentRepository"/> diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Payflow/SilentPostTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Payflow/SilentPostTest.php index b4904c10dc22e..2bebb5bd95bb2 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Payflow/SilentPostTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Payflow/SilentPostTest.php @@ -8,8 +8,8 @@ use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\DataObject; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Logger\Monolog; +use Magento\Payment\Gateway\Command\CommandException; use Magento\Paypal\Model\Payflow\Service\Gateway; use Magento\Paypal\Model\Payflowlink; use Magento\Sales\Api\Data\OrderInterface; @@ -116,7 +116,7 @@ public function testFraudulentNotification() ->getMock(); $this->_objectManager->addSharedInstance($logger, Monolog::class); - $exception = new LocalizedException(__('Response message from PayPal gateway')); + $exception = new CommandException(__('Response message from PayPal gateway')); $logger->expects(self::once()) ->method('critical') ->with(self::equalTo($exception)); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowTest.php index 987f14c8c87e9..367f68ce25872 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowTest.php @@ -5,32 +5,74 @@ */ namespace Magento\Paypal\Controller; +use Magento\Checkout\Model\Session; +use Magento\Paypal\Model\Config; +use Magento\Quote\Model\Quote; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + /** * @magentoDataFixture Magento/Sales/_files/order.php */ class PayflowTest extends \Magento\TestFramework\TestCase\AbstractController { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var CartRepositoryInterface + */ + private $quoteRepository; + + /** + * @var OrderInterface + */ + private $order; + + /** + * @inheritdoc + */ protected function setUp() { parent::setUp(); - $order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Sales\Model\Order::class); - $order->load('100000001', 'increment_id'); - $order->getPayment()->setMethod(\Magento\Paypal\Model\Config::METHOD_PAYFLOWLINK); + /** @var FilterBuilder $filterBuilder */ + $filterBuilder = $this->_objectManager->get(FilterBuilder::class); + $filters = [ + $filterBuilder->setField(OrderInterface::INCREMENT_ID) + ->setValue('100000001') + ->create(), + ]; - $quote = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Quote\Model\Quote::class - )->setStoreId( - $order->getStoreId() - )->save(); + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilters($filters) + ->create(); - $order->setQuoteId($quote->getId()); - $order->save(); + $this->orderRepository = $this->_objectManager->get(OrderRepositoryInterface::class); + $orders = $this->orderRepository->getList($searchCriteria) + ->getItems(); - $session = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Checkout\Model\Session::class - ); - $session->setLastRealOrderId($order->getRealOrderId())->setLastQuoteId($order->getQuoteId()); + /** @var OrderInterface $order */ + $this->order = array_pop($orders); + $this->order->getPayment()->setMethod(Config::METHOD_PAYFLOWLINK); + + /** @var $quote \Magento\Quote\Model\Quote */ + $quote = $this->_objectManager->create(Quote::class)->setStoreid($this->order->getStoreid()); + + $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); + $this->quoteRepository->save($quote); + + $this->order->setQuoteId($quote->getId()); + $this->orderRepository->save($this->order); + + $session = $this->_objectManager->get(Session::class); + $session->setLastRealOrderId($this->order->getRealOrderId())->setLastQuoteId($this->order->getQuoteId()); } public function testCancelPaymentActionIsContentGenerated() @@ -65,22 +107,24 @@ public function testFormActionIsContentGenerated() } /** - * @magentoDataFixture Magento/Sales/_files/order.php * @magentoConfigFixture current_store payment/paypal_payflow/active 1 * @magentoConfigFixture current_store paypal/general/business_account merchant_2012050718_biz@example.com + * @return void */ public function testCancelAction() { - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class); - $session = $this->_objectManager->get(\Magento\Checkout\Model\Session::class); + $orderId = $this->order->getEntityId(); + /** @var \Magento\Sales\Model\Order $order */ + $order = $this->orderRepository->get($orderId); + /** @var $quote \Magento\Quote\Model\Quote */ + $quote = $this->quoteRepository->get($order->getQuoteId()); - $quote = $this->_objectManager->create(\Magento\Quote\Model\Quote::class); - $quote->load('test02', 'reserved_order_id'); - $order->load('100000001', 'increment_id')->setQuoteId($quote->getId())->save(); + $session = $this->_objectManager->get(Session::class); $session->setQuoteId($quote->getId()); $session->setPaypalStandardQuoteId($quote->getId())->setLastRealOrderId('100000001'); $this->dispatch('paypal/payflow/cancelpayment'); - $order->load('100000001', 'increment_id'); + + $order = $this->_objectManager->create(OrderRepositoryInterface::class)->get($orderId); $this->assertEquals('canceled', $order->getState()); $this->assertEquals($session->getQuote()->getGrandTotal(), $quote->getGrandTotal()); $this->assertEquals($session->getQuote()->getItemsCount(), $quote->getItemsCount()); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowadvancedTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowadvancedTest.php index ae6053a4801b0..7996acc7ed3b0 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowadvancedTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowadvancedTest.php @@ -5,30 +5,75 @@ */ namespace Magento\Paypal\Controller; +use Magento\Checkout\Model\Session; +use Magento\Paypal\Model\Config; +use Magento\Quote\Model\Quote; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; + /** * @magentoDataFixture Magento/Sales/_files/order.php */ class PayflowadvancedTest extends \Magento\TestFramework\TestCase\AbstractController { + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var CartRepositoryInterface + */ + private $quoteRepository; + + /** + * @var OrderInterface + */ + private $order; + + /** + * @inheritdoc + */ protected function setUp() { parent::setUp(); - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class); - $order->load('100000001', 'increment_id'); - $order->getPayment()->setMethod(\Magento\Paypal\Model\Config::METHOD_PAYFLOWADVANCED); + /** @var FilterBuilder $filterBuilder */ + $filterBuilder = $this->_objectManager->get(FilterBuilder::class); + $filters = [ + $filterBuilder->setField(OrderInterface::INCREMENT_ID) + ->setValue('100000001') + ->create(), + ]; + + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilters($filters) + ->create(); + + $this->orderRepository = $this->_objectManager->get(OrderRepositoryInterface::class); + $orders = $this->orderRepository->getList($searchCriteria) + ->getItems(); + + /** @var OrderInterface $order */ + $this->order = array_pop($orders); + $this->order->getPayment()->setMethod(Config::METHOD_PAYFLOWLINK); + + /** @var $quote \Magento\Quote\Model\Quote */ + $quote = $this->_objectManager->create(Quote::class) + ->setStoreid($this->order->getStoreid()); - $quote = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Quote\Model\Quote::class - )->setStoreId( - $order->getStoreId() - )->save(); + $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); + $this->quoteRepository->save($quote); - $order->setQuoteId($quote->getId()); - $order->save(); + $this->order->setQuoteId($quote->getId()); + $this->orderRepository->save($this->order); - $session = $this->_objectManager->create(\Magento\Checkout\Model\Session::class); - $session->setLastRealOrderId($order->getRealOrderId())->setLastQuoteId($order->getQuoteId()); + $session = $this->_objectManager->get(Session::class); + $session->setLastRealOrderId($this->order->getRealOrderId())->setLastQuoteId($this->order->getQuoteId()); } public function testCancelPaymentActionIsContentGenerated() @@ -55,23 +100,24 @@ public function testFormActionIsContentGenerated() } /** - * @magentoDataFixture Magento/Sales/_files/order.php * @magentoConfigFixture current_store payment/paypal_payflow/active 1 * @magentoConfigFixture current_store paypal/general/business_account merchant_2012050718_biz@example.com + * @return void */ public function testCancelAction() { - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class); - $session = $this->_objectManager->get(\Magento\Checkout\Model\Session::class); + $orderId = $this->order->getEntityId(); + /** @var \Magento\Sales\Model\Order $order */ + $order = $this->orderRepository->get($orderId); + /** @var $quote \Magento\Quote\Model\Quote */ + $quote = $this->quoteRepository->get($order->getQuoteId()); - $quote = $this->_objectManager->create(\Magento\Quote\Model\Quote::class); - $quote->load('test02', 'reserved_order_id'); - $order->load('100000001', 'increment_id')->setQuoteId($quote->getId())->save(); + $session = $this->_objectManager->get(Session::class); $session->setQuoteId($quote->getId()); $session->setPaypalStandardQuoteId($quote->getId())->setLastRealOrderId('100000001'); $this->dispatch('paypal/payflow/cancelpayment'); - $order->load('100000001', 'increment_id'); + $order = $this->_objectManager->create(OrderRepositoryInterface::class)->get($orderId); $this->assertEquals('canceled', $order->getState()); $this->assertEquals($session->getQuote()->getGrandTotal(), $quote->getGrandTotal()); $this->assertEquals($session->getQuote()->getItemsCount(), $quote->getItemsCount()); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php new file mode 100644 index 0000000000000..1e334c9660d19 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\Service; + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Sales\Api\PaymentFailuresInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Tests \Magento\Sales\Api\PaymentFailuresInterface. + */ +class PaymentFailuresServiceTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var PaymentFailuresInterface + */ + private $paymentFailures; + + /** + * @var Quote + */ + private $quote; + + /** + * @var CartRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $cartRepositoryMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->quote = Bootstrap::getObjectManager()->create(Quote::class); + $this->cartRepositoryMock = $this->getMockBuilder(CartRepositoryInterface::class) + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMockForAbstractClass(); + + $this->paymentFailures = Bootstrap::getObjectManager()->create( + PaymentFailuresInterface::class, + [ + 'cartRepository' => $this->cartRepositoryMock, + ] + ); + } + + /** + * @magentoDataFixture Magento/Sales/_files/quote_with_two_products_and_customer.php + * @magentoConfigFixture current_store payment/payflowpro/title Some Title Of The Method + * @magentoConfigFixture current_store carriers/freeshipping/title Some Shipping Method + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @return void + */ + public function testHandlerWithCustomer() + { + $errorMessage = __('Transaction declined.'); + $checkoutType = 'custom_checkout'; + + $this->quote->load('test01', 'reserved_order_id'); + $this->cartRepositoryMock->method('get') + ->with($this->quote->getId()) + ->willReturn($this->quote); + + $this->paymentFailures->handle((int)$this->quote->getId(), $errorMessage->render()); + + $paymentReflection = new \ReflectionClass($this->paymentFailures); + $templateTimeMethod = $paymentReflection->getMethod('getLocaleDate'); + $templateTimeMethod->setAccessible(true); + + $templateVarsMethod = $paymentReflection->getMethod('getTemplateVars'); + $templateVarsMethod->setAccessible(true); + + $templateVars = $templateVarsMethod->invoke($this->paymentFailures, $this->quote, $errorMessage, $checkoutType); + $expectedVars = [ + 'reason' => $errorMessage, + 'checkoutType' => $checkoutType, + 'dateAndTime' => $templateTimeMethod->invoke($this->paymentFailures), + 'customer' => 'John Smith', + 'customerEmail' => 'aaa@aaa.com', + 'paymentMethod' => 'Some Title Of The Method', + 'shippingMethod' => 'Some Shipping Method', + 'items' => 'Simple Product x 2 USD 10<br />Custom Design Simple Product x 1 USD 10', + 'total' => 'USD 30.0000', + 'billingAddress' => $this->quote->getBillingAddress(), + 'shippingAddress' => $this->quote->getShippingAddress(), + ]; + + $this->assertEquals($expectedVars, $templateVars); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php index f1e2afc88b6f8..a1c5f8277762c 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php @@ -4,6 +4,13 @@ * See COPYING.txt for license details. */ +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Sales\Model\Order\Payment; +use Magento\Store\Model\StoreManagerInterface; + require 'default_rollback.php'; require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; /** @var \Magento\Catalog\Model\Product $product */ @@ -12,57 +19,50 @@ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); $billingAddress->setAddressType('billing'); $shippingAddress = clone $billingAddress; $shippingAddress->setId(null)->setAddressType('shipping'); -$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); -$payment->setMethod('checkmo'); -$payment->setAdditionalInformation('last_trans_id', '11122'); -$payment->setAdditionalInformation('metadata', [ - 'type' => 'free', - 'fraudulent' => false -]); +/** @var Payment $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + +/** @var OrderItem $orderItem */ +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple'); -/** @var \Magento\Sales\Model\Order\Item $orderItem */ -$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); -$orderItem->setProductId($product->getId())->setQtyOrdered(2); -$orderItem->setBasePrice($product->getPrice()); -$orderItem->setPrice($product->getPrice()); -$orderItem->setRowTotal($product->getPrice()); -$orderItem->setProductType('simple'); +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000001') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem) + ->setPayment($payment); -/** @var \Magento\Sales\Model\Order $order */ -$order = $objectManager->create(\Magento\Sales\Model\Order::class); -$order->setIncrementId( - '100000001' -)->setState( - \Magento\Sales\Model\Order::STATE_PROCESSING -)->setStatus( - $order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_PROCESSING) -)->setSubtotal( - 100 -)->setGrandTotal( - 100 -)->setBaseSubtotal( - 100 -)->setBaseGrandTotal( - 100 -)->setCustomerIsGuest( - true -)->setCustomerEmail( - 'customer@null.com' -)->setBillingAddress( - $billingAddress -)->setShippingAddress( - $shippingAddress -)->setStoreId( - $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId() -)->addItem( - $orderItem -)->setPayment( - $payment -); -$order->save(); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_two_products_and_customer.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_two_products_and_customer.php new file mode 100644 index 0000000000000..b3b341c092d69 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_two_products_and_customer.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../Checkout/_files/quote_with_address.php'; + +/** @var \Magento\Quote\Model\Quote $quote */ +$quote->addProduct( + $customDesignProduct->load($customDesignProduct->getId()), + 1 +); + +$quote->getPayment()->setMethod('payflowpro'); +$quote->setIsMultiShipping('0'); +$quote->getShippingAddress()->setShippingMethod('freeshipping_freeshipping'); +$quote->setReservedOrderId('test01'); +$quote->collectTotals() + ->save(); + +/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ +$quoteIdMask = Bootstrap::getObjectManager() + ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class) + ->create(); +$quoteIdMask->setQuoteId($quote->getId()); +$quoteIdMask->setDataChanges(true); +$quoteIdMask->save(); From 8559900dab7a81c5dc7fd9b7396fb05aa594f8c1 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Tue, 29 May 2018 16:15:40 +0300 Subject: [PATCH 228/333] MAGETWO-92134: [2.2.x] Payment Failed Email is not generated --- app/code/Magento/Checkout/composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Checkout/composer.json b/app/code/Magento/Checkout/composer.json index 5f695adc9f4b4..540565345bd9b 100644 --- a/app/code/Magento/Checkout/composer.json +++ b/app/code/Magento/Checkout/composer.json @@ -7,7 +7,6 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-backend": "*", "magento/module-catalog": "*", "magento/module-catalog-inventory": "*", "magento/module-config": "*", From 67ad1659e6c907bf452ab9263b3df5456b09ef05 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 29 May 2018 17:08:59 +0200 Subject: [PATCH 229/333] DocBloc typo fixes --- app/code/Magento/Config/Block/System/Config/Form.php | 2 +- .../Model/Import/Product/Type/Configurable.php | 2 +- app/code/Magento/Customer/Model/Session.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Config/Block/System/Config/Form.php b/app/code/Magento/Config/Block/System/Config/Form.php index c17df229cf549..81e39a83296d7 100644 --- a/app/code/Magento/Config/Block/System/Config/Form.php +++ b/app/code/Magento/Config/Block/System/Config/Form.php @@ -709,7 +709,7 @@ protected function _getAdditionalElementTypes() } /** - * Temporary moved those $this->getRequest()->getParam('blabla') from the code accross this block + * Temporary moved those $this->getRequest()->getParam('blabla') from the code across this block * to getBlala() methods to be later set from controller with setters */ diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 64a0c23139c01..151bf5aa9263e 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -629,7 +629,7 @@ protected function _insertData() } /** - * Get new supper attribute id. + * Get new super attribute id. * * @return int */ diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index 71b0297fdd114..680e68b5c4c0f 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -555,7 +555,7 @@ public function setAfterAuthUrl($url) } /** - * Reset core session hosts after reseting session ID + * Reset core session hosts after resetting session ID * * @return $this */ From 154fc4007c264fcf456c58d63691fcf8ef9b6adb Mon Sep 17 00:00:00 2001 From: Tom Richards <tom.r@delegator.com> Date: Wed, 16 May 2018 20:46:22 -0400 Subject: [PATCH 230/333] Fix typo in Image::open exception message --- lib/internal/Magento/Framework/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Image.php b/lib/internal/Magento/Framework/Image.php index 183f57ca26f69..5dc1969559fdd 100644 --- a/lib/internal/Magento/Framework/Image.php +++ b/lib/internal/Magento/Framework/Image.php @@ -49,7 +49,7 @@ public function open() $this->_adapter->checkDependencies(); if (!file_exists($this->_fileName)) { - throw new \Exception("File '{$this->_fileName}' does not exists."); + throw new \Exception("File '{$this->_fileName}' does not exist."); } $this->_adapter->open($this->_fileName); From 6a0f4ca056425fa2a5d4722a942ae2e08b95ed84 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Thu, 17 May 2018 09:35:17 +0200 Subject: [PATCH 231/333] typo in method name from Nvp class --- app/code/Magento/Paypal/Model/Api/Nvp.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Api/Nvp.php b/app/code/Magento/Paypal/Model/Api/Nvp.php index 25883590350f4..09b305330d752 100644 --- a/app/code/Magento/Paypal/Model/Api/Nvp.php +++ b/app/code/Magento/Paypal/Model/Api/Nvp.php @@ -844,7 +844,7 @@ public function callGetExpressCheckoutDetails() $request = $this->_exportToRequest($this->_getExpressCheckoutDetailsRequest); $response = $this->call(self::GET_EXPRESS_CHECKOUT_DETAILS, $request); $this->_importFromResponse($this->_paymentInformationResponse, $response); - $this->_exportAddressses($response); + $this->_exportAddresses($response); } /** @@ -1462,7 +1462,7 @@ protected function _exportLineItems(array &$request, $i = 0) * @param array $data * @return void */ - protected function _exportAddressses($data) + protected function _exportAddresses($data) { $address = new \Magento\Framework\DataObject(); \Magento\Framework\DataObject\Mapper::accumulateByMap($data, $address, $this->_billingAddressMap); From b4398ae7ea7fc0cfc4211961a91705495861a1d9 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Fri, 18 May 2018 09:29:39 +0200 Subject: [PATCH 232/333] add deprecated notice to misspelled method --- app/code/Magento/Paypal/Model/Api/Nvp.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/code/Magento/Paypal/Model/Api/Nvp.php b/app/code/Magento/Paypal/Model/Api/Nvp.php index 09b305330d752..624068395394d 100644 --- a/app/code/Magento/Paypal/Model/Api/Nvp.php +++ b/app/code/Magento/Paypal/Model/Api/Nvp.php @@ -1456,6 +1456,19 @@ protected function _exportLineItems(array &$request, $i = 0) return parent::_exportLineItems($request, $i); } + /** + * Create billing and shipping addresses basing on response data + * + * @param array $data + * @return void + * @deprecated 100.2.2 typo in method name + * @see _exportAddresses + */ + protected function _exportAddressses($data) + { + $this->_exportAddresses($data); + } + /** * Create billing and shipping addresses basing on response data * From 784099e7ec3276ce3e3dc24d498c3e3a3445cf4c Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Thu, 17 May 2018 09:42:41 +0200 Subject: [PATCH 233/333] typo in method name _getCharg[e]ableOptionPrice --- .../Catalog/Model/Product/Option/Type/DefaultType.php | 6 +++--- .../Magento/Catalog/Model/Product/Option/Type/Select.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) 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 51480e849d9f3..cdb3338d4bc06 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php @@ -341,7 +341,7 @@ public function getOptionPrice($optionValue, $basePrice) { $option = $this->getOption(); - return $this->_getChargableOptionPrice($option->getPrice(), $option->getPriceType() == 'percent', $basePrice); + return $this->_getChargeableOptionPrice($option->getPrice(), $option->getPriceType() == 'percent', $basePrice); } /** @@ -395,14 +395,14 @@ public function getProductOptions() } /** - * Return final chargable price for option + * 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 */ - protected function _getChargableOptionPrice($price, $isPercent, $basePrice) + protected function _getChargeableOptionPrice($price, $isPercent, $basePrice) { if ($isPercent) { return $basePrice * $price / 100; 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 0ace0372c43bb..4a257a4781063 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php @@ -231,7 +231,7 @@ public function getOptionPrice($optionValue, $basePrice) foreach (explode(',', $optionValue) as $value) { $_result = $option->getValueById($value); if ($_result) { - $result += $this->_getChargableOptionPrice( + $result += $this->_getChargeableOptionPrice( $_result->getPrice(), $_result->getPriceType() == 'percent', $basePrice @@ -246,7 +246,7 @@ public function getOptionPrice($optionValue, $basePrice) } elseif ($this->_isSingleSelection()) { $_result = $option->getValueById($optionValue); if ($_result) { - $result = $this->_getChargableOptionPrice( + $result = $this->_getChargeableOptionPrice( $_result->getPrice(), $_result->getPriceType() == 'percent', $basePrice From 382d6854e94000e2bc74b13f3a82df12ae51bfa7 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Fri, 18 May 2018 09:23:29 +0200 Subject: [PATCH 234/333] add deprecated notice to misspelled method --- .../Model/Product/Option/Type/DefaultType.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 cdb3338d4bc06..2390a049fbeb6 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php @@ -394,6 +394,19 @@ public function getProductOptions() return []; } + /** + * @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 + */ + protected function _getChargableOptionPrice($price, $isPercent, $basePrice) + { + return $this->_getChargeableOptionPrice($price, $isPercent, $basePrice); + } + /** * Return final chargeable price for option * From 751c3e609e4529d96c64c0c06c6e84efcdec5f92 Mon Sep 17 00:00:00 2001 From: neeta-wagento <neeta@wagento.com> Date: Sat, 19 May 2018 10:47:00 +0530 Subject: [PATCH 235/333] set alignment purchase order form and place order button --- .../web/css/source/module/checkout/_payments.less | 5 +++++ 1 file changed, 5 insertions(+) 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 3136056d9426d..188fff9379b18 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 @@ -46,6 +46,11 @@ .lib-css(border-top, @checkout-payment-method-title__border); } } + form{ + &.form-purchase-order { + margin-bottom: 15px; + } + } } .payment-method-content { From fb0f192c767f3f8e30a7c861108768d35fbc8b24 Mon Sep 17 00:00:00 2001 From: Piyush Dankhara <dankhrapiyush@gmail.com> Date: Mon, 21 May 2018 19:35:34 +0530 Subject: [PATCH 236/333] Update _payments.less Added space --- .../web/css/source/module/checkout/_payments.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less index 188fff9379b18..c7fa7a62fd68e 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 @@ -46,7 +46,7 @@ .lib-css(border-top, @checkout-payment-method-title__border); } } - form{ + form { &.form-purchase-order { margin-bottom: 15px; } From 6e3acbc0a00b2efe7cd4344e312ab7289cfa7f06 Mon Sep 17 00:00:00 2001 From: Sanjay Patel <sanjay@wagento.com> Date: Sat, 19 May 2018 12:41:34 +0530 Subject: [PATCH 237/333] #12820 - Wrong annotation in _toOptionArray - magento/framework/Data/Collection/AbstractDb.php --- lib/internal/Magento/Framework/Data/Collection/AbstractDb.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php index 4feaf21d2b2e8..63ba6824e5ab9 100644 --- a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php +++ b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php @@ -630,7 +630,7 @@ public function fetchItem() /** * Overridden to use _idFieldName by default. * - * @param null $valueField + * @param string|null $valueField * @param string $labelField * @param array $additional * @return array From 093f1b9f8e900b1272d4528d2274312c4006b7d6 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Tue, 29 May 2018 18:29:43 -0500 Subject: [PATCH 238/333] ENGCOM-1663: Include 'products' in category query #48 - Fixed test in case when indexer mode is 'on schedule' --- .../Magento/GraphQl/Catalog/CategoryTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 8fa731fb0d7f2..8fc19a21421c0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -120,6 +120,18 @@ public function testCategoriesTree() */ public function testCategoryProducts() { + /** @var \Magento\Catalog\Model\Indexer\Category\Product\Processor $categoryProductIndexer */ + $categoryProductIndexer = $this->objectManager->get( + \Magento\Catalog\Model\Indexer\Category\Product\Processor::class + ); + $categoryProductIndexer->reindexAll(); + + /** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor $inventoryIndexer */ + $inventoryIndexer = $this->objectManager->get( + \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class + ); + $inventoryIndexer->reindexAll(); + $categoryId = 4; $query = <<<QUERY { From 909c430c615b962b0393f7e98ae33fba11c09194 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Tue, 29 May 2018 23:43:56 -0500 Subject: [PATCH 239/333] MAGETWO-92260: GraphQL - Fix urlResolver query to support relative path #13 - Fixed and added tests - Resolved incorrect dependencies between modules and introduced extension point for custom URL resolver --- .../UrlRewrite/HomePageUrlResolver.php | 47 ++++++++++++ .../Magento/CmsUrlRewriteGraphQl/etc/di.xml | 16 +++++ .../Model/Resolver/UrlRewrite.php | 23 +++--- .../Resolver/UrlRewrite/CustomUrlResolver.php | 41 +++++++++++ .../UrlRewrite/CustomUrlResolverInterface.php | 24 +++++++ .../Magento/UrlRewriteGraphQl/composer.json | 5 +- app/code/Magento/UrlRewriteGraphQl/etc/di.xml | 10 +++ .../GraphQl/UrlRewrite/UrlResolverTest.php | 71 +++++++++++++------ 8 files changed, 197 insertions(+), 40 deletions(-) create mode 100644 app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlResolver.php create mode 100644 app/code/Magento/CmsUrlRewriteGraphQl/etc/di.xml create mode 100644 app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolver.php create mode 100644 app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolverInterface.php create mode 100644 app/code/Magento/UrlRewriteGraphQl/etc/di.xml diff --git a/app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlResolver.php b/app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlResolver.php new file mode 100644 index 0000000000000..d20323a485d71 --- /dev/null +++ b/app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlResolver.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CmsUrlRewriteGraphQl\Model\Resolver\UrlRewrite; + +use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolverInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Cms\Helper\Page; + +/** + * Home page URL resolver. + */ +class HomePageUrlResolver implements CustomUrlResolverInterface +{ + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + */ + public function __construct( + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + ) { + $this->scopeConfig = $scopeConfig; + } + + /** + * @inheritdoc + */ + public function resolveUrl($urlKey): ?string + { + if ($urlKey === '/') { + $homePageUrl = $this->scopeConfig->getValue( + Page::XML_PATH_HOME_PAGE, + ScopeInterface::SCOPE_STORE + ); + return $homePageUrl; + } + return null; + } +} diff --git a/app/code/Magento/CmsUrlRewriteGraphQl/etc/di.xml b/app/code/Magento/CmsUrlRewriteGraphQl/etc/di.xml new file mode 100644 index 0000000000000..8cd226b3fd258 --- /dev/null +++ b/app/code/Magento/CmsUrlRewriteGraphQl/etc/di.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolver"> + <arguments> + <argument name="urlResolvers" xsi:type="array"> + <item name="homePageResolver" xsi:type="object">Magento\CmsUrlRewriteGraphQl\Model\Resolver\UrlRewrite\HomePageUrlResolver</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 8ec3874d9095b..0de21c81f7a8c 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -14,8 +14,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\UrlFinderInterface; -use Magento\Store\Model\ScopeInterface; -use Magento\Cms\Helper\Page; +use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolverInterface; /** * UrlRewrite field resolver, used for GraphQL request processing. @@ -38,26 +37,26 @@ class UrlRewrite implements ResolverInterface private $valueFactory; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var UrlRewrite\CustomUrlResolverInterface */ - private $scopeConfig; - + private $customUrlResolver; + /** * @param UrlFinderInterface $urlFinder * @param StoreManagerInterface $storeManager * @param ValueFactory $valueFactory - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param CustomUrlResolverInterface $customUrlResolver */ public function __construct( UrlFinderInterface $urlFinder, StoreManagerInterface $storeManager, ValueFactory $valueFactory, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + CustomUrlResolverInterface $customUrlResolver ) { $this->urlFinder = $urlFinder; $this->storeManager = $storeManager; $this->valueFactory = $valueFactory; - $this->scopeConfig = $scopeConfig; + $this->customUrlResolver = $customUrlResolver; } /** @@ -78,13 +77,9 @@ public function resolve( $url = $args['url']; if (substr($url, 0, 1) === '/' && $url !== '/') { $url = ltrim($url, '/'); - } else if ($url === '/') { - $homePageIdentifier = $this->scopeConfig->getValue( - Page::XML_PATH_HOME_PAGE, - ScopeInterface::SCOPE_STORE - ); - $url = $homePageIdentifier; } + $customUrl = $this->customUrlResolver->resolveUrl($url); + $url = $customUrl ?: $url; $urlRewrite = $this->findCanonicalUrl($url); if ($urlRewrite) { $urlRewriteReturnArray = [ diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolver.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolver.php new file mode 100644 index 0000000000000..08efb54bd6bbb --- /dev/null +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolver.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite; + +/** + * Pool of custom URL resolvers. + */ +class CustomUrlResolver implements CustomUrlResolverInterface +{ + /** + * @var CustomUrlResolverInterface[] + */ + private $urlResolvers; + + /** + * @param CustomUrlResolverInterface[] $urlResolvers + */ + public function __construct(array $urlResolvers = []) + { + $this->urlResolvers = $urlResolvers; + } + + /** + * @inheritdoc + */ + public function resolveUrl($urlKey): ?string + { + foreach ($this->urlResolvers as $urlResolver) { + $url = $urlResolver->resolveUrl($urlKey); + if ($url !== null) { + return $url; + } + } + return null; + } +} diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolverInterface.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolverInterface.php new file mode 100644 index 0000000000000..1aee63b670635 --- /dev/null +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolverInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite; + +/** + * Interface for resolution of custom URLs. + * + * It can be used, for example, to resolve '\' URL path to a 'Home' page. + */ +interface CustomUrlResolverInterface +{ + /** + * Resolve URL based on custom rules. + * + * @param string $urlKey + * @return string|null Return null if URL cannot be resolved + */ + public function resolveUrl($urlKey): ?string; +} diff --git a/app/code/Magento/UrlRewriteGraphQl/composer.json b/app/code/Magento/UrlRewriteGraphQl/composer.json index a1db660ab1526..8d5295a2389fa 100644 --- a/app/code/Magento/UrlRewriteGraphQl/composer.json +++ b/app/code/Magento/UrlRewriteGraphQl/composer.json @@ -6,8 +6,7 @@ "php" : "~7.1.3||~7.2.0", "magento/framework" : "*", "magento/module-url-rewrite" : "*", - "magento/module-store" : "*", - "magento/cms" : "*" + "magento/module-store": "*" }, "suggest" : { "magento/module-graph-ql" : "*" @@ -24,4 +23,4 @@ "Magento\\UrlRewriteGraphQl\\" : "" } } -} \ No newline at end of file +} diff --git a/app/code/Magento/UrlRewriteGraphQl/etc/di.xml b/app/code/Magento/UrlRewriteGraphQl/etc/di.xml new file mode 100644 index 0000000000000..c7b1533962af0 --- /dev/null +++ b/app/code/Magento/UrlRewriteGraphQl/etc/di.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:ObjectManager/etc/config.xsd"> + <preference for="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolverInterface" type="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolver"/> +</config> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php index 4b6e00ba5cf74..0bd24ee7bc88c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php @@ -39,13 +39,13 @@ public function testProductUrlResolver() /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - $storeId = $product->getStoreId(); + $storeId = $product->getStoreId(); /** @var UrlFinderInterface $urlFinder */ $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $actualUrls = $urlFinder->findOneByData( [ - 'request_path' =>$urlPath, + 'request_path' => $urlPath, 'store_id' => $storeId ] ); @@ -81,14 +81,14 @@ public function testProductUrlWithCanonicalUrlInput() /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - $storeId = $product->getStoreId(); + $storeId = $product->getStoreId(); $product->getUrlKey(); /** @var UrlFinderInterface $urlFinder */ $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $actualUrls = $urlFinder->findOneByData( [ - 'request_path' =>$urlPath, + 'request_path' => $urlPath, 'store_id' => $storeId ] ); @@ -125,13 +125,13 @@ public function testCategoryUrlResolver() /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - $storeId = $product->getStoreId(); + $storeId = $product->getStoreId(); /** @var UrlFinderInterface $urlFinder */ $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $actualUrls = $urlFinder->findOneByData( [ - 'request_path' =>$urlPath2, + 'request_path' => $urlPath2, 'store_id' => $storeId ] ); @@ -146,7 +146,7 @@ public function testCategoryUrlResolver() id canonical_url type - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -178,7 +178,7 @@ public function testCMSPageUrlResolver() = <<<QUERY { urlResolver(url:"{$requestPath}") - { + { id canonical_url type @@ -202,16 +202,16 @@ public function testProductUrlRewriteResolver() /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - $storeId = $product->getStoreId(); + $storeId = $product->getStoreId(); $product->setUrlKey('p002-new')->save(); - $urlPath = $product->getUrlKey() .'.html'; + $urlPath = $product->getUrlKey() . '.html'; $this->assertEquals($urlPath, 'p002-new.html'); /** @var UrlFinderInterface $urlFinder */ $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $actualUrls = $urlFinder->findOneByData( [ - 'request_path' =>$urlPath, + 'request_path' => $urlPath, 'store_id' => $storeId ] ); @@ -225,7 +225,7 @@ public function testProductUrlRewriteResolver() id canonical_url type - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -247,13 +247,13 @@ public function testInvalidUrlResolverInput() /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - $storeId = $product->getStoreId(); + $storeId = $product->getStoreId(); /** @var UrlFinderInterface $urlFinder */ $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $urlFinder->findOneByData( [ - 'request_path' =>$urlPath, + 'request_path' => $urlPath, 'store_id' => $storeId ] ); @@ -272,35 +272,36 @@ public function testInvalidUrlResolverInput() $this->assertArrayHasKey('urlResolver', $response); $this->assertNull($response['urlResolver']); } - + /** * Test for category entity with leading slash + * + * @magentoApiDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php */ public function testCategoryUrlWithLeadingSlash() { $productSku = 'p002'; - $urlPath2 = 'cat-1.html'; + $urlPath = 'cat-1.html'; /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - $storeId = $product->getStoreId(); - + $storeId = $product->getStoreId(); + /** @var UrlFinderInterface $urlFinder */ $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $actualUrls = $urlFinder->findOneByData( [ - 'request_path' => $urlPath2, + 'request_path' => $urlPath, 'store_id' => $storeId ] ); $categoryId = $actualUrls->getEntityId(); $targetPath = $actualUrls->getTargetPath(); $expectedType = $actualUrls->getEntityType(); - - $query - = <<<QUERY + + $query = <<<QUERY { - urlResolver(url:"/{$urlPath2}") + urlResolver(url:"/{$urlPath}") { id canonical_url @@ -314,4 +315,28 @@ public function testCategoryUrlWithLeadingSlash() $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']); $this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']); } + + /** + * Test resolution of '/' path to home page + */ + public function testResolveSlash() + { + $query + = <<<QUERY +{ + urlResolver(url:"/") + { + id + canonical_url + type + } +} +QUERY; + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('urlResolver', $response); + $this->assertEquals(2, $response['urlResolver']['id']); + $this->assertEquals('cms/page/view/page_id/2', $response['urlResolver']['canonical_url']); + $this->assertEquals('CMS_PAGE', $response['urlResolver']['type']); + } } From aca9103445f3d260fc9dae178f91e60f7794d405 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 30 May 2018 08:07:06 +0300 Subject: [PATCH 240/333] ENGCOM-1702: [Forwardport] Moved css from media #TODO #15536 --- .../luma/Magento_Theme/web/css/source/_module.less | 5 ++++- .../Magento/Sniffs/Less/PropertiesSortingSniff.php | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index 94f34cd0cf1e1..83157281561c2 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 @@ -332,7 +332,10 @@ .widget.block { .lib-css(margin, @indent__base 0); } - .links .widget.block { margin: 0; } + + .links .widget.block { + margin: 0; + } } .no-display:extend(.abs-no-display all) { diff --git a/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesSortingSniff.php b/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesSortingSniff.php index 29b6d9a8cae9d..5f8953da5cf9f 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesSortingSniff.php +++ b/dev/tests/static/framework/Magento/Sniffs/Less/PropertiesSortingSniff.php @@ -100,6 +100,14 @@ public function process(File $phpcsFile, $stackPtr) */ private function validatePropertiesSorting(File $phpcsFile, $stackPtr, array $properties) { + // Fix needed for cases when incorrect properties passed for validation due to bug in PHP tokens. + $symbolsForSkip = ['(', 'block']; + $properties = array_filter( + $properties, + function ($var) use ($symbolsForSkip) { + return !in_array($var, $symbolsForSkip); + } + ); $originalProperties = $properties; sort($properties); From cccfe04d0f2dec820c09a6562ffdf5abe9061159 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Fri, 25 May 2018 11:48:45 +0200 Subject: [PATCH 241/333] fix dynamical assigned property as it wasn't assigned to an existing one --- app/code/Magento/Webapi/Model/Soap/Fault.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Webapi/Model/Soap/Fault.php b/app/code/Magento/Webapi/Model/Soap/Fault.php index b8ddf4e47d451..74b1b41ee1bf5 100644 --- a/app/code/Magento/Webapi/Model/Soap/Fault.php +++ b/app/code/Magento/Webapi/Model/Soap/Fault.php @@ -39,7 +39,9 @@ class Fault const NODE_DETAIL_WRAPPER = 'GenericFault'; /**#@-*/ - /**#@-*/ + /** + * @var string + */ protected $_soapFaultCode; /** @@ -114,7 +116,7 @@ public function __construct( \Magento\Framework\Locale\ResolverInterface $localeResolver, State $appState ) { - $this->_soapCode = $exception->getOriginator(); + $this->_soapFaultCode = $exception->getOriginator(); $this->_parameters = $exception->getDetails(); $this->_wrappedErrors = $exception->getErrors(); $this->stackTrace = $exception->getStackTrace() ?: $exception->getTraceAsString(); @@ -194,7 +196,7 @@ public function getDetails() */ public function getSoapCode() { - return $this->_soapCode; + return $this->_soapFaultCode; } /** From 6f4e81b6ec8d685d2fdfbd65b7b64939b533fb45 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 30 May 2018 10:25:47 +0300 Subject: [PATCH 242/333] MAGETWO-90762: [2.3.0] Placing an order takes more time then usual --- .../GuestPaymentInformationManagement.php | 32 ++++++- .../GuestPaymentInformationManagementTest.php | 90 +++++++++++++++---- .../Quote/Model/PaymentMethodManagement.php | 24 ++--- .../Model/PaymentMethodManagementTest.php | 36 ++++---- 4 files changed, 131 insertions(+), 51 deletions(-) diff --git a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php index a17cf41585649..333226b7d216f 100644 --- a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Checkout\Model; @@ -10,6 +11,7 @@ use Magento\Framework\App\ResourceConnection; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Quote\Model\Quote; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -135,13 +137,19 @@ public function savePaymentInformation( \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, \Magento\Quote\Api\Data\AddressInterface $billingAddress = null ) { + $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id'); + /** @var Quote $quote */ + $quote = $this->cartRepository->getActive($quoteIdMask->getQuoteId()); + if ($billingAddress) { $billingAddress->setEmail($email); - $this->billingAddressManagement->assign($cartId, $billingAddress); + $quote->removeAddress($quote->getBillingAddress()->getId()); + $quote->setBillingAddress($billingAddress); + $quote->setDataChanges(true); } else { - $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id'); - $this->cartRepository->getActive($quoteIdMask->getQuoteId())->getBillingAddress()->setEmail($email); + $quote->getBillingAddress()->setEmail($email); } + $this->limitShippingCarrier($quote); $this->paymentMethodManagement->set($cartId, $paymentMethod); return true; @@ -169,4 +177,22 @@ private function getLogger() } return $this->logger; } + + /** + * Limits shipping rates request by carrier from shipping address. + * + * @param Quote $quote + * + * @return void + * @see \Magento\Shipping\Model\Shipping::collectRates + */ + private function limitShippingCarrier(Quote $quote) : void + { + $shippingAddress = $quote->getShippingAddress(); + if ($shippingAddress && $shippingAddress->getShippingMethod()) { + $shippingDataArray = explode('_', $shippingAddress->getShippingMethod()); + $shippingCarrier = array_shift($shippingDataArray); + $shippingAddress->setLimitCarrier($shippingCarrier); + } + } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php index da0de5d4f0a3d..c4924ca211024 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php @@ -3,11 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); 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; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -96,6 +100,7 @@ public function testSavePaymentInformationAndPlaceOrder() $email = 'email@magento.com'; $paymentMock = $this->createMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); + $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); @@ -119,10 +124,6 @@ public function testSavePaymentInformationAndPlaceOrder() ->willReturn($adapterMockForCheckout); $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); $adapterMockForCheckout->expects($this->once())->method('commit'); - - $this->billingAddressManagementMock->expects($this->once()) - ->method('assign') - ->with($cartId, $billingAddressMock); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $this->cartManagementMock->expects($this->once())->method('placeOrder')->with($cartId)->willReturn($orderId); @@ -142,6 +143,7 @@ public function testSavePaymentInformationAndPlaceOrderException() $paymentMock = $this->createMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); + $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); $adapterMockForSales = $this->getMockBuilder(AdapterInterface::class) @@ -164,10 +166,7 @@ public function testSavePaymentInformationAndPlaceOrderException() ->willReturn($adapterMockForCheckout); $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); $adapterMockForCheckout->expects($this->once())->method('rollback'); - - $this->billingAddressManagementMock->expects($this->once()) - ->method('assign') - ->with($cartId, $billingAddressMock); + $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $exception = new \Exception(__('DB exception')); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); @@ -185,11 +184,9 @@ public function testSavePaymentInformation() $email = 'email@magento.com'; $paymentMock = $this->createMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); + $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); - $this->billingAddressManagementMock->expects($this->once()) - ->method('assign') - ->with($cartId, $billingAddressMock); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $this->assertTrue($this->model->savePaymentInformation($cartId, $email, $paymentMock, $billingAddressMock)); @@ -201,13 +198,13 @@ public function testSavePaymentInformationWithoutBillingAddress() $email = 'email@magento.com'; $paymentMock = $this->createMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $quoteMock = $this->createMock(Quote::class); $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); $this->billingAddressManagementMock->expects($this->never())->method('assign'); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); - $quoteIdMaskMock = $this->createPartialMock(\Magento\Quote\Model\QuoteIdMask::class, ['getQuoteId', 'load']); + $quoteIdMaskMock = $this->createPartialMock(QuoteIdMask::class, ['getQuoteId', 'load']); $this->quoteIdMaskFactoryMock->expects($this->once())->method('create')->willReturn($quoteIdMaskMock); $quoteIdMaskMock->expects($this->once())->method('load')->with($cartId, 'masked_id')->willReturnSelf(); $quoteIdMaskMock->expects($this->once())->method('getQuoteId')->willReturn($cartId); @@ -228,6 +225,15 @@ public function testSavePaymentInformationAndPlaceOrderWithLocalizedException() $paymentMock = $this->createMock(\Magento\Quote\Api\Data\PaymentInterface::class); $billingAddressMock = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); + $quoteMock = $this->createMock(Quote::class); + $quoteMock->method('getBillingAddress')->willReturn($billingAddressMock); + $this->cartRepositoryMock->method('getActive')->with($cartId)->willReturn($quoteMock); + + $quoteIdMask = $this->createPartialMock(QuoteIdMask::class, ['getQuoteId', 'load']); + $this->quoteIdMaskFactoryMock->method('create')->willReturn($quoteIdMask); + $quoteIdMask->method('load')->with($cartId, 'masked_id')->willReturnSelf(); + $quoteIdMask->method('getQuoteId')->willReturn($cartId); + $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); $adapterMockForSales = $this->getMockBuilder(AdapterInterface::class) @@ -250,10 +256,7 @@ public function testSavePaymentInformationAndPlaceOrderWithLocalizedException() ->willReturn($adapterMockForCheckout); $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); $adapterMockForCheckout->expects($this->once())->method('rollback'); - - $this->billingAddressManagementMock->expects($this->once()) - ->method('assign') - ->with($cartId, $billingAddressMock); + $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $phrase = new \Magento\Framework\Phrase(__('DB exception')); $exception = new \Magento\Framework\Exception\LocalizedException($phrase); @@ -262,4 +265,57 @@ public function testSavePaymentInformationAndPlaceOrderWithLocalizedException() $this->model->savePaymentInformationAndPlaceOrder($cartId, $email, $paymentMock, $billingAddressMock); } + + /** + * @param int $cartId + * @param \PHPUnit_Framework_MockObject_MockObject $billingAddressMock + * @return void + */ + private function getMockForAssignBillingAddress( + int $cartId, + \PHPUnit_Framework_MockObject_MockObject $billingAddressMock + ) : void { + $quoteIdMask = $this->createPartialMock(QuoteIdMask::class, ['getQuoteId', 'load']); + $this->quoteIdMaskFactoryMock->method('create') + ->willReturn($quoteIdMask); + $quoteIdMask->method('load') + ->with($cartId, 'masked_id') + ->willReturnSelf(); + $quoteIdMask->method('getQuoteId') + ->willReturn($cartId); + + $billingAddressId = 1; + $quote = $this->createMock(Quote::class); + $quoteBillingAddress = $this->createMock(Address::class); + $quoteShippingAddress = $this->createPartialMock( + Address::class, + ['setLimitCarrier', 'getShippingMethod'] + ); + $this->cartRepositoryMock->method('getActive') + ->with($cartId) + ->willReturn($quote); + $quote->expects($this->once()) + ->method('getBillingAddress') + ->willReturn($quoteBillingAddress); + $quote->expects($this->once()) + ->method('getShippingAddress') + ->willReturn($quoteShippingAddress); + $quoteBillingAddress->expects($this->once()) + ->method('getId') + ->willReturn($billingAddressId); + $quote->expects($this->once()) + ->method('removeAddress') + ->with($billingAddressId); + $quote->expects($this->once()) + ->method('setBillingAddress') + ->with($billingAddressMock); + $quote->expects($this->once()) + ->method('setDataChanges') + ->willReturnSelf(); + $quoteShippingAddress->method('getShippingMethod') + ->willReturn('flatrate_flatrate'); + $quoteShippingAddress->expects($this->once()) + ->method('setLimitCarrier') + ->with('flatrate'); + } } diff --git a/app/code/Magento/Quote/Model/PaymentMethodManagement.php b/app/code/Magento/Quote/Model/PaymentMethodManagement.php index 91d8fe4dbcffd..b6e4bcf5ccc8f 100644 --- a/app/code/Magento/Quote/Model/PaymentMethodManagement.php +++ b/app/code/Magento/Quote/Model/PaymentMethodManagement.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Quote\Model; @@ -52,38 +53,37 @@ public function set($cartId, \Magento\Quote\Api\Data\PaymentInterface $method) { /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->quoteRepository->get($cartId); - + $quote->setTotalsCollectedFlag(false); $method->setChecks([ \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_CHECKOUT, \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_COUNTRY, \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_CURRENCY, \Magento\Payment\Model\Method\AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX, ]); - $payment = $quote->getPayment(); - - $data = $method->getData(); - $payment->importData($data); if ($quote->isVirtual()) { - $quote->getBillingAddress()->setPaymentMethod($payment->getMethod()); + $address = $quote->getBillingAddress(); } else { + $address = $quote->getShippingAddress(); // check if shipping address is set - if ($quote->getShippingAddress()->getCountryId() === null) { + if ($address->getCountryId() === null) { throw new InvalidTransitionException( __('The shipping address is missing. Set the address and try again.') ); } - $quote->getShippingAddress()->setPaymentMethod($payment->getMethod()); - } - if (!$quote->isVirtual() && $quote->getShippingAddress()) { - $quote->getShippingAddress()->setCollectShippingRates(true); + $address->setCollectShippingRates(true); } + $paymentData = $method->getData(); + $payment = $quote->getPayment(); + $payment->importData($paymentData); + $address->setPaymentMethod($payment->getMethod()); + if (!$this->zeroTotalValidator->isApplicable($payment->getMethodInstance(), $quote)) { throw new InvalidTransitionException(__('The requested Payment Method is not available.')); } - $quote->setTotalsCollectedFlag(false)->collectTotals()->save(); + $quote->save(); return $quote->getPayment()->getId(); } diff --git a/app/code/Magento/Quote/Test/Unit/Model/PaymentMethodManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/PaymentMethodManagementTest.php index 68b077fcdb965..f18d1fa1b06e5 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/PaymentMethodManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/PaymentMethodManagementTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Quote\Test\Unit\Model; @@ -152,8 +153,8 @@ public function testSetVirtualProduct() ->with($paymentMethod) ->willReturnSelf(); - $quoteMock->expects($this->exactly(2))->method('getPayment')->willReturn($paymentMock); - $quoteMock->expects($this->exactly(2))->method('isVirtual')->willReturn(true); + $quoteMock->method('getPayment')->willReturn($paymentMock); + $quoteMock->expects($this->once())->method('isVirtual')->willReturn(true); $quoteMock->expects($this->once())->method('getBillingAddress')->willReturn($billingAddressMock); $methodInstance = $this->getMockForAbstractClass(\Magento\Payment\Model\MethodInterface::class); @@ -165,7 +166,6 @@ public function testSetVirtualProduct() ->willReturn(true); $quoteMock->expects($this->once())->method('setTotalsCollectedFlag')->with(false)->willReturnSelf(); - $quoteMock->expects($this->once())->method('collectTotals')->willReturnSelf(); $quoteMock->expects($this->once())->method('save')->willReturnSelf(); $paymentMock->expects($this->once())->method('getId')->willReturn($paymentId); @@ -218,9 +218,9 @@ public function testSetVirtualProductThrowsExceptionIfPaymentMethodNotAvailable( ->with($paymentMethod) ->willReturnSelf(); - $quoteMock->expects($this->once())->method('getPayment')->willReturn($paymentMock); - $quoteMock->expects($this->exactly(2))->method('isVirtual')->willReturn(true); - $quoteMock->expects($this->once())->method('getBillingAddress')->willReturn($billingAddressMock); + $quoteMock->method('getPayment')->willReturn($paymentMock); + $quoteMock->method('isVirtual')->willReturn(true); + $quoteMock->method('getBillingAddress')->willReturn($billingAddressMock); $methodInstance = $this->getMockForAbstractClass(\Magento\Payment\Model\MethodInterface::class); $paymentMock->expects($this->once())->method('getMethodInstance')->willReturn($methodInstance); @@ -268,17 +268,20 @@ public function testSetSimpleProduct() $shippingAddressMock = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, - ['getCountryId', 'setPaymentMethod'] + ['getCountryId', 'setPaymentMethod', 'setCollectShippingRates'] ); $shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(100); $shippingAddressMock->expects($this->once()) ->method('setPaymentMethod') ->with($paymentMethod) ->willReturnSelf(); + $shippingAddressMock->expects($this->once()) + ->method('setCollectShippingRates') + ->with(true); - $quoteMock->expects($this->exactly(2))->method('getPayment')->willReturn($paymentMock); - $quoteMock->expects($this->exactly(2))->method('isVirtual')->willReturn(false); - $quoteMock->expects($this->exactly(4))->method('getShippingAddress')->willReturn($shippingAddressMock); + $quoteMock->method('getPayment')->willReturn($paymentMock); + $quoteMock->method('isVirtual')->willReturn(false); + $quoteMock->method('getShippingAddress')->willReturn($shippingAddressMock); $methodInstance = $this->getMockForAbstractClass(\Magento\Payment\Model\MethodInterface::class); $paymentMock->expects($this->once())->method('getMethodInstance')->willReturn($methodInstance); @@ -289,7 +292,6 @@ public function testSetSimpleProduct() ->willReturn(true); $quoteMock->expects($this->once())->method('setTotalsCollectedFlag')->with(false)->willReturnSelf(); - $quoteMock->expects($this->once())->method('collectTotals')->willReturnSelf(); $quoteMock->expects($this->once())->method('save')->willReturnSelf(); $paymentMock->expects($this->once())->method('getId')->willReturn($paymentId); @@ -303,7 +305,6 @@ public function testSetSimpleProduct() public function testSetSimpleProductTrowsExceptionIfShippingAddressNotSet() { $cartId = 100; - $methodData = ['method' => 'data']; $quoteMock = $this->createPartialMock( \Magento\Quote\Model\Quote::class, @@ -311,6 +312,7 @@ public function testSetSimpleProductTrowsExceptionIfShippingAddressNotSet() ); $this->quoteRepositoryMock->expects($this->once())->method('get')->with($cartId)->willReturn($quoteMock); + /** @var \Magento\Quote\Model\Quote\Payment|\PHPUnit_Framework_MockObject_MockObject $methodMock */ $methodMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Payment::class, ['setChecks', 'getData']); $methodMock->expects($this->once()) ->method('setChecks') @@ -321,17 +323,13 @@ public function testSetSimpleProductTrowsExceptionIfShippingAddressNotSet() \Magento\Payment\Model\Method\AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX, ]) ->willReturnSelf(); - $methodMock->expects($this->once())->method('getData')->willReturn($methodData); - - $paymentMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Payment::class, ['importData']); - $paymentMock->expects($this->once())->method('importData')->with($methodData)->willReturnSelf(); + $methodMock->expects($this->never())->method('getData'); $shippingAddressMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Address::class, ['getCountryId']); $shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(null); - $quoteMock->expects($this->once())->method('getPayment')->willReturn($paymentMock); - $quoteMock->expects($this->once())->method('isVirtual')->willReturn(false); - $quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($shippingAddressMock); + $quoteMock->method('isVirtual')->willReturn(false); + $quoteMock->method('getShippingAddress')->willReturn($shippingAddressMock); $this->model->set($cartId, $methodMock); } From 15c920ee03b8d3d0b6a97aede4e0a944c876abd9 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Wed, 30 May 2018 10:27:04 +0300 Subject: [PATCH 243/333] MAGETWO-90349: Template file 'header.html' is not found. --- .../Customer/Model/AccountManagement.php | 2 + .../Test/Unit/Model/AccountManagementTest.php | 101 ++++++++++++++ .../Magento/Email/Model/AbstractTemplate.php | 7 +- .../Magento/Email/Model/Template/Config.php | 25 +++- .../Test/Unit/Model/Template/ConfigTest.php | 14 ++ .../Theme/Model/Design/Config/Validator.php | 1 + .../Test/Unit/Model/Config/ValidatorTest.php | 4 +- .../Model/Design/Config/ValidatorTest.php | 125 ++++++++++++++++++ 8 files changed, 272 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Theme/Test/Unit/Model/Design/Config/ValidatorTest.php diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 7d0b271b9b137..4fc74fa695829 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -873,6 +873,8 @@ protected function sendEmailConfirmation(CustomerInterface $customer, $redirectU } catch (MailException $e) { // If we are not able to send a new account email, this should be ignored $this->logger->critical($e); + } catch (\UnexpectedValueException $e) { + $this->logger->error($e); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index cfd1729e4e06e..9e3a16a307923 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -1875,4 +1875,105 @@ private function prepareDateTimeFactory() return $dateTime; } + + /** + * @return void + */ + public function testCreateAccountUnexpectedValueException(): void + { + $websiteId = 1; + $storeId = null; + $defaultStoreId = 1; + $customerId = 1; + $customerEmail = 'email@email.com'; + $newLinkToken = '2jh43j5h2345jh23lh452h345hfuzasd96ofu'; + $exception = new \UnexpectedValueException('Template file was not found'); + + $datetime = $this->prepareDateTimeFactory(); + + $address = $this->createMock(\Magento\Customer\Api\Data\AddressInterface::class); + $address->expects($this->once()) + ->method('setCustomerId') + ->with($customerId); + $store = $this->createMock(\Magento\Store\Model\Store::class); + $store->expects($this->once()) + ->method('getId') + ->willReturn($defaultStoreId); + $website = $this->createMock(\Magento\Store\Model\Website::class); + $website->expects($this->atLeastOnce()) + ->method('getStoreIds') + ->willReturn([1, 2, 3]); + $website->expects($this->once()) + ->method('getDefaultStore') + ->willReturn($store); + $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $customer->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn($customerId); + $customer->expects($this->atLeastOnce()) + ->method('getEmail') + ->willReturn($customerEmail); + $customer->expects($this->atLeastOnce()) + ->method('getWebsiteId') + ->willReturn($websiteId); + $customer->expects($this->atLeastOnce()) + ->method('getStoreId') + ->willReturn($storeId); + $customer->expects($this->once()) + ->method('setStoreId') + ->with($defaultStoreId); + $customer->expects($this->once()) + ->method('getAddresses') + ->willReturn([$address]); + $customer->expects($this->once()) + ->method('setAddresses') + ->with(null); + $this->customerRepository->expects($this->once()) + ->method('get') + ->with($customerEmail) + ->willReturn($customer); + $this->share->expects($this->once()) + ->method('isWebsiteScope') + ->willReturn(true); + $this->storeManager->expects($this->atLeastOnce()) + ->method('getWebsite') + ->with($websiteId) + ->willReturn($website); + $this->customerRepository->expects($this->atLeastOnce()) + ->method('save') + ->willReturn($customer); + $this->addressRepository->expects($this->atLeastOnce()) + ->method('save') + ->with($address); + $this->customerRepository->expects($this->once()) + ->method('getById') + ->with($customerId) + ->willReturn($customer); + $this->random->expects($this->once()) + ->method('getUniqueHash') + ->willReturn($newLinkToken); + $customerSecure = $this->createPartialMock( + \Magento\Customer\Model\Data\CustomerSecure::class, + ['setRpToken', 'setRpTokenCreatedAt', 'getPasswordHash'] + ); + $customerSecure->expects($this->any()) + ->method('setRpToken') + ->with($newLinkToken); + $customerSecure->expects($this->any()) + ->method('setRpTokenCreatedAt') + ->with($datetime) + ->willReturnSelf(); + $customerSecure->expects($this->any()) + ->method('getPasswordHash') + ->willReturn(null); + $this->customerRegistry->expects($this->atLeastOnce()) + ->method('retrieveSecureData') + ->willReturn($customerSecure); + $this->emailNotificationMock->expects($this->once()) + ->method('newAccount') + ->willThrowException($exception); + $this->logger->expects($this->once())->method('error')->with($exception); + + $this->accountManagement->createAccount($customer); + } } diff --git a/app/code/Magento/Email/Model/AbstractTemplate.php b/app/code/Magento/Email/Model/AbstractTemplate.php index 4830ecfbb74b3..a6ecdaf24ebbb 100644 --- a/app/code/Magento/Email/Model/AbstractTemplate.php +++ b/app/code/Magento/Email/Model/AbstractTemplate.php @@ -531,14 +531,13 @@ protected function cancelDesignConfig() * * @param string $templateId * @return $this - * @throws \Magento\Framework\Exception\MailException */ public function setForcedArea($templateId) { - if ($this->area) { - throw new \LogicException(__('The area is already set.')); + if ($this->area === null) { + $this->area = $this->emailConfig->getTemplateArea($templateId); } - $this->area = $this->emailConfig->getTemplateArea($templateId); + return $this; } diff --git a/app/code/Magento/Email/Model/Template/Config.php b/app/code/Magento/Email/Model/Template/Config.php index bdd9054e7969b..d10c394dcb715 100644 --- a/app/code/Magento/Email/Model/Template/Config.php +++ b/app/code/Magento/Email/Model/Template/Config.php @@ -205,8 +205,9 @@ public function getTemplateFilename($templateId, $designParams = []) $designParams['module'] = $module; $file = $this->_getInfo($templateId, 'file'); + $filename = $this->getFilename($file, $designParams, $module); - return $this->viewFileSystem->getEmailTemplateFileName($file, $designParams, $module); + return $filename; } /** @@ -230,4 +231,26 @@ protected function _getInfo($templateId, $fieldName) } return $data[$templateId][$fieldName]; } + + /** + * Retrieve template file path. + * + * @param string $file + * @param array $designParams + * @param string $module + * + * @return string + * + * @throws \UnexpectedValueException + */ + private function getFilename(string $file, array $designParams, string $module): string + { + $filename = $this->viewFileSystem->getEmailTemplateFileName($file, $designParams, $module); + + if ($filename === false) { + throw new \UnexpectedValueException("Template file '{$file}' is not found."); + } + + return $filename; + } } diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/ConfigTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/ConfigTest.php index 47c3ac1e7e450..b396f2ede8977 100644 --- a/app/code/Magento/Email/Test/Unit/Model/Template/ConfigTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/Template/ConfigTest.php @@ -272,6 +272,20 @@ public function testGetTemplateFilenameWithNoParams() $this->assertEquals('_files/Fixture/ModuleOne/view/frontend/email/one.html', $actualResult); } + /** + * @expectedException \UnexpectedValueException + * @expectedExceptionMessage Template file 'one.html' is not found + * @return void + */ + public function testGetTemplateFilenameWrongFileName(): void + { + $this->viewFileSystem->expects($this->once())->method('getEmailTemplateFileName') + ->with('one.html', $this->designParams, 'Fixture_ModuleOne') + ->willReturn(false); + + $this->model->getTemplateFilename('template_one', $this->designParams); + } + /** * @param string $getterMethod * @param $argument diff --git a/app/code/Magento/Theme/Model/Design/Config/Validator.php b/app/code/Magento/Theme/Model/Design/Config/Validator.php index 9fed5be6201b4..994eeba317a34 100644 --- a/app/code/Magento/Theme/Model/Design/Config/Validator.php +++ b/app/code/Magento/Theme/Model/Design/Config/Validator.php @@ -114,6 +114,7 @@ private function getTemplateText($templateId, DesignConfigInterface $designConfi if (is_numeric($templateId)) { $template->load($templateId); } else { + $template->setForcedArea($templateId); $template->loadDefault($templateId); } $text = $template->getTemplateText(); diff --git a/app/code/Magento/Theme/Test/Unit/Model/Config/ValidatorTest.php b/app/code/Magento/Theme/Test/Unit/Model/Config/ValidatorTest.php index 4a1a1982ea653..3b337312e3fb5 100644 --- a/app/code/Magento/Theme/Test/Unit/Model/Config/ValidatorTest.php +++ b/app/code/Magento/Theme/Test/Unit/Model/Config/ValidatorTest.php @@ -70,7 +70,7 @@ public function testValidateHasRecursiveReference() $designElementMock->expects($this->once())->method('getValue')->willReturn($fieldConfig['field']); $templateMock = $this->getMockBuilder(\Magento\Email\Model\TemplateInterface::class) - ->setMethods(['getTemplateText', 'emulateDesign', 'loadDefault', 'revertDesign']) + ->setMethods(['getTemplateText', 'emulateDesign', 'loadDefault', 'revertDesign', 'setForcedArea']) ->getMock(); $this->templateFactoryMock->expects($this->once())->method('create')->willReturn($templateMock); @@ -115,7 +115,7 @@ public function testValidateNoRecursiveReference() $designElementMock->expects($this->once())->method('getValue')->willReturn($fieldConfig['field']); $templateMock = $this->getMockBuilder(\Magento\Email\Model\TemplateInterface::class) - ->setMethods(['getTemplateText', 'emulateDesign', 'loadDefault', 'revertDesign']) + ->setMethods(['getTemplateText', 'emulateDesign', 'loadDefault', 'revertDesign', 'setForcedArea']) ->getMock(); $this->templateFactoryMock->expects($this->once())->method('create')->willReturn($templateMock); diff --git a/app/code/Magento/Theme/Test/Unit/Model/Design/Config/ValidatorTest.php b/app/code/Magento/Theme/Test/Unit/Model/Design/Config/ValidatorTest.php new file mode 100644 index 0000000000000..6b26235d0efcc --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Model/Design/Config/ValidatorTest.php @@ -0,0 +1,125 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Theme\Test\Unit\Model\Design\Config; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Theme\Model\Design\Config\Validator; + +/** + * Unit tests for Magento\Theme\Test\Unit\Model\Design\Config\Validator. + */ +class ValidatorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\Mail\TemplateInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $templateFactory; + + /** + * @var \Magento\Framework\Mail\TemplateInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $template; + + /** + * @var \Magento\Theme\Api\Data\DesignConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $designConfig; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->templateFactory = $this->getMockBuilder(\Magento\Framework\Mail\TemplateInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMockForAbstractClass(); + $this->template = $this->getMockBuilder(\Magento\Framework\Mail\TemplateInterface::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'emulateDesign', + 'setForcedArea', + 'loadDefault', + 'getTemplateText', + 'revertDesign', + ] + ) + ->getMockForAbstractClass(); + $this->templateFactory->expects($this->any())->method('create')->willReturn($this->template); + $this->designConfig = $this->getMockBuilder(\Magento\Theme\Api\Data\DesignConfigInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getExtensionAttributes']) + ->getMockForAbstractClass(); + } + + /** + * @return void + */ + public function testGetDefaultTemplateTextDefaultScope(): void + { + $templateId = 'email_template'; + $designData = [ + 'field_config' => ['field' => 'fieldValue'], + 'value' => $templateId, + ]; + + $this->templateFactory->expects($this->once())->method('create'); + $this->designConfig->expects($this->any())->method('getScope')->willReturn('default'); + $this->template->expects($this->once())->method('emulateDesign'); + $this->template->expects($this->once())->method('setForcedArea')->with($templateId); + $this->template->expects($this->once())->method('loadDefault')->with($templateId); + $this->template->expects($this->once())->method('getTemplateText'); + $this->template->expects($this->once())->method('revertDesign'); + + $extensionAttributes = $this->getMockBuilder(\Magento\Theme\Api\Data\DesignConfigExtensionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getDesignConfigData']) + ->getMockForAbstractClass(); + + $extensionAttributes->expects($this->any())->method('getDesignConfigData')->willReturn( + [ + $this->getDesignConfigData($designData), + ] + ); + + $this->designConfig->expects($this->any())->method('getExtensionAttributes')->willReturn($extensionAttributes); + + /** @var Validator $validator */ + $validator = $this->objectManager->getObject( + Validator::class, + [ + 'templateFactory' => $this->templateFactory, + 'fields' => ['field' => 'fieldValue'], + ] + ); + $validator->validate($this->designConfig); + } + + /** + * Returns design config data object. + * + * @param array $data + * @return \Magento\Theme\Model\Data\Design\Config\Data + */ + private function getDesignConfigData(array $data = []): \Magento\Theme\Model\Data\Design\Config\Data + { + return $this->objectManager->getObject( + \Magento\Theme\Model\Data\Design\Config\Data::class, + [ + 'data' => $data, + ] + ); + } +} From 107aa22bf146ed70318f97ae4c987e11754de5ac Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 30 May 2018 10:48:27 +0300 Subject: [PATCH 244/333] MAGETWO-66217: Product Url key value comes from product name on storeview level (use default = yes) --- .../Product/Initialization/Helper.php | 57 ++++------- .../Initialization/Helper/AttributeFilter.php | 58 +++++++++-- .../Helper/AttributeFilterTest.php | 98 +++++++++++++------ 3 files changed, 141 insertions(+), 72 deletions(-) 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 beb6f2b13bcfe..95339870b4d61 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -3,14 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Controller\Adminhtml\Product\Initialization; use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory as CustomOptionFactory; use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory as ProductLinkFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface\Proxy as ProductRepository; +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeDefaultValueFilter; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks; use Magento\Catalog\Model\Product\Link\Resolver as LinkResolver; +use Magento\Catalog\Model\Product\LinkTypeProvider; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter; @@ -81,7 +85,7 @@ class Helper private $dateTimeFilter; /** - * @var \Magento\Catalog\Model\Product\LinkTypeProvider + * @var LinkTypeProvider */ private $linkTypeProvider; @@ -99,10 +103,10 @@ class Helper * @param ProductLinks $productLinks * @param \Magento\Backend\Helper\Js $jsHelper * @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter - * @param \Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory|null $customOptionFactory - * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory|null $productLinkFactory - * @param \Magento\Catalog\Api\ProductRepositoryInterface|null $productRepository - * @param \Magento\Catalog\Model\Product\LinkTypeProvider|null $linkTypeProvider + * @param CustomOptionFactory|null $customOptionFactory + * @param ProductLinkFactory |null $productLinkFactory + * @param ProductRepositoryInterface|null $productRepository + * @param LinkTypeProvider|null $linkTypeProvider * @param AttributeFilter|null $attributeFilter * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -113,10 +117,10 @@ public function __construct( \Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks $productLinks, \Magento\Backend\Helper\Js $jsHelper, \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter, - \Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory $customOptionFactory = null, - \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory = null, - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository = null, - \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider = null, + CustomOptionFactory $customOptionFactory = null, + ProductLinkFactory $productLinkFactory = null, + ProductRepositoryInterface $productRepository = null, + LinkTypeProvider $linkTypeProvider = null, AttributeFilter $attributeFilter = null ) { $this->request = $request; @@ -125,16 +129,13 @@ public function __construct( $this->productLinks = $productLinks; $this->jsHelper = $jsHelper; $this->dateFilter = $dateFilter; - $this->customOptionFactory = $customOptionFactory ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory::class); - $this->productLinkFactory = $productLinkFactory ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class); - $this->productRepository = $productRepository ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $this->linkTypeProvider = $linkTypeProvider ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Model\Product\LinkTypeProvider::class); - $this->attributeFilter = $attributeFilter ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(AttributeFilter::class); + + $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $this->customOptionFactory = $customOptionFactory ?: $objectManager->get(CustomOptionFactory::class); + $this->productLinkFactory = $productLinkFactory ?: $objectManager->get(ProductLinkFactory::class); + $this->productRepository = $productRepository ?: $objectManager->get(ProductRepositoryInterface::class); + $this->linkTypeProvider = $linkTypeProvider ?: $objectManager->get(LinkTypeProvider::class); + $this->attributeFilter = $attributeFilter ?: $objectManager->get(AttributeFilter::class); } /** @@ -150,8 +151,7 @@ public function __construct( */ public function initializeFromData(\Magento\Catalog\Model\Product $product, array $productData) { - unset($productData['custom_attributes']); - unset($productData['extension_attributes']); + unset($productData['custom_attributes'], $productData['extension_attributes']); if ($productData) { $stockData = isset($productData['stock_data']) ? $productData['stock_data'] : []; @@ -199,28 +199,13 @@ public function initializeFromData(\Magento\Catalog\Model\Product $product, arra $productData['tier_price'] = isset($productData['tier_price']) ? $productData['tier_price'] : []; $useDefaults = (array)$this->request->getPost('use_default', []); - $productData = $this->attributeFilter->prepareProductAttributes($product, $productData, $useDefaults); - $product->addData($productData); if ($wasLockedMedia) { $product->lockAttribute('media'); } - /** - * Check "Use Default Value" checkboxes values - */ - foreach ($useDefaults as $attributeCode => $useDefaultState) { - if ($useDefaultState) { - $product->setData($attributeCode, null); - // UI component sends value even if field is disabled, so 'Use Config Settings' must be reset to false - if ($product->hasData('use_config_' . $attributeCode)) { - $product->setData('use_config_' . $attributeCode, false); - } - } - } - $product = $this->setProductLinks($product); $product = $this->fillProductOptions($product, $productOptions); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php index ee3a6d491e92f..09af738a13201 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php @@ -28,11 +28,57 @@ class AttributeFilter * @param array $useDefaults * @return array */ - public function prepareProductAttributes(Product $product, array $productData, array $useDefaults) + public function prepareProductAttributes(Product $product, array $productData, array $useDefaults): array { - foreach ($productData as $attribute => $value) { - if ($this->isAttributeShouldNotBeUpdated($product, $useDefaults, $attribute, $value)) { - unset($productData[$attribute]); + $attributeList = $product->getAttributes(); + foreach ($productData as $attributeCode => $attributeValue) { + if ($this->isAttributeShouldNotBeUpdated($product, $useDefaults, $attributeCode, $attributeValue)) { + unset($productData[$attributeCode]); + } + + if (isset($useDefaults[$attributeCode]) && $useDefaults[$attributeCode] === '1') { + $productData = $this->prepareDefaultData($attributeList, $attributeCode, $productData); + $productData = $this->prepareConfigData($product, $attributeCode, $productData); + } + } + + return $productData; + } + + /** + * @param Product $product + * @param string $attributeCode + * @param array $productData + * @return array + */ + private function prepareConfigData(Product $product, $attributeCode, array $productData): array + { + // UI component sends value even if field is disabled, so 'Use Config Settings' must be reset to false + if ($product->hasData('use_config_' . $attributeCode)) { + $productData['use_config_' . $attributeCode] = false; + } + + return $productData; + } + + /** + * @param array $attributeList + * @param string $attributeCode + * @param array $productData + * @return array + */ + private function prepareDefaultData(array $attributeList, $attributeCode, array $productData): array + { + if (isset($attributeList[$attributeCode])) { + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */ + $attribute = $attributeList[$attributeCode]; + $attributeType = $attribute->getBackendType(); + // For non-numberic types set the attributeValue to 'false' to trigger their removal from the db + if ($attributeType === 'varchar' || $attributeType === 'text' || $attributeType === 'datetime') { + $attribute->setIsRequired(false); + $productData[$attributeCode] = false; + } else { + $productData[$attributeCode] = null; } } @@ -46,9 +92,9 @@ public function prepareProductAttributes(Product $product, array $productData, a * @param $value * @return bool */ - private function isAttributeShouldNotBeUpdated(Product $product, $useDefaults, $attribute, $value) : bool + private function isAttributeShouldNotBeUpdated(Product $product, $useDefaults, $attribute, $value): bool { - $considerUseDefaultsAttribute = !isset($useDefaults[$attribute]) || $useDefaults[$attribute] === "1"; + $considerUseDefaultsAttribute = !isset($useDefaults[$attribute]) || $useDefaults[$attribute] === '1'; return ($value === '' && $considerUseDefaultsAttribute && !$product->getData($attribute)); } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilterTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilterTest.php index 28617addc6d27..6e25d02df53ee 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilterTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilterTest.php @@ -3,10 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\Initialization\Helper; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use PHPUnit_Framework_MockObject_MockObject; class AttributeFilterTest extends \PHPUnit\Framework\TestCase { @@ -16,12 +19,12 @@ class AttributeFilterTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $objectManagerMock; /** - * @var Product|\PHPUnit_Framework_MockObject_MockObject + * @var Product|PHPUnit_Framework_MockObject_MockObject */ protected $productMock; @@ -44,15 +47,25 @@ public function testPrepareProductAttributes( $expectedProductData, $initialProductData ) { + /** @var PHPUnit_Framework_MockObject_MockObject | Product $productMockMap */ $productMockMap = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['getData']) + ->setMethods(['getData', 'getAttributes']) ->getMock(); if (!empty($initialProductData)) { $productMockMap->expects($this->any())->method('getData')->willReturnMap($initialProductData); } + if ($useDefaults) { + $productMockMap + ->expects($this->once()) + ->method('getAttributes') + ->willReturn( + $this->getProductAttributesMock($useDefaults) + ); + } + $actualProductData = $this->model->prepareProductAttributes($productMockMap, $requestProductData, $useDefaults); $this->assertEquals($expectedProductData, $actualProductData); } @@ -69,15 +82,15 @@ public function setupInputDataProvider() 'name' => 'testName', 'sku' => 'testSku', 'price' => '100', - 'description' => '' + 'description' => '', ], 'useDefaults' => [], 'expectedProductData' => [ 'name' => 'testName', 'sku' => 'testSku', - 'price' => '100' + 'price' => '100', ], - 'initialProductData' => [] + 'initialProductData' => [], ], 'update_product_without_use_defaults' => [ 'productData' => [ @@ -85,21 +98,21 @@ public function setupInputDataProvider() 'sku' => 'testSku2', 'price' => '101', 'description' => '', - 'special_price' => null + 'special_price' => null, ], 'useDefaults' => [], 'expectedProductData' => [ 'name' => 'testName2', 'sku' => 'testSku2', 'price' => '101', - 'special_price' => null + 'special_price' => null, ], 'initialProductData' => [ ['name', 'testName2'], ['sku', 'testSku2'], ['price', '101'], - ['special_price', null] - ] + ['special_price', null], + ], ], 'update_product_without_use_defaults_2' => [ 'productData' => [ @@ -107,7 +120,7 @@ public function setupInputDataProvider() 'sku' => 'testSku2', 'price' => '101', 'description' => 'updated description', - 'special_price' => null + 'special_price' => null, ], 'useDefaults' => [], 'expectedProductData' => [ @@ -115,14 +128,14 @@ public function setupInputDataProvider() 'sku' => 'testSku2', 'price' => '101', 'description' => 'updated description', - 'special_price' => null + 'special_price' => null, ], 'initialProductData' => [ ['name', 'testName2'], ['sku', 'testSku2'], ['price', '101'], - ['special_price', null] - ] + ['special_price', null], + ], ], 'update_product_with_use_defaults' => [ 'productData' => [ @@ -130,25 +143,25 @@ public function setupInputDataProvider() 'sku' => 'testSku2', 'price' => '101', 'description' => '', - 'special_price' => null + 'special_price' => null, ], 'useDefaults' => [ - 'description' => '0' + 'description' => '0', ], 'expectedProductData' => [ 'name' => 'testName2', 'sku' => 'testSku2', 'price' => '101', 'special_price' => null, - 'description' => '' + 'description' => '', ], 'initialProductData' => [ ['name', 'testName2'], ['sku', 'testSku2'], ['price', '101'], ['special_price', null], - ['description', 'descr text'] - ] + ['description', 'descr text'], + ], ], 'update_product_with_use_defaults_2' => [ 'requestProductData' => [ @@ -156,48 +169,73 @@ public function setupInputDataProvider() 'sku' => 'testSku3', 'price' => '103', 'description' => 'descr modified', - 'special_price' => '100' + 'special_price' => '100', ], 'useDefaults' => [ - 'description' => '0' + 'description' => '0', ], 'expectedProductData' => [ 'name' => 'testName3', 'sku' => 'testSku3', 'price' => '103', 'special_price' => '100', - 'description' => 'descr modified' + 'description' => 'descr modified', ], 'initialProductData' => [ - ['name', null,'testName2'], + ['name', null, 'testName2'], ['sku', null, 'testSku2'], ['price', null, '101'], - ['description', null, 'descr text'] - ] + ['description', null, 'descr text'], + ], ], 'update_product_with_use_defaults_3' => [ 'requestProductData' => [ 'name' => 'testName3', 'sku' => 'testSku3', 'price' => '103', - 'special_price' => '100' + 'special_price' => '100', + 'description' => 'descr modified', ], 'useDefaults' => [ - 'description' => '1' + 'description' => '1', ], 'expectedProductData' => [ 'name' => 'testName3', 'sku' => 'testSku3', 'price' => '103', 'special_price' => '100', + 'description' => false, ], 'initialProductData' => [ - ['name', null,'testName2'], + ['name', null, 'testName2'], ['sku', null, 'testSku2'], ['price', null, '101'], - ['description', null, 'descr text'] - ] + ['description', null, 'descr text'], + ], ], ]; } + + /** + * @param array $useDefaults + * @return array + */ + private function getProductAttributesMock(array $useDefaults): array + { + $returnArray = []; + foreach ($useDefaults as $attributecode => $isDefault) { + if ($isDefault === '1') { + /** @var Attribute | PHPUnit_Framework_MockObject_MockObject $attribute */ + $attribute = $this->getMockBuilder(Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $attribute->expects($this->any()) + ->method('getBackendType') + ->willReturn('varchar'); + + $returnArray[$attributecode] = $attribute; + } + } + return $returnArray; + } } From e71aa356ee43e9510706901065174444f42f1b8d Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Wed, 30 May 2018 10:58:49 +0300 Subject: [PATCH 245/333] MAGETWO-90349: Template file 'header.html' is not found. --- app/code/Magento/Email/Model/Template/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Email/Model/Template/Config.php b/app/code/Magento/Email/Model/Template/Config.php index d10c394dcb715..9b45f509d97e5 100644 --- a/app/code/Magento/Email/Model/Template/Config.php +++ b/app/code/Magento/Email/Model/Template/Config.php @@ -234,7 +234,7 @@ protected function _getInfo($templateId, $fieldName) /** * Retrieve template file path. - * + * * @param string $file * @param array $designParams * @param string $module From ea9c0f195600347f2e65031761260e8a7093fe31 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 30 May 2018 11:17:20 +0300 Subject: [PATCH 246/333] MAGETWO-90762: [2.3.0] Placing an order takes more time then usual --- .../Test/Unit/Model/GuestPaymentInformationManagementTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php index c4924ca211024..853ae0157e64a 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php @@ -168,7 +168,7 @@ public function testSavePaymentInformationAndPlaceOrderException() $adapterMockForCheckout->expects($this->once())->method('rollback'); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); - $exception = new \Exception(__('DB exception')); + $exception = new \Magento\Framework\Exception\CouldNotSaveException(__('DB exception')); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $email, $paymentMock, $billingAddressMock); From 6cc997ea2759c3039434395acef4960e665120bd Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Mon, 28 May 2018 20:34:17 +0200 Subject: [PATCH 247/333] [fix] PHPDoc and type hints --- app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php | 2 +- .../Catalog/Block/Adminhtml/Category/Widget/Chooser.php | 2 +- .../Catalog/Block/Adminhtml/Form/Renderer/Fieldset/Element.php | 2 +- .../Magento/Catalog/Block/Adminhtml/Product/Attribute/Grid.php | 3 +-- .../Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php | 2 +- app/code/Magento/Catalog/Block/Adminhtml/Rss/Grid/Link.php | 2 +- app/code/Magento/Catalog/Block/Category/Rss/Link.php | 2 +- app/code/Magento/Catalog/Block/Product/AbstractProduct.php | 2 +- .../Catalog/Block/Product/View/Options/AbstractOptions.php | 2 +- app/code/Magento/Catalog/Block/Rss/Product/Special.php | 2 +- 10 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php index 4b0d233d3e77b..34da5bb1d4ca1 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php @@ -325,7 +325,7 @@ public function getBreadcrumbsJavascript($path, $javascriptVarName) * * @param Node|array $node * @param int $level - * @return string + * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Widget/Chooser.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Widget/Chooser.php index 5e98313f95f0f..b5330ab66af71 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Widget/Chooser.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Widget/Chooser.php @@ -144,7 +144,7 @@ function (node, e) { * * @param \Magento\Framework\Data\Tree\Node|array $node * @param int $level - * @return string + * @return array */ protected function _getNodeJson($node, $level = 0) { diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Fieldset/Element.php b/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Fieldset/Element.php index 10214fc1d16fd..ad6df27b89334 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Fieldset/Element.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Fieldset/Element.php @@ -21,7 +21,7 @@ class Element extends \Magento\Backend\Block\Widget\Form\Renderer\Fieldset\Eleme /** * Retrieve data object related with form * - * @return \Magento\Catalog\Model\Product || \Magento\Catalog\Model\Category + * @return \Magento\Catalog\Model\Product|\Magento\Catalog\Model\Category */ public function getDataObject() { diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Grid.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Grid.php index ab5026b1e69b9..66e04ef03f771 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Grid.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Grid.php @@ -101,8 +101,7 @@ protected function _prepareColumns() 'type' => 'options', 'options' => ['1' => __('Yes'), '0' => __('No')], 'align' => 'center' - ], - 'is_user_defined' + ] ); $this->_eventManager->dispatch('product_attribute_grid_build', ['grid' => $this]); diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php index d5f66231f1d82..e1b97f996c769 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php @@ -81,7 +81,7 @@ public function getSelectorOptions() * * @param string $labelPart * @param int $templateId - * @return \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection + * @return array */ public function getSuggestedAttributes($labelPart, $templateId = null) { diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Rss/Grid/Link.php b/app/code/Magento/Catalog/Block/Adminhtml/Rss/Grid/Link.php index dbeff93683bc0..9d13d89d54b80 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Rss/Grid/Link.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Rss/Grid/Link.php @@ -69,7 +69,7 @@ public function isRssAllowed() } /** - * @return string + * @return array */ protected function getLinkParams() { diff --git a/app/code/Magento/Catalog/Block/Category/Rss/Link.php b/app/code/Magento/Catalog/Block/Category/Rss/Link.php index 0599d5f4b989c..e40b81200574c 100644 --- a/app/code/Magento/Catalog/Block/Category/Rss/Link.php +++ b/app/code/Magento/Catalog/Block/Category/Rss/Link.php @@ -62,7 +62,7 @@ public function getLabel() } /** - * @return string + * @return array */ protected function getLinkParams() { diff --git a/app/code/Magento/Catalog/Block/Product/AbstractProduct.php b/app/code/Magento/Catalog/Block/Product/AbstractProduct.php index f22edd91e7914..4102c82a0a316 100644 --- a/app/code/Magento/Catalog/Block/Product/AbstractProduct.php +++ b/app/code/Magento/Catalog/Block/Product/AbstractProduct.php @@ -195,7 +195,7 @@ public function getAddToCompareUrl() * Gets minimal sales quantity * * @param \Magento\Catalog\Model\Product $product - * @return int|null + * @return float|null */ public function getMinimalQty($product) { 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 66bf5eafb156e..d582005f653ef 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php @@ -178,7 +178,7 @@ public function getPrice($price, $includingTax = null) * Returns price converted to current currency rate * * @param float $price - * @return float + * @return float|string */ public function getCurrencyPrice($price) { diff --git a/app/code/Magento/Catalog/Block/Rss/Product/Special.php b/app/code/Magento/Catalog/Block/Rss/Product/Special.php index c61bee4417cbc..a9107f14cc5e4 100644 --- a/app/code/Magento/Catalog/Block/Rss/Product/Special.php +++ b/app/code/Magento/Catalog/Block/Rss/Product/Special.php @@ -107,7 +107,7 @@ protected function _construct() } /** - * @return string + * @return array */ public function getRssData() { From 026da34ff1b8eaa069a316e9c84dcbf1811f34b1 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 30 May 2018 12:10:31 +0300 Subject: [PATCH 248/333] MAGETWO-89991: Change default AVS and CVV codes for Signifyd --- .../Magento/Braintree/Model/AvsEmsCodeMapper.php | 2 +- .../Test/Unit/Model/AvsEmsCodeMapperTest.php | 10 +++++----- .../Paypal/Model/Payflow/AvsEmsCodeMapper.php | 2 +- .../Unit/Model/Payflow/AvsEmsCodeMapperTest.php | 14 +++++++------- .../Signifyd/Model/PaymentVerificationFactory.php | 4 ++-- .../SignifydGateway/Debugger/DebuggerFactory.php | 2 +- .../SignifydGateway/Request/PurchaseBuilder.php | 6 +++++- app/code/Magento/Signifyd/etc/di.xml | 6 +----- .../Signifyd/Test/Repository/SignifydData.xml | 1 - .../Request/CreateCaseBuilderTest.php | 2 +- 10 files changed, 24 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Braintree/Model/AvsEmsCodeMapper.php b/app/code/Magento/Braintree/Model/AvsEmsCodeMapper.php index 1d5057d83d6cf..f9fae8a469b1d 100644 --- a/app/code/Magento/Braintree/Model/AvsEmsCodeMapper.php +++ b/app/code/Magento/Braintree/Model/AvsEmsCodeMapper.php @@ -24,7 +24,7 @@ class AvsEmsCodeMapper implements PaymentVerificationInterface * * @var string */ - private static $unavailableCode = 'U'; + private static $unavailableCode = ''; /** * List of mapping AVS codes diff --git a/app/code/Magento/Braintree/Test/Unit/Model/AvsEmsCodeMapperTest.php b/app/code/Magento/Braintree/Test/Unit/Model/AvsEmsCodeMapperTest.php index 9b80a2237a8fb..c82634d36db31 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/AvsEmsCodeMapperTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/AvsEmsCodeMapperTest.php @@ -84,11 +84,11 @@ public function testGetCodeWithException() public function getCodeDataProvider() { return [ - ['avsZip' => null, 'avsStreet' => null, 'expected' => 'U'], - ['avsZip' => null, 'avsStreet' => 'M', 'expected' => 'U'], - ['avsZip' => 'M', 'avsStreet' => null, 'expected' => 'U'], - ['avsZip' => 'M', 'avsStreet' => 'Unknown', 'expected' => 'U'], - ['avsZip' => 'I', 'avsStreet' => 'A', 'expected' => 'U'], + ['avsZip' => null, 'avsStreet' => null, 'expected' => ''], + ['avsZip' => null, 'avsStreet' => 'M', 'expected' => ''], + ['avsZip' => 'M', 'avsStreet' => null, 'expected' => ''], + ['avsZip' => 'M', 'avsStreet' => 'Unknown', 'expected' => ''], + ['avsZip' => 'I', 'avsStreet' => 'A', 'expected' => ''], ['avsZip' => 'M', 'avsStreet' => 'M', 'expected' => 'Y'], ['avsZip' => 'N', 'avsStreet' => 'M', 'expected' => 'A'], ['avsZip' => 'M', 'avsStreet' => 'N', 'expected' => 'Z'], diff --git a/app/code/Magento/Paypal/Model/Payflow/AvsEmsCodeMapper.php b/app/code/Magento/Paypal/Model/Payflow/AvsEmsCodeMapper.php index 661d1f3814a0b..1ec7f4832bcb2 100644 --- a/app/code/Magento/Paypal/Model/Payflow/AvsEmsCodeMapper.php +++ b/app/code/Magento/Paypal/Model/Payflow/AvsEmsCodeMapper.php @@ -24,7 +24,7 @@ class AvsEmsCodeMapper implements PaymentVerificationInterface * * @var string */ - private static $unavailableCode = 'U'; + private static $unavailableCode = ''; /** * List of mapping AVS codes diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/AvsEmsCodeMapperTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/AvsEmsCodeMapperTest.php index eb259043a2d4f..ea86a04206f7b 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/AvsEmsCodeMapperTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/AvsEmsCodeMapperTest.php @@ -85,17 +85,17 @@ public function testGetCodeWithException() public function getCodeDataProvider() { return [ - ['avsZip' => null, 'avsStreet' => null, 'expected' => 'U'], - ['avsZip' => null, 'avsStreet' => 'Y', 'expected' => 'U'], - ['avsZip' => 'Y', 'avsStreet' => null, 'expected' => 'U'], + ['avsZip' => null, 'avsStreet' => null, 'expected' => ''], + ['avsZip' => null, 'avsStreet' => 'Y', 'expected' => ''], + ['avsZip' => 'Y', 'avsStreet' => null, 'expected' => ''], ['avsZip' => 'Y', 'avsStreet' => 'Y', 'expected' => 'Y'], ['avsZip' => 'N', 'avsStreet' => 'Y', 'expected' => 'A'], ['avsZip' => 'Y', 'avsStreet' => 'N', 'expected' => 'Z'], ['avsZip' => 'N', 'avsStreet' => 'N', 'expected' => 'N'], - ['avsZip' => 'X', 'avsStreet' => 'Y', 'expected' => 'U'], - ['avsZip' => 'N', 'avsStreet' => 'X', 'expected' => 'U'], - ['avsZip' => '', 'avsStreet' => 'Y', 'expected' => 'U'], - ['avsZip' => 'N', 'avsStreet' => '', 'expected' => 'U'] + ['avsZip' => 'X', 'avsStreet' => 'Y', 'expected' => ''], + ['avsZip' => 'N', 'avsStreet' => 'X', 'expected' => ''], + ['avsZip' => '', 'avsStreet' => 'Y', 'expected' => ''], + ['avsZip' => 'N', 'avsStreet' => '', 'expected' => ''] ]; } } diff --git a/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php b/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php index a26beda520944..5be5ccbc5e55a 100644 --- a/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php +++ b/app/code/Magento/Signifyd/Model/PaymentVerificationFactory.php @@ -60,7 +60,7 @@ public function __construct( * * @param string $paymentCode * @return PaymentVerificationInterface - * @throws \Exception + * @throws ConfigurationMismatchException */ public function createPaymentCvv($paymentCode) { @@ -73,7 +73,7 @@ public function createPaymentCvv($paymentCode) * * @param string $paymentCode * @return PaymentVerificationInterface - * @throws \Exception + * @throws ConfigurationMismatchException */ public function createPaymentAvs($paymentCode) { diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php index 02031e6f5b9b5..19408e99ae02e 100644 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php +++ b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php @@ -30,7 +30,7 @@ class DebuggerFactory /** * DebuggerFactory constructor. * - * @param bjectManagerInterface $objectManager + * @param ObjectManagerInterface $objectManager * @param Config $config */ public function __construct( diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php index 858ce0f0f3287..5e544e4b4048e 100644 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php +++ b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/PurchaseBuilder.php @@ -7,12 +7,13 @@ use Magento\Framework\App\Area; use Magento\Framework\Config\ScopeInterface; +use Magento\Framework\Exception\ConfigurationMismatchException; use Magento\Framework\Intl\DateTimeFactory; use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order; +use Magento\Signifyd\Model\PaymentMethodMapper\PaymentMethodMapper; use Magento\Signifyd\Model\PaymentVerificationFactory; use Magento\Signifyd\Model\SignifydOrderSessionId; -use Magento\Signifyd\Model\PaymentMethodMapper\PaymentMethodMapper; /** * Prepare data related to purchase event represented in case creation request. @@ -72,6 +73,7 @@ public function __construct( * * @param Order $order * @return array + * @throws ConfigurationMismatchException */ public function build(Order $order) { @@ -202,6 +204,7 @@ private function getOrderChannel() * * @param OrderPaymentInterface $orderPayment * @return string + * @throws ConfigurationMismatchException */ private function getAvsCode(OrderPaymentInterface $orderPayment) { @@ -214,6 +217,7 @@ private function getAvsCode(OrderPaymentInterface $orderPayment) * * @param OrderPaymentInterface $orderPayment * @return string + * @throws ConfigurationMismatchException */ private function getCvvCode(OrderPaymentInterface $orderPayment) { diff --git a/app/code/Magento/Signifyd/etc/di.xml b/app/code/Magento/Signifyd/etc/di.xml index 92ad8a0bfd87a..fd78fff27f619 100644 --- a/app/code/Magento/Signifyd/etc/di.xml +++ b/app/code/Magento/Signifyd/etc/di.xml @@ -15,11 +15,7 @@ <preference for="Magento\Signifyd\Api\GuaranteeCancelingServiceInterface" type="Magento\Signifyd\Model\Guarantee\CancelingService" /> <preference for="Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface" type="Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilder" /> - <virtualType name="SignifydAvsDefaultMapper" type="Magento\Signifyd\Model\PredefinedVerificationCode"> - <arguments> - <argument name="code" xsi:type="string">U</argument> - </arguments> - </virtualType> + <virtualType name="SignifydAvsDefaultMapper" type="Magento\Signifyd\Model\PredefinedVerificationCode" /> <virtualType name="SignifydCvvDefaultMapper" type="Magento\Signifyd\Model\PredefinedVerificationCode" /> <type name="Magento\Signifyd\Model\PaymentVerificationFactory"> diff --git a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml index 5b3be4b9d570a..02037534b0149 100644 --- a/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml +++ b/dev/tests/functional/tests/app/Magento/Signifyd/Test/Repository/SignifydData.xml @@ -27,7 +27,6 @@ <field name="team" xsi:type="string">autotest</field> <field name="caseFlag" xsi:type="string">Bad</field> <field name="guaranteeDisposition" xsi:type="string">Declined</field> - <field name="avsResponse" xsi:type="string">Unavailable (U)</field> <field name="shippingPrice" xsi:type="string">GBP 10.00</field> </dataset> </repository> diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php index e53e0c26e286a..594c065185ac7 100644 --- a/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php +++ b/dev/tests/integration/testsuite/Magento/Signifyd/Model/SignifydGateway/Request/CreateCaseBuilderTest.php @@ -86,7 +86,7 @@ public function testCreateCaseBuilderWithFullSetOfData() 'paymentGateway' => 'paypal_account', 'transactionId' => $payment->getLastTransId(), 'currency' => $order->getOrderCurrencyCode(), - 'avsResponseCode' => 'U', + 'avsResponseCode' => '', 'cvvResponseCode' => '', 'orderChannel' => 'WEB', 'totalPrice' => $order->getGrandTotal(), From d6aabc3f7a115627d7c1c766e76045d5390b8588 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 30 May 2018 12:27:20 +0300 Subject: [PATCH 249/333] Update MagentoStyle.php --- setup/src/Magento/Setup/Console/Style/MagentoStyle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Console/Style/MagentoStyle.php b/setup/src/Magento/Setup/Console/Style/MagentoStyle.php index e709d9158a049..cd2d4a77db65a 100755 --- a/setup/src/Magento/Setup/Console/Style/MagentoStyle.php +++ b/setup/src/Magento/Setup/Console/Style/MagentoStyle.php @@ -519,7 +519,7 @@ private function autoPrependText() } /** - * @param $messages + * @param array $messages * @return array */ private function reduceBuffer($messages) From aa41f77d390917562f227ae80dc9038630e82594 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Wed, 30 May 2018 13:01:14 +0300 Subject: [PATCH 250/333] MAGETWO-92134: [2.2.x] Payment Failed Email is not generated --- .../Magento/Authorizenet/Model/Directpost.php | 6 +- .../Checkout/Test/Unit/Helper/DataTest.php | 60 +++++++++---------- app/code/Magento/Paypal/Model/Payflowpro.php | 1 - .../Model/Service/PaymentFailuresService.php | 13 ++-- .../Magento/Paypal/Controller/PayflowTest.php | 4 +- .../Paypal/Controller/PayflowadvancedTest.php | 8 +-- .../Service/PaymentFailuresServiceTest.php | 2 +- 7 files changed, 46 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php index de567a8895f7e..5476fd05a0fed 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ b/app/code/Magento/Authorizenet/Model/Directpost.php @@ -143,8 +143,8 @@ class Directpost extends \Magento\Authorizenet\Model\Authorizenet implements Tra * @param \Magento\Framework\Module\ModuleListInterface $moduleList * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Magento\Authorizenet\Helper\Data $dataHelper - * @param Directpost\Request\Factory $requestFactory - * @param Directpost\Response\Factory $responseFactory + * @param \Magento\Authorizenet\Model\Directpost\Request\Factory $requestFactory + * @param \Magento\Authorizenet\Model\Directpost\Response\Factory $responseFactory * @param \Magento\Authorizenet\Model\TransactionService $transactionService * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory * @param \Magento\Sales\Model\OrderFactory $orderFactory @@ -171,7 +171,7 @@ public function __construct( \Magento\Authorizenet\Helper\Data $dataHelper, \Magento\Authorizenet\Model\Directpost\Request\Factory $requestFactory, \Magento\Authorizenet\Model\Directpost\Response\Factory $responseFactory, - TransactionService $transactionService, + \Magento\Authorizenet\Model\TransactionService $transactionService, \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory, \Magento\Sales\Model\OrderFactory $orderFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, diff --git a/app/code/Magento/Checkout/Test/Unit/Helper/DataTest.php b/app/code/Magento/Checkout/Test/Unit/Helper/DataTest.php index f6f9ff78c1cb2..53132ffaa748b 100644 --- a/app/code/Magento/Checkout/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Helper/DataTest.php @@ -23,32 +23,32 @@ class DataTest extends \PHPUnit\Framework\TestCase /** * @var Data */ - private $_helper; + private $helper; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $_transportBuilder; + private $transportBuilder; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $_translator; + private $translator; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $_checkoutSession; + private $checkoutSession; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $_scopeConfig; + private $scopeConfig; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $_eventManager; + private $eventManager; /** * @inheritdoc @@ -60,10 +60,10 @@ protected function setUp() $arguments = $objectManagerHelper->getConstructArguments($className); /** @var \Magento\Framework\App\Helper\Context $context */ $context = $arguments['context']; - $this->_translator = $arguments['inlineTranslation']; - $this->_eventManager = $context->getEventManager(); - $this->_scopeConfig = $context->getScopeConfig(); - $this->_scopeConfig->expects($this->any()) + $this->translator = $arguments['inlineTranslation']; + $this->eventManager = $context->getEventManager(); + $this->scopeConfig = $context->getScopeConfig(); + $this->scopeConfig->expects($this->any()) ->method('getValue') ->willReturnMap( [ @@ -118,16 +118,16 @@ protected function setUp() ] ); - $this->_checkoutSession = $arguments['checkoutSession']; + $this->checkoutSession = $arguments['checkoutSession']; $arguments['localeDate']->expects($this->any()) ->method('formatDateTime') ->willReturn('Oct 02, 2013'); - $this->_transportBuilder = $arguments['transportBuilder']; + $this->transportBuilder = $arguments['transportBuilder']; $this->priceCurrency = $arguments['priceCurrency']; - $this->_helper = $objectManagerHelper->getObject($className, $arguments); + $this->helper = $objectManagerHelper->getObject($className, $arguments); } /** @@ -141,7 +141,7 @@ public function testSendPaymentFailedEmail() ->getMock(); $quoteMock->expects($this->any())->method('getId')->willReturn(1); - $this->assertSame($this->_helper, $this->_helper->sendPaymentFailedEmail($quoteMock, 'test message')); + $this->assertSame($this->helper, $this->helper->sendPaymentFailedEmail($quoteMock, 'test message')); } /** @@ -149,14 +149,14 @@ public function testSendPaymentFailedEmail() */ public function testGetCheckout() { - $this->assertEquals($this->_checkoutSession, $this->_helper->getCheckout()); + $this->assertEquals($this->checkoutSession, $this->helper->getCheckout()); } public function testGetQuote() { $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); - $this->_checkoutSession->expects($this->once())->method('getQuote')->will($this->returnValue($quoteMock)); - $this->assertEquals($quoteMock, $this->_helper->getQuote()); + $this->checkoutSession->expects($this->once())->method('getQuote')->will($this->returnValue($quoteMock)); + $this->assertEquals($quoteMock, $this->helper->getQuote()); } public function testFormatPrice() @@ -164,26 +164,26 @@ public function testFormatPrice() $price = 5.5; $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); $storeMock = $this->createPartialMock(\Magento\Store\Model\Store::class, ['formatPrice', '__wakeup']); - $this->_checkoutSession->expects($this->once())->method('getQuote')->will($this->returnValue($quoteMock)); + $this->checkoutSession->expects($this->once())->method('getQuote')->will($this->returnValue($quoteMock)); $quoteMock->expects($this->once())->method('getStore')->will($this->returnValue($storeMock)); $this->priceCurrency->expects($this->once())->method('format')->will($this->returnValue('5.5')); - $this->assertEquals('5.5', $this->_helper->formatPrice($price)); + $this->assertEquals('5.5', $this->helper->formatPrice($price)); } public function testConvertPrice() { $price = 5.5; $this->priceCurrency->expects($this->once())->method('convertAndFormat')->willReturn($price); - $this->assertEquals(5.5, $this->_helper->convertPrice($price)); + $this->assertEquals(5.5, $this->helper->convertPrice($price)); } public function testCanOnepageCheckout() { - $this->_scopeConfig->expects($this->once())->method('getValue')->with( + $this->scopeConfig->expects($this->once())->method('getValue')->with( 'checkout/options/onepage_checkout_enabled', 'store' )->will($this->returnValue(true)); - $this->assertTrue($this->_helper->canOnepageCheckout()); + $this->assertTrue($this->helper->canOnepageCheckout()); } public function testIsContextCheckout() @@ -204,18 +204,18 @@ public function testIsContextCheckout() public function testIsCustomerMustBeLogged() { - $this->_scopeConfig->expects($this->once())->method('isSetFlag')->with( + $this->scopeConfig->expects($this->once())->method('isSetFlag')->with( 'checkout/options/customer_must_be_logged', \Magento\Store\Model\ScopeInterface::SCOPE_STORE )->will($this->returnValue(true)); - $this->assertTrue($this->_helper->isCustomerMustBeLogged()); + $this->assertTrue($this->helper->isCustomerMustBeLogged()); } public function testGetPriceInclTax() { $itemMock = $this->createPartialMock(\Magento\Framework\DataObject::class, ['getPriceInclTax']); $itemMock->expects($this->exactly(2))->method('getPriceInclTax')->will($this->returnValue(5.5)); - $this->assertEquals(5.5, $this->_helper->getPriceInclTax($itemMock)); + $this->assertEquals(5.5, $this->helper->getPriceInclTax($itemMock)); } public function testGetPriceInclTaxWithoutTax() @@ -256,7 +256,7 @@ public function testGetSubtotalInclTax() $expected = 5.5; $itemMock = $this->createPartialMock(\Magento\Framework\DataObject::class, ['getRowTotalInclTax']); $itemMock->expects($this->exactly(2))->method('getRowTotalInclTax')->will($this->returnValue($rowTotalInclTax)); - $this->assertEquals($expected, $this->_helper->getSubtotalInclTax($itemMock)); + $this->assertEquals($expected, $this->helper->getSubtotalInclTax($itemMock)); } public function testGetSubtotalInclTaxNegative() @@ -274,7 +274,7 @@ public function testGetSubtotalInclTaxNegative() $itemMock->expects($this->once()) ->method('getDiscountTaxCompensation')->will($this->returnValue($discountTaxCompensation)); $itemMock->expects($this->once())->method('getRowTotal')->will($this->returnValue($rowTotal)); - $this->assertEquals($expected, $this->_helper->getSubtotalInclTax($itemMock)); + $this->assertEquals($expected, $this->helper->getSubtotalInclTax($itemMock)); } public function testGetBasePriceInclTaxWithoutQty() @@ -321,7 +321,7 @@ public function testGetBaseSubtotalInclTax() $itemMock->expects($this->once())->method('getBaseTaxAmount'); $itemMock->expects($this->once())->method('getBaseDiscountTaxCompensation'); $itemMock->expects($this->once())->method('getBaseRowTotal'); - $this->_helper->getBaseSubtotalInclTax($itemMock); + $this->helper->getBaseSubtotalInclTax($itemMock); } public function testIsAllowedGuestCheckoutWithoutStore() @@ -329,9 +329,9 @@ public function testIsAllowedGuestCheckoutWithoutStore() $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); $store = null; $quoteMock->expects($this->once())->method('getStoreId')->will($this->returnValue(1)); - $this->_scopeConfig->expects($this->once()) + $this->scopeConfig->expects($this->once()) ->method('isSetFlag') ->will($this->returnValue(true)); - $this->assertTrue($this->_helper->isAllowedGuestCheckout($quoteMock, $store)); + $this->assertTrue($this->helper->isAllowedGuestCheckout($quoteMock, $store)); } } diff --git a/app/code/Magento/Paypal/Model/Payflowpro.php b/app/code/Magento/Paypal/Model/Payflowpro.php index 16ba4efc90d6a..b5fdaf4ae9fd4 100644 --- a/app/code/Magento/Paypal/Model/Payflowpro.php +++ b/app/code/Magento/Paypal/Model/Payflowpro.php @@ -647,7 +647,6 @@ public function buildBasicRequest() * * @param DataObject $response * @return void - * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Payment\Gateway\Command\CommandException * @throws \Magento\Framework\Exception\State\InvalidTransitionException */ diff --git a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php index 2e8da0dadd5b1..6da1dc1b7e7cc 100644 --- a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php +++ b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php @@ -189,10 +189,12 @@ private function getConfigValue(string $configPath, Quote $quote) * @param Quote $quote * @return string */ - private function getShippingMethod(Quote $quote) + private function getShippingMethod(Quote $quote): string { $shippingMethod = ''; - if ($shippingInfo = $quote->getShippingAddress()->getShippingMethod()) { + $shippingInfo = $quote->getShippingAddress()->getShippingMethod(); + + if ($shippingInfo) { $data = explode('_', $shippingInfo); $shippingMethod = $data[0]; } @@ -206,12 +208,9 @@ private function getShippingMethod(Quote $quote) * @param Quote $quote * @return string */ - private function getPaymentMethod(Quote $quote) + private function getPaymentMethod(Quote $quote): string { - $paymentMethod = ''; - if ($paymentInfo = $quote->getPayment()) { - $paymentMethod = $paymentInfo->getMethod(); - } + $paymentMethod = $quote->getPayment()->getMethod() ?? ''; return $paymentMethod; } diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowTest.php index 367f68ce25872..670cc6850b233 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowTest.php @@ -63,7 +63,7 @@ protected function setUp() $this->order->getPayment()->setMethod(Config::METHOD_PAYFLOWLINK); /** @var $quote \Magento\Quote\Model\Quote */ - $quote = $this->_objectManager->create(Quote::class)->setStoreid($this->order->getStoreid()); + $quote = $this->_objectManager->create(Quote::class)->setStoreid($this->order->getStoreId()); $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); $this->quoteRepository->save($quote); @@ -111,7 +111,7 @@ public function testFormActionIsContentGenerated() * @magentoConfigFixture current_store paypal/general/business_account merchant_2012050718_biz@example.com * @return void */ - public function testCancelAction() + public function testCancelAction(): void { $orderId = $this->order->getEntityId(); /** @var \Magento\Sales\Model\Order $order */ diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowadvancedTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowadvancedTest.php index 7996acc7ed3b0..bf404f74b769e 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowadvancedTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/PayflowadvancedTest.php @@ -60,11 +60,11 @@ protected function setUp() /** @var OrderInterface $order */ $this->order = array_pop($orders); - $this->order->getPayment()->setMethod(Config::METHOD_PAYFLOWLINK); + $this->order->getPayment()->setMethod(Config::METHOD_PAYFLOWADVANCED); /** @var $quote \Magento\Quote\Model\Quote */ $quote = $this->_objectManager->create(Quote::class) - ->setStoreid($this->order->getStoreid()); + ->setStoreid($this->order->getStoreId()); $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); $this->quoteRepository->save($quote); @@ -104,7 +104,7 @@ public function testFormActionIsContentGenerated() * @magentoConfigFixture current_store paypal/general/business_account merchant_2012050718_biz@example.com * @return void */ - public function testCancelAction() + public function testCancelAction(): void { $orderId = $this->order->getEntityId(); /** @var \Magento\Sales\Model\Order $order */ @@ -115,7 +115,7 @@ public function testCancelAction() $session = $this->_objectManager->get(Session::class); $session->setQuoteId($quote->getId()); $session->setPaypalStandardQuoteId($quote->getId())->setLastRealOrderId('100000001'); - $this->dispatch('paypal/payflow/cancelpayment'); + $this->dispatch('paypal/payflowadvanced/cancelpayment'); $order = $this->_objectManager->create(OrderRepositoryInterface::class)->get($orderId); $this->assertEquals('canceled', $order->getState()); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php index 1e334c9660d19..6367ff6d1a6d3 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Service/PaymentFailuresServiceTest.php @@ -59,7 +59,7 @@ protected function setUp() * @magentoAppIsolation enabled * @return void */ - public function testHandlerWithCustomer() + public function testHandlerWithCustomer(): void { $errorMessage = __('Transaction declined.'); $checkoutType = 'custom_checkout'; From 556bad21bfa142260f3dd9f7d0a2636c77067d06 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 30 May 2018 14:17:32 +0300 Subject: [PATCH 251/333] MAGETWO-89407: [2.3] PayPal Payments Pro settings gets from wrong store --- .../Service/Request/SecureTokenTest.php | 24 +++++++++++++------ .../_files/quote_payflowpro_rollback.php | 3 +++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php index df6a1aae3fdb8..2318edbb80b25 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Payflow/Service/Request/SecureTokenTest.php @@ -70,7 +70,7 @@ protected function setUp() $this->service = $this->objectManager->create( SecureToken::class, [ - 'mathRandom' => $this->mathRandom + 'mathRandom' => $this->mathRandom, ] ); } @@ -89,8 +89,9 @@ protected function tearDown() * @magentoDataFixture Magento/Paypal/_files/quote_payflowpro.php * @magentoDataFixture Magento/Paypal/Fixtures/default_payment_configuration.php * @magentoAppArea adminhtml + * @return void */ - public function testRequestToken() + public function testRequestToken(): void { $quote = $this->getQuote('100000015'); $quote->setStoreId(null); @@ -103,8 +104,9 @@ public function testRequestToken() * @magentoDataFixture Magento/Paypal/_files/quote_payflowpro.php * @magentoDataFixture Magento/Paypal/Fixtures/store_payment_configuration.php * @magentoAppArea adminhtml + * @return void */ - public function testRequestTokenWithStoreConfiguration() + public function testRequestTokenWithStoreConfiguration(): void { $quote = $this->getQuote('100000015'); $store = $this->getStore('test'); @@ -118,8 +120,9 @@ public function testRequestTokenWithStoreConfiguration() * @magentoDataFixture Magento/Paypal/_files/quote_payflowpro.php * @magentoDataFixture Magento/Paypal/Fixtures/website_payment_configuration.php * @magentoAppArea adminhtml + * @return void */ - public function testRequestTokenWithWebsiteConfiguration() + public function testRequestTokenWithWebsiteConfiguration(): void { $quote = $this->getQuote('100000015'); $store = $this->getStore('fixture_second_store'); @@ -135,9 +138,15 @@ public function testRequestTokenWithWebsiteConfiguration() * @param string $expVendor * @param string $expUser * @param string $expPwd + * @return void */ - private function execute(Quote $quote, string $expPartner, string $expVendor, string $expUser, string $expPwd) - { + private function execute( + Quote $quote, + string $expPartner, + string $expVendor, + string $expUser, + string $expPwd + ): void { $secureTokenId = '31f2a7c8d257c70b1c9eb9051b90e0'; $token = '80IgSbabyj0CtBDWHZZeQN3'; @@ -174,8 +183,9 @@ private function execute(Quote $quote, string $expPartner, string $expVendor, st * @param string $expected * @param string $actual * @param string $property + * @return void */ - private function performAssertion(string $expected, string $actual, string $property) + private function performAssertion(string $expected, string $actual, string $property): void { self::assertEquals($expected, $actual, "$property should match."); } diff --git a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php index 6eded10e5c555..32a76834dbf27 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/_files/quote_payflowpro_rollback.php @@ -45,3 +45,6 @@ $quote = array_pop($items); $quoteRepository->delete($quote); } + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From fdacd0d73579dccc8f945ba844a91cbf686b1f40 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Wed, 30 May 2018 10:42:40 -0500 Subject: [PATCH 252/333] MAGETWO-92260: GraphQL - Fix urlResolver query to support relative path #13 --- .../CmsUrlRewriteGraphQl/composer.json | 9 ++-- .../Magento/UrlRewriteGraphQl/composer.json | 46 +++++++++---------- .../Magento/Test/Integrity/ComposerTest.php | 2 +- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/CmsUrlRewriteGraphQl/composer.json b/app/code/Magento/CmsUrlRewriteGraphQl/composer.json index 1e0c836389340..c57e4cdc92a83 100644 --- a/app/code/Magento/CmsUrlRewriteGraphQl/composer.json +++ b/app/code/Magento/CmsUrlRewriteGraphQl/composer.json @@ -4,13 +4,14 @@ "type": "magento2-module", "require": { "php": "~7.1.3||~7.2.0", - "magento/framework": "*" - + "magento/framework": "*", + "magento/module-url-rewrite-graph-ql": "*", + "magento/module-store": "*", + "magento/module-cms": "*" }, "suggest": { "magento/module-cms-url-rewrite": "*", - "magento/module-catalog-graph-ql": "*", - "magento/module-url-rewrite-graph-ql": "*" + "magento/module-catalog-graph-ql": "*" }, "license": [ "OSL-3.0", diff --git a/app/code/Magento/UrlRewriteGraphQl/composer.json b/app/code/Magento/UrlRewriteGraphQl/composer.json index 8d5295a2389fa..1c99276269aa7 100644 --- a/app/code/Magento/UrlRewriteGraphQl/composer.json +++ b/app/code/Magento/UrlRewriteGraphQl/composer.json @@ -1,26 +1,26 @@ { - "name" : "magento/module-url-rewrite-graph-ql", - "description" : "N/A", - "type" : "magento2-module", - "require" : { - "php" : "~7.1.3||~7.2.0", - "magento/framework" : "*", - "magento/module-url-rewrite" : "*", + "name": "magento/module-url-rewrite-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-url-rewrite": "*", "magento/module-store": "*" - }, - "suggest" : { - "magento/module-graph-ql" : "*" - }, - "license" : [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload" : { - "files" : [ - "registration.php" - ], - "psr-4" : { - "Magento\\UrlRewriteGraphQl\\" : "" - } - } + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\UrlRewriteGraphQl\\": "" + } + } } diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index d671efe10fb6e..a42fe147303bb 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -144,7 +144,7 @@ private function validateComposerJsonFile($path) */ private function assertCodingStyle($contents) { - $this->assertNotRegExp('/" :\s*["{]/', $contents, 'Coding style: no space before colon.'); + $this->assertNotRegExp('/" :\s*["{]/', $contents, 'Coding style: there should be no space before colon.'); $this->assertNotRegExp('/":["{]/', $contents, 'Coding style: a space is necessary after colon.'); } From 1e2019ccb7f39c4158f77258cec6d52f32011d70 Mon Sep 17 00:00:00 2001 From: Brian LaBelle <labelle.brian.c@gmail.com> Date: Thu, 24 May 2018 10:40:38 -0500 Subject: [PATCH 253/333] Issue 15469: Javascript error dropdowns.js Fixes Javascript error in dropdowns.js by properly initializing the el variable. options.autoclose can now be set to false --- lib/web/mage/dropdowns.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/web/mage/dropdowns.js b/lib/web/mage/dropdowns.js index 6c1389924a24b..1496a1c65d957 100644 --- a/lib/web/mage/dropdowns.js +++ b/lib/web/mage/dropdowns.js @@ -127,11 +127,9 @@ define([ } elem.on('click.toggleDropdown', function () { - var el; + var el = actionElem; if (options.autoclose === true) { - el = actionElem; - actionElem = $(); $(document).trigger('click.hideDropdown'); actionElem = el; From 3b3dcd44914887e1b3f8f1e637647365f23c5562 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Wed, 30 May 2018 13:15:14 -0500 Subject: [PATCH 254/333] MAGETWO-92260: GraphQL - Fix urlResolver query to support relative path #13 --- ...UrlResolver.php => HomePageUrlLocator.php} | 8 ++-- .../Magento/CmsUrlRewriteGraphQl/etc/di.xml | 6 +-- .../Model/Resolver/UrlRewrite.php | 14 +++---- .../Resolver/UrlRewrite/CustomUrlLocator.php | 41 +++++++++++++++++++ ...face.php => CustomUrlLocatorInterface.php} | 4 +- .../Resolver/UrlRewrite/CustomUrlResolver.php | 41 ------------------- app/code/Magento/UrlRewriteGraphQl/etc/di.xml | 2 +- 7 files changed, 58 insertions(+), 58 deletions(-) rename app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/{HomePageUrlResolver.php => HomePageUrlLocator.php} (86%) create mode 100644 app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlLocator.php rename app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/{CustomUrlResolverInterface.php => CustomUrlLocatorInterface.php} (84%) delete mode 100644 app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolver.php diff --git a/app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlResolver.php b/app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlLocator.php similarity index 86% rename from app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlResolver.php rename to app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlLocator.php index d20323a485d71..6cc669e46d080 100644 --- a/app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlResolver.php +++ b/app/code/Magento/CmsUrlRewriteGraphQl/Model/Resolver/UrlRewrite/HomePageUrlLocator.php @@ -7,14 +7,14 @@ namespace Magento\CmsUrlRewriteGraphQl\Model\Resolver\UrlRewrite; -use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolverInterface; +use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocatorInterface; use Magento\Store\Model\ScopeInterface; use Magento\Cms\Helper\Page; /** - * Home page URL resolver. + * Home page URL locator. */ -class HomePageUrlResolver implements CustomUrlResolverInterface +class HomePageUrlLocator implements CustomUrlLocatorInterface { /** * @var \Magento\Framework\App\Config\ScopeConfigInterface @@ -33,7 +33,7 @@ public function __construct( /** * @inheritdoc */ - public function resolveUrl($urlKey): ?string + public function locateUrl($urlKey): ?string { if ($urlKey === '/') { $homePageUrl = $this->scopeConfig->getValue( diff --git a/app/code/Magento/CmsUrlRewriteGraphQl/etc/di.xml b/app/code/Magento/CmsUrlRewriteGraphQl/etc/di.xml index 8cd226b3fd258..d384c898acb62 100644 --- a/app/code/Magento/CmsUrlRewriteGraphQl/etc/di.xml +++ b/app/code/Magento/CmsUrlRewriteGraphQl/etc/di.xml @@ -6,10 +6,10 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolver"> + <type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocator"> <arguments> - <argument name="urlResolvers" xsi:type="array"> - <item name="homePageResolver" xsi:type="object">Magento\CmsUrlRewriteGraphQl\Model\Resolver\UrlRewrite\HomePageUrlResolver</item> + <argument name="urlLocators" xsi:type="array"> + <item name="homePageUrlLocator" xsi:type="object">Magento\CmsUrlRewriteGraphQl\Model\Resolver\UrlRewrite\HomePageUrlLocator</item> </argument> </arguments> </type> diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 0de21c81f7a8c..3f07edc601909 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -14,7 +14,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\UrlFinderInterface; -use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolverInterface; +use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocatorInterface; /** * UrlRewrite field resolver, used for GraphQL request processing. @@ -37,26 +37,26 @@ class UrlRewrite implements ResolverInterface private $valueFactory; /** - * @var UrlRewrite\CustomUrlResolverInterface + * @var CustomUrlLocatorInterface */ - private $customUrlResolver; + private $customUrlLocator; /** * @param UrlFinderInterface $urlFinder * @param StoreManagerInterface $storeManager * @param ValueFactory $valueFactory - * @param CustomUrlResolverInterface $customUrlResolver + * @param CustomUrlLocatorInterface $customUrlLocator */ public function __construct( UrlFinderInterface $urlFinder, StoreManagerInterface $storeManager, ValueFactory $valueFactory, - CustomUrlResolverInterface $customUrlResolver + CustomUrlLocatorInterface $customUrlLocator ) { $this->urlFinder = $urlFinder; $this->storeManager = $storeManager; $this->valueFactory = $valueFactory; - $this->customUrlResolver = $customUrlResolver; + $this->customUrlLocator = $customUrlLocator; } /** @@ -78,7 +78,7 @@ public function resolve( if (substr($url, 0, 1) === '/' && $url !== '/') { $url = ltrim($url, '/'); } - $customUrl = $this->customUrlResolver->resolveUrl($url); + $customUrl = $this->customUrlLocator->locateUrl($url); $url = $customUrl ?: $url; $urlRewrite = $this->findCanonicalUrl($url); if ($urlRewrite) { diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlLocator.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlLocator.php new file mode 100644 index 0000000000000..553c08e29137a --- /dev/null +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlLocator.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite; + +/** + * Pool of custom URL locators. + */ +class CustomUrlLocator implements CustomUrlLocatorInterface +{ + /** + * @var CustomUrlLocatorInterface[] + */ + private $urlLocators; + + /** + * @param CustomUrlLocatorInterface[] $urlLocators + */ + public function __construct(array $urlLocators = []) + { + $this->urlLocators = $urlLocators; + } + + /** + * @inheritdoc + */ + public function locateUrl($urlKey): ?string + { + foreach ($this->urlLocators as $urlLocator) { + $url = $urlLocator->locateUrl($urlKey); + if ($url !== null) { + return $url; + } + } + return null; + } +} diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolverInterface.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlLocatorInterface.php similarity index 84% rename from app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolverInterface.php rename to app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlLocatorInterface.php index 1aee63b670635..df0c0ee96c16c 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolverInterface.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlLocatorInterface.php @@ -12,7 +12,7 @@ * * It can be used, for example, to resolve '\' URL path to a 'Home' page. */ -interface CustomUrlResolverInterface +interface CustomUrlLocatorInterface { /** * Resolve URL based on custom rules. @@ -20,5 +20,5 @@ interface CustomUrlResolverInterface * @param string $urlKey * @return string|null Return null if URL cannot be resolved */ - public function resolveUrl($urlKey): ?string; + public function locateUrl($urlKey): ?string; } diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolver.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolver.php deleted file mode 100644 index 08efb54bd6bbb..0000000000000 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite/CustomUrlResolver.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite; - -/** - * Pool of custom URL resolvers. - */ -class CustomUrlResolver implements CustomUrlResolverInterface -{ - /** - * @var CustomUrlResolverInterface[] - */ - private $urlResolvers; - - /** - * @param CustomUrlResolverInterface[] $urlResolvers - */ - public function __construct(array $urlResolvers = []) - { - $this->urlResolvers = $urlResolvers; - } - - /** - * @inheritdoc - */ - public function resolveUrl($urlKey): ?string - { - foreach ($this->urlResolvers as $urlResolver) { - $url = $urlResolver->resolveUrl($urlKey); - if ($url !== null) { - return $url; - } - } - return null; - } -} diff --git a/app/code/Magento/UrlRewriteGraphQl/etc/di.xml b/app/code/Magento/UrlRewriteGraphQl/etc/di.xml index c7b1533962af0..a9865db4e97e8 100644 --- a/app/code/Magento/UrlRewriteGraphQl/etc/di.xml +++ b/app/code/Magento/UrlRewriteGraphQl/etc/di.xml @@ -6,5 +6,5 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolverInterface" type="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlResolver"/> + <preference for="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocatorInterface" type="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocator"/> </config> From a7138bc1b562ef7cbc156aa57f51a1c182cf3196 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Wed, 30 May 2018 13:51:22 -0500 Subject: [PATCH 255/333] ENGCOM-1663: Include 'products' in category query #48 --- .../Magento/GraphQl/Catalog/CategoryTest.php | 14 +------------ .../Catalog/_files/categories_indexed.php | 21 +++++++++++++++++++ .../_files/categories_indexed_rollback.php | 21 +++++++++++++++++++ 3 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 8fc19a21421c0..d82b15e3990e9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -115,23 +115,11 @@ public function testCategoriesTree() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @magentoApiDataFixture Magento/Catalog/_files/categories_indexed.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testCategoryProducts() { - /** @var \Magento\Catalog\Model\Indexer\Category\Product\Processor $categoryProductIndexer */ - $categoryProductIndexer = $this->objectManager->get( - \Magento\Catalog\Model\Indexer\Category\Product\Processor::class - ); - $categoryProductIndexer->reindexAll(); - - /** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor $inventoryIndexer */ - $inventoryIndexer = $this->objectManager->get( - \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class - ); - $inventoryIndexer->reindexAll(); - $categoryId = 4; $query = <<<QUERY { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php new file mode 100644 index 0000000000000..f76370978404d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +include __DIR__ . '/categories.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Catalog\Model\Indexer\Category\Product\Processor $categoryProductIndexer */ +$categoryProductIndexer = $objectManager->get( + \Magento\Catalog\Model\Indexer\Category\Product\Processor::class +); +$categoryProductIndexer->reindexAll(); + +/** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor $inventoryIndexer */ +$inventoryIndexer = $objectManager->get( + \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class +); +$inventoryIndexer->reindexAll(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php new file mode 100644 index 0000000000000..573fb7c3a2b11 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +include __DIR__ . '/categories_rollback.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Catalog\Model\Indexer\Category\Product\Processor $categoryProductIndexer */ +$categoryProductIndexer = $objectManager->get( + \Magento\Catalog\Model\Indexer\Category\Product\Processor::class +); +$categoryProductIndexer->reindexAll(); + +/** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor $inventoryIndexer */ +$inventoryIndexer = $objectManager->get( + \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class +); +$inventoryIndexer->reindexAll(); From 8e8d0d2e2351fd9b2dbf1937156b0264440c4394 Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Wed, 30 May 2018 19:31:18 -0500 Subject: [PATCH 256/333] ENGCOM-1663: Include 'products' in category query #48 --- .../Catalog/_files/categories_indexed.php | 19 ++++++++----------- .../_files/categories_indexed_rollback.php | 19 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php index f76370978404d..ff444813a9d07 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php @@ -3,19 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); include __DIR__ . '/categories.php'; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var \Magento\Catalog\Model\Indexer\Category\Product\Processor $categoryProductIndexer */ -$categoryProductIndexer = $objectManager->get( - \Magento\Catalog\Model\Indexer\Category\Product\Processor::class -); -$categoryProductIndexer->reindexAll(); - -/** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor $inventoryIndexer */ -$inventoryIndexer = $objectManager->get( - \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class -); -$inventoryIndexer->reindexAll(); +/** @var \Magento\Indexer\Model\Indexer\CollectionFactory $indexerCollectionFactory */ +$indexerCollectionFactory = $objectManager->get(\Magento\Indexer\Model\Indexer\CollectionFactory::class); +$indexerCollection = $indexerCollectionFactory->create(); +$indexers = $indexerCollection->getItems(); +foreach ($indexers as $indexer) { + $indexer->reindexAll(); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php index 573fb7c3a2b11..338a4b23bee59 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php @@ -3,19 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); include __DIR__ . '/categories_rollback.php'; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var \Magento\Catalog\Model\Indexer\Category\Product\Processor $categoryProductIndexer */ -$categoryProductIndexer = $objectManager->get( - \Magento\Catalog\Model\Indexer\Category\Product\Processor::class -); -$categoryProductIndexer->reindexAll(); - -/** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor $inventoryIndexer */ -$inventoryIndexer = $objectManager->get( - \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class -); -$inventoryIndexer->reindexAll(); +/** @var \Magento\Indexer\Model\Indexer\CollectionFactory $indexerCollectionFactory */ +$indexerCollectionFactory = $objectManager->get(\Magento\Indexer\Model\Indexer\CollectionFactory::class); +$indexerCollection = $indexerCollectionFactory->create(); +$indexers = $indexerCollection->getItems(); +foreach ($indexers as $indexer) { + $indexer->reindexAll(); +} From 18fe48c2ff20110c84ef13fd867a50d7ecd6b75e Mon Sep 17 00:00:00 2001 From: Alex Paliarush <apaliarush@magento.com> Date: Wed, 30 May 2018 20:25:15 -0500 Subject: [PATCH 257/333] ENGCOM-1663: Include 'products' in category query #48 --- .../Magento/GraphQl/Catalog/CategoryTest.php | 18 ++++++------------ .../Catalog/_files/categories_indexed.php | 18 ------------------ .../_files/categories_indexed_rollback.php | 18 ------------------ 3 files changed, 6 insertions(+), 48 deletions(-) delete mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php delete mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index d82b15e3990e9..223fa75006d45 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -114,13 +114,12 @@ public function testCategoriesTree() } /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Catalog/_files/categories_indexed.php + * @magentoApiDataFixture Magento/Catalog/_files/categories.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testCategoryProducts() { - $categoryId = 4; + $categoryId = 2; $query = <<<QUERY { category(id: {$categoryId}) { @@ -266,25 +265,21 @@ public function testCategoryProducts() $response = $this->graphQlQuery($query); $this->assertArrayHasKey('products', $response['category']); $this->assertArrayHasKey('total_count', $response['category']['products']); - $this->assertEquals(2, $response['category']['products']['total_count']); + $this->assertGreaterThanOrEqual(1, $response['category']['products']['total_count']); $this->assertEquals(1, $response['category']['products']['page_info']['current_page']); $this->assertEquals(20, $response['category']['products']['page_info']['page_size']); + $this->assertArrayHasKey('sku', $response['category']['products']['items'][0]); + $firstProductSku = $response['category']['products']['items'][0]['sku']; + /** * @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); - $firstProductSku = 'simple'; $firstProduct = $productRepository->get($firstProductSku, false, null, true); $this->assertBaseFields($firstProduct, $response['category']['products']['items'][0]); $this->assertAttributes($response['category']['products']['items'][0]); $this->assertWebsites($firstProduct, $response['category']['products']['items'][0]['websites']); - - $secondProductSku = '12345'; - $secondProduct = $productRepository->get($secondProductSku, false, null, true); - $this->assertBaseFields($secondProduct, $response['category']['products']['items'][1]); - $this->assertAttributes($response['category']['products']['items'][1]); - $this->assertWebsites($secondProduct, $response['category']['products']['items'][1]['websites']); } /** @@ -297,7 +292,6 @@ private function assertBaseFields($product, $actualResponse) $assertionMap = [ ['response_field' => 'attribute_set_id', 'expected_value' => $product->getAttributeSetId()], ['response_field' => 'created_at', 'expected_value' => $product->getCreatedAt()], - ['response_field' => 'id', 'expected_value' => $product->getId()], ['response_field' => 'name', 'expected_value' => $product->getName()], ['response_field' => 'price', 'expected_value' => [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php deleted file mode 100644 index ff444813a9d07..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -include __DIR__ . '/categories.php'; - -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - -/** @var \Magento\Indexer\Model\Indexer\CollectionFactory $indexerCollectionFactory */ -$indexerCollectionFactory = $objectManager->get(\Magento\Indexer\Model\Indexer\CollectionFactory::class); -$indexerCollection = $indexerCollectionFactory->create(); -$indexers = $indexerCollection->getItems(); -foreach ($indexers as $indexer) { - $indexer->reindexAll(); -} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php deleted file mode 100644 index 338a4b23bee59..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_indexed_rollback.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -include __DIR__ . '/categories_rollback.php'; - -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - -/** @var \Magento\Indexer\Model\Indexer\CollectionFactory $indexerCollectionFactory */ -$indexerCollectionFactory = $objectManager->get(\Magento\Indexer\Model\Indexer\CollectionFactory::class); -$indexerCollection = $indexerCollectionFactory->create(); -$indexers = $indexerCollection->getItems(); -foreach ($indexers as $indexer) { - $indexer->reindexAll(); -} From 3ac2c28ecb0e70452993001eafd417936cb44c48 Mon Sep 17 00:00:00 2001 From: Stanislav Lopukhov <slopukhov@magento.com> Date: Thu, 31 May 2018 08:50:56 +0300 Subject: [PATCH 258/333] MAGETWO-92306: [Forwardport] Fix Category Product Indexer --- .../Model/Indexer/Category/Product/TableMaintainer.php | 8 ++++++++ app/code/Magento/Catalog/etc/adminhtml/di.xml | 3 --- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php index d2f8925d09a7b..1278434fcad43 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/TableMaintainer.php @@ -13,6 +13,9 @@ use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction; use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver as TableResolver; +/** + * Class encapsulate logic of work with tables per store in Category Product indexer + */ class TableMaintainer { /** @@ -202,9 +205,14 @@ public function createMainTmpTable(int $storeId) * @param $storeId * * @return string + * + * @throws \Exception */ public function getMainTmpTable(int $storeId) { + if (!isset($this->mainTmpTable[$storeId])) { + throw new \Exception('Temporary table does not exist'); + } return $this->mainTmpTable[$storeId]; } } diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index ca8390a6c8f8a..10251d35dffcd 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -220,7 +220,4 @@ <argument name="filter" xsi:type="object">Magento\Catalog\Ui\DataProvider\Product\AddSearchKeyConditionToCollection</argument> </arguments> </type> - <type name="Magento\Framework\App\ResourceConnection"> - <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> - </type> </config> From 6a0e8cbf0ba1f08a94140b7346be00ca95243890 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 31 May 2018 09:59:03 +0300 Subject: [PATCH 259/333] MAGETWO-66217: Product Url key value comes from product name on storeview level (use default = yes) --- .../Initialization/Helper/AttributeFilter.php | 12 ++++++------ .../Initialization/Helper/AttributeFilterTest.php | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php index 09af738a13201..188b0b22f33bf 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php @@ -51,7 +51,7 @@ public function prepareProductAttributes(Product $product, array $productData, a * @param array $productData * @return array */ - private function prepareConfigData(Product $product, $attributeCode, array $productData): array + private function prepareConfigData(Product $product, string $attributeCode, array $productData): array { // UI component sends value even if field is disabled, so 'Use Config Settings' must be reset to false if ($product->hasData('use_config_' . $attributeCode)) { @@ -67,7 +67,7 @@ private function prepareConfigData(Product $product, $attributeCode, array $prod * @param array $productData * @return array */ - private function prepareDefaultData(array $attributeList, $attributeCode, array $productData): array + private function prepareDefaultData(array $attributeList, string $attributeCode, array $productData): array { if (isset($attributeList[$attributeCode])) { /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */ @@ -87,12 +87,12 @@ private function prepareDefaultData(array $attributeList, $attributeCode, array /** * @param Product $product - * @param $useDefaults - * @param $attribute - * @param $value + * @param array $useDefaults + * @param string $attribute + * @param mixed $value * @return bool */ - private function isAttributeShouldNotBeUpdated(Product $product, $useDefaults, $attribute, $value): bool + private function isAttributeShouldNotBeUpdated(Product $product, array $useDefaults, $attribute, $value): bool { $considerUseDefaultsAttribute = !isset($useDefaults[$attribute]) || $useDefaults[$attribute] === '1'; diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilterTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilterTest.php index 6e25d02df53ee..424427b871456 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilterTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilterTest.php @@ -9,7 +9,7 @@ use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use PHPUnit_Framework_MockObject_MockObject; +use PHPUnit_Framework_MockObject_MockObject as MockObject; class AttributeFilterTest extends \PHPUnit\Framework\TestCase { @@ -19,12 +19,12 @@ class AttributeFilterTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $objectManagerMock; /** - * @var Product|PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ protected $productMock; @@ -47,7 +47,7 @@ public function testPrepareProductAttributes( $expectedProductData, $initialProductData ) { - /** @var PHPUnit_Framework_MockObject_MockObject | Product $productMockMap */ + /** @var MockObject | Product $productMockMap */ $productMockMap = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods(['getData', 'getAttributes']) @@ -225,7 +225,7 @@ private function getProductAttributesMock(array $useDefaults): array $returnArray = []; foreach ($useDefaults as $attributecode => $isDefault) { if ($isDefault === '1') { - /** @var Attribute | PHPUnit_Framework_MockObject_MockObject $attribute */ + /** @var Attribute | MockObject $attribute */ $attribute = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->getMock(); From 483c8583ae2bd97c8cbe0117b9b092427b4604e0 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 31 May 2018 10:17:23 +0300 Subject: [PATCH 260/333] MAGETWO-91104: [2.3.0] Using a multiselect product attribute with a custom source model in the adminhtml doesn't render selected value --- .../Product/Form/Modifier/EavTest.php | 321 +++++++----------- .../Product/Form/Modifier/Eav.php | 20 +- 2 files changed, 151 insertions(+), 190 deletions(-) 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 a29379647b9e1..320ec821d7ffb 100755 --- 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 @@ -5,11 +5,10 @@ */ namespace Magento\Catalog\Test\Unit\Ui\DataProvider\Product\Form\Modifier; -use Magento\Catalog\Model\Product\Type; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav; use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Source\SourceInterface; use Magento\Framework\App\RequestInterface; -use Magento\Framework\EntityManager\EventManager; use Magento\Framework\Phrase; use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Api\Data\StoreInterface; @@ -257,7 +256,15 @@ protected function setUp() $this->searchResultsMock = $this->getMockBuilder(SearchResultsInterface::class) ->getMockForAbstractClass(); $this->eavAttributeMock = $this->getMockBuilder(Attribute::class) - ->setMethods(['load', 'getAttributeGroupCode', 'getApplyTo', 'getFrontendInput', 'getAttributeCode']) + ->setMethods([ + 'load', + 'getAttributeGroupCode', + 'getApplyTo', + 'getFrontendInput', + 'getAttributeCode', + 'usesSource', + 'getSource', + ]) ->disableOriginalConstructor() ->getMock(); $this->productAttributeMock = $this->getMockBuilder(ProductAttributeInterface::class) @@ -451,64 +458,60 @@ public function testModifyData() } /** - * @param int $productId + * @param int|null $productId * @param bool $productRequired - * @param string $attrValue - * @param string $note + * @param string|null $attrValue * @param array $expected * @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($productId, $productRequired, $attrValue, $note, $expected) - { - $configPath = 'arguments/data/config'; + public function testSetupAttributeMetaDefaultAttribute( + $productId, + $productRequired, + $attrValue, + $expected + ) { + $configPath = 'arguments/data/config'; $groupCode = 'product-details'; $sortOrder = '0'; + $attributeOptions = [ + ['value' => 1, 'label' => 'Int label'], + ['value' => 1.5, 'label' => 'Float label'], + ['value' => true, 'label' => 'Boolean label'], + ['value' => 'string', 'label' => 'String label'], + ['value' => ['test1', 'test2'], 'label' => 'Array label'] + ]; + $attributeOptionsExpected = [ + ['value' => '1', 'label' => 'Int label'], + ['value' => '1.5', 'label' => 'Float label'], + ['value' => '1', 'label' => 'Boolean label'], + ['value' => 'string', 'label' => 'String label'], + ['value' => ['test1', 'test2'], 'label' => 'Array label'] + ]; - $this->productMock->expects($this->any()) - ->method('getId') - ->willReturn($productId); - - $this->productAttributeMock->expects($this->any()) - ->method('getIsRequired') - ->willReturn($productRequired); - - $this->productAttributeMock->expects($this->any()) - ->method('getDefaultValue') - ->willReturn('required_value'); - - $this->productAttributeMock->expects($this->any()) - ->method('getAttributeCode') - ->willReturn('code'); - - $this->productAttributeMock->expects($this->any()) - ->method('getValue') - ->willReturn('value'); - - $this->productAttributeMock->expects($this->any()) - ->method('getNote') - ->willReturn($note); - - $this->productAttributeMock->expects($this->any()) - ->method('getDefaultFrontendLabel') - ->willReturn(new Phrase('mylabel')); + $this->productMock->method('getId')->willReturn($productId); + $this->productAttributeMock->method('getIsRequired')->willReturn($productRequired); + $this->productAttributeMock->method('getDefaultValue')->willReturn('required_value'); + $this->productAttributeMock->method('getAttributeCode')->willReturn('code'); + $this->productAttributeMock->method('getValue')->willReturn('value'); $attributeMock = $this->getMockBuilder(AttributeInterface::class) ->setMethods(['getValue']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $attributeMock->expects($this->any()) - ->method('getValue') - ->willReturn($attrValue); + $attributeMock->method('getValue')->willReturn($attrValue); - $this->productMock->expects($this->any()) - ->method('getCustomAttribute') - ->willReturn($attributeMock); + $this->productMock->method('getCustomAttribute')->willReturn($attributeMock); + $this->eavAttributeMock->method('usesSource')->willReturn(true); + + $attributeSource = $this->getMockBuilder(SourceInterface::class)->getMockForAbstractClass(); + $attributeSource->method('getAllOptions')->willReturn($attributeOptions); - $this->arrayManagerMock->expects($this->any()) - ->method('set') + $this->eavAttributeMock->method('getSource')->willReturn($attributeSource); + + $this->arrayManagerMock->method('set') ->with( $configPath, [], @@ -516,16 +519,21 @@ public function testSetupAttributeMetaDefaultAttribute($productId, $productRequi ) ->willReturn($expected); - $this->arrayManagerMock->expects($this->any()) + $this->arrayManagerMock->expects($this->once()) ->method('merge') + ->with( + $this->anything(), + $this->anything(), + $this->callback( + function ($value) use ($attributeOptionsExpected) { + return $value['options'] === $attributeOptionsExpected; + } + ) + ) ->willReturn($expected); - $this->arrayManagerMock->expects($this->any()) - ->method('get') - ->willReturn([]); - - $this->arrayManagerMock->expects($this->any()) - ->method('exists'); + $this->arrayManagerMock->method('get')->willReturn([]); + $this->arrayManagerMock->method('exists')->willReturn(true); $this->assertEquals( $expected, @@ -539,147 +547,82 @@ public function testSetupAttributeMetaDefaultAttribute($productId, $productRequi public function setupAttributeMetaDataProvider() { return [ - 'default_null_prod_not_new_and_required' => $this->defaultNullProdNotNewAndRequired(), - 'default_null_prod_not_new_and_not_required' => $this->defaultNullProdNotNewAndNotRequired(), - 'default_null_prod_new_and_not_required' => $this->defaultNullProdNewAndNotRequired(), - 'default_null_prod_new_and_required' => $this->defaultNullProdNewAndRequired(), - 'default_null_prod_new_and_required_and_filled_notice' => - $this->defaultNullProdNewAndRequiredAndFilledNotice() - ]; - } - - /** - * @return array - */ - private function defaultNullProdNotNewAndRequired() - { - return [ - 'productId' => 1, - 'productRequired' => true, - 'attrValue' => 'val', - 'note' => null, - 'expected' => [ - 'dataType' => null, - 'formElement' => null, - 'visible' => null, - 'required' => true, - 'notice' => null, - 'default' => null, - 'label' => new Phrase('mylabel'), - 'code' => 'code', - 'source' => 'product-details', - 'scopeLabel' => '', - 'globalScope' => false, - 'sortOrder' => 0 - ], - ]; - } - - /** - * @return array - */ - private function defaultNullProdNotNewAndNotRequired() - { - return [ - 'productId' => 1, - 'productRequired' => false, - 'attrValue' => 'val', - 'note' => null, - 'expected' => [ - 'dataType' => null, - 'formElement' => null, - 'visible' => null, - 'required' => false, - 'notice' => null, - 'default' => null, - 'label' => new Phrase('mylabel'), - 'code' => 'code', - 'source' => 'product-details', - 'scopeLabel' => '', - 'globalScope' => false, - 'sortOrder' => 0 + 'default_null_prod_not_new_and_required' => [ + 'productId' => 1, + 'productRequired' => true, + 'attrValue' => 'val', + 'expected' => [ + 'dataType' => null, + 'formElement' => null, + 'visible' => null, + 'required' => true, + 'notice' => null, + 'default' => null, + 'label' => new Phrase(null), + 'code' => 'code', + 'source' => 'product-details', + 'scopeLabel' => '', + 'globalScope' => false, + 'sortOrder' => 0 + ], ], - ]; - } - - /** - * @return array - */ - private function defaultNullProdNewAndNotRequired() - { - return [ - 'productId' => null, - 'productRequired' => false, - 'attrValue' => null, - 'note' => null, - 'expected' => [ - 'dataType' => null, - 'formElement' => null, - 'visible' => null, - 'required' => false, - 'notice' => null, - 'default' => 'required_value', - 'label' => new Phrase('mylabel'), - 'code' => 'code', - 'source' => 'product-details', - 'scopeLabel' => '', - 'globalScope' => false, - 'sortOrder' => 0 + 'default_null_prod_not_new_and_not_required' => [ + 'productId' => 1, + 'productRequired' => false, + 'attrValue' => 'val', + 'expected' => [ + 'dataType' => null, + 'formElement' => null, + 'visible' => null, + 'required' => false, + 'notice' => null, + 'default' => null, + 'label' => new Phrase(null), + 'code' => 'code', + 'source' => 'product-details', + 'scopeLabel' => '', + 'globalScope' => false, + 'sortOrder' => 0 + ], ], - ]; - } - - /** - * @return array - */ - private function defaultNullProdNewAndRequired() - { - return [ - 'productId' => null, - 'productRequired' => false, - 'attrValue' => null, - 'note' => null, - 'expected' => [ - 'dataType' => null, - 'formElement' => null, - 'visible' => null, - 'required' => false, - 'notice' => null, - 'default' => 'required_value', - 'label' => new Phrase('mylabel'), - 'code' => 'code', - 'source' => 'product-details', - 'scopeLabel' => '', - 'globalScope' => false, - 'sortOrder' => 0 - ], - ]; - } - - /** - * @return array - */ - private function defaultNullProdNewAndRequiredAndFilledNotice() - { - return [ - 'productId' => null, - 'productRequired' => false, - 'attrValue' => null, - 'note' => 'example notice', - 'expected' => [ - 'dataType' => null, - 'formElement' => null, - 'visible' => null, - 'required' => false, - 'notice' => __('example notice'), - 'default' => 'required_value', - 'label' => new Phrase('mylabel'), - 'code' => 'code', - 'source' => 'product-details', - 'scopeLabel' => '', - 'globalScope' => false, - 'sortOrder' => 0 + 'default_null_prod_new_and_not_required' => [ + 'productId' => null, + 'productRequired' => false, + 'attrValue' => null, + 'expected' => [ + 'dataType' => null, + 'formElement' => null, + 'visible' => null, + 'required' => false, + 'notice' => null, + 'default' => 'required_value', + 'label' => new Phrase(null), + 'code' => 'code', + 'source' => 'product-details', + 'scopeLabel' => '', + 'globalScope' => false, + 'sortOrder' => 0 + ], ], + 'default_null_prod_new_and_required' => [ + 'productId' => null, + 'productRequired' => false, + 'attrValue' => null, + 'expected' => [ + 'dataType' => null, + 'formElement' => null, + 'visible' => null, + 'required' => false, + 'notice' => null, + 'default' => 'required_value', + 'label' => new Phrase(null), + 'code' => 'code', + 'source' => 'product-details', + 'scopeLabel' => '', + 'globalScope' => false, + 'sortOrder' => 0 + ], + ] ]; } } 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 b216ee8c9c547..0e6f17d761bc3 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -611,8 +611,9 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC // TODO: Refactor to $attribute->getOptions() when MAGETWO-48289 is done $attributeModel = $this->getAttributeModel($attribute); if ($attributeModel->usesSource()) { + $options = $attributeModel->getSource()->getAllOptions(); $meta = $this->arrayManager->merge($configPath, $meta, [ - 'options' => $attributeModel->getSource()->getAllOptions(), + 'options' => $this->convertOptionsValueToString($options), ]); } @@ -683,6 +684,23 @@ private function getAttributeDefaultValue(ProductAttributeInterface $attribute) return $attribute->getDefaultValue(); } + /** + * Convert options value to string. + * + * @param array $options + * @return array + */ + private function convertOptionsValueToString(array $options) : array + { + array_walk($options, function (&$value) { + if (isset($value['value']) && is_scalar($value['value'])) { + $value['value'] = (string)$value['value']; + } + }); + + return $options; + } + /** * @param ProductAttributeInterface $attribute * @param array $meta From 4f1b50b1683e0bc3c00ee5d9d76aaa01b326cd80 Mon Sep 17 00:00:00 2001 From: Artem Cherednichenko <artemcherednichenkovasikievich@gmail.com> Date: Thu, 31 May 2018 13:26:13 +0300 Subject: [PATCH 261/333] Fix translations --- .../view/frontend/web/template/minicart/content.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html index 8bf1a87d34e6e..2daca51a2f5da 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html @@ -30,8 +30,12 @@ <span class="count" if="maxItemsToDisplay < getCartLineItemsCount()" text="maxItemsToDisplay"/> <translate args="'of'" if="maxItemsToDisplay < getCartLineItemsCount()"/> <span class="count" text="getCartLineItemsCount()"/> - <translate args="'Item in Cart'" if="getCartLineItemsCount() === 1"/> - <translate args="'Items in Cart'" if="getCartLineItemsCount() > 1"/> + <!-- ko if: (getCartLineItemsCount() === 1) --> + <span translate="'Item in Cart'"/> + <!--/ko--> + <!-- ko if: (getCartLineItemsCount() > 1) --> + <span translate="'Items in Cart'"/> + <!--/ko--> </div> <each args="getRegion('subtotalContainer')" render=""/> From f8355aefb9b3253a13842ed968e91cdf91b9df1b Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Thu, 31 May 2018 15:28:13 +0300 Subject: [PATCH 262/333] MAGETWO-64173: Browser back button issue (Checkout pages) --- .../view/frontend/web/js/model/step-navigator.js | 11 ++++++++++- .../view/frontend/web/js/view/progress-bar.js | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/step-navigator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/step-navigator.js index c707792111c82..0f2b0f4e26869 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/step-navigator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/step-navigator.js @@ -182,6 +182,15 @@ define([ }); }, + /** + * Sets window location hash. + * + * @param {String} hash + */ + setHash: function (hash) { + window.location.hash = hash; + }, + /** * Next step. */ @@ -199,7 +208,7 @@ define([ if (steps().length > activeIndex + 1) { code = steps()[activeIndex + 1].code; steps()[activeIndex + 1].isVisible(true); - window.location = window.checkoutConfig.checkoutUrl + '#' + code; + this.setHash(code); document.body.scrollTop = document.documentElement.scrollTop = 0; } } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/progress-bar.js b/app/code/Magento/Checkout/view/frontend/web/js/view/progress-bar.js index 72cf4e3d479c3..683a18d0e4ead 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/progress-bar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/progress-bar.js @@ -25,6 +25,11 @@ define([ initialize: function () { this._super(); window.addEventListener('hashchange', _.bind(stepNavigator.handleHash, stepNavigator)); + + if (!window.location.hash) { + stepNavigator.setHash(stepNavigator.steps().sort(stepNavigator.sortItems)[0].code); + } + stepNavigator.handleHash(); }, From 5542c81d42610c7ac5d15f1ff7aecf884f1f92fe Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 31 May 2018 18:09:16 +0300 Subject: [PATCH 263/333] MAGETWO-92134: [2.2.x] Payment Failed Email is not generated --- app/code/Magento/Sales/Model/Service/PaymentFailuresService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php index 6da1dc1b7e7cc..3a49bbce256ef 100644 --- a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php +++ b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php @@ -283,7 +283,7 @@ private function getLocaleDate(): string */ private function getCustomerName(Quote $quote): string { - $customer = __('Guest'); + $customer = __('Guest')->render(); if (!$quote->getCustomerIsGuest()) { $customer = $quote->getCustomer()->getFirstname() . ' ' . $quote->getCustomer()->getLastname(); From 3ac0addc2d6e688d767686132ce1cf78639a2613 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 31 May 2018 18:17:27 +0300 Subject: [PATCH 264/333] MAGETWO-91104: [2.3.0] Using a multiselect product attribute with a custom source model in the adminhtml doesn't render selected value --- .../Product/Form/Modifier/EavTest.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) 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 320ec821d7ffb..0426e389d9aeb 100755 --- 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 @@ -462,16 +462,17 @@ public function testModifyData() * @param bool $productRequired * @param string|null $attrValue * @param array $expected + * @return void * @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( $productId, - $productRequired, + bool $productRequired, $attrValue, - $expected - ) { + array $expected + ) : void { $configPath = 'arguments/data/config'; $groupCode = 'product-details'; $sortOrder = '0'; @@ -480,14 +481,14 @@ public function testSetupAttributeMetaDefaultAttribute( ['value' => 1.5, 'label' => 'Float label'], ['value' => true, 'label' => 'Boolean label'], ['value' => 'string', 'label' => 'String label'], - ['value' => ['test1', 'test2'], 'label' => 'Array label'] + ['value' => ['test1', 'test2'], 'label' => 'Array label'], ]; $attributeOptionsExpected = [ ['value' => '1', 'label' => 'Int label'], ['value' => '1.5', 'label' => 'Float label'], ['value' => '1', 'label' => 'Boolean label'], ['value' => 'string', 'label' => 'String label'], - ['value' => ['test1', 'test2'], 'label' => 'Array label'] + ['value' => ['test1', 'test2'], 'label' => 'Array label'], ]; $this->productMock->method('getId')->willReturn($productId); @@ -563,7 +564,7 @@ public function setupAttributeMetaDataProvider() 'source' => 'product-details', 'scopeLabel' => '', 'globalScope' => false, - 'sortOrder' => 0 + 'sortOrder' => 0, ], ], 'default_null_prod_not_new_and_not_required' => [ @@ -582,7 +583,7 @@ public function setupAttributeMetaDataProvider() 'source' => 'product-details', 'scopeLabel' => '', 'globalScope' => false, - 'sortOrder' => 0 + 'sortOrder' => 0, ], ], 'default_null_prod_new_and_not_required' => [ @@ -601,7 +602,7 @@ public function setupAttributeMetaDataProvider() 'source' => 'product-details', 'scopeLabel' => '', 'globalScope' => false, - 'sortOrder' => 0 + 'sortOrder' => 0, ], ], 'default_null_prod_new_and_required' => [ @@ -620,7 +621,7 @@ public function setupAttributeMetaDataProvider() 'source' => 'product-details', 'scopeLabel' => '', 'globalScope' => false, - 'sortOrder' => 0 + 'sortOrder' => 0, ], ] ]; From ed7fb27e198b3fe3879dfbd27a7ffb8f2cfd5071 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@magento.com> Date: Thu, 31 May 2018 11:36:44 -0500 Subject: [PATCH 265/333] MQE-1026: Stablize RE-develop branch --- .../Catalog/Section/StorefrontProductActionSection.xml | 2 +- .../SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml | 1 + .../Test/AdminCreateCartPriceRuleForCouponCodeTest.xml | 1 + .../Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml index f9e034aa3ae58..c2360efc05fa4 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductActionSection.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="StorefrontProductActionSection"> <element name="quantity" type="input" selector="#qty"/> - <element name="addToCart" type="button" selector="#product-addtocart-button" timeout="30"/> + <element name="addToCart" type="button" selector="#product-addtocart-button"/> <element name="addToCartButtonTitleIsAdding" type="text" selector="//button/span[text()='Adding...']"/> <element name="addToCartButtonTitleIsAdded" type="text" selector="//button/span[text()='Added']"/> <element name="addToCartButtonTitleIsAddToCart" type="text" selector="//button/span[text()='Add to Cart']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml index bba7a81a7c884..0027d9a71fff3 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/StorefrontSalesRuleActionGroup.xml @@ -49,6 +49,7 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 33703d3a97072..29f7fedc33af5 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -74,6 +74,7 @@ <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="applyCoupon"> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index b5b30ce3c265a..15a2d8cedc99c 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -66,6 +66,7 @@ <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="goToProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> <conditionalClick selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" dependentSelector="{{StorefrontSalesRuleCartCouponSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader"/> From 1d510977b021079681fe6b723fba0c45ac174fc9 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Mon, 28 May 2018 11:32:28 +0530 Subject: [PATCH 266/333] Fixed typo error --- app/code/Magento/Config/Block/System/Config/Form.php | 2 +- .../Model/Import/Product/Type/Configurable.php | 2 +- app/code/Magento/Customer/Model/Session.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Config/Block/System/Config/Form.php b/app/code/Magento/Config/Block/System/Config/Form.php index c17df229cf549..81e39a83296d7 100644 --- a/app/code/Magento/Config/Block/System/Config/Form.php +++ b/app/code/Magento/Config/Block/System/Config/Form.php @@ -709,7 +709,7 @@ protected function _getAdditionalElementTypes() } /** - * Temporary moved those $this->getRequest()->getParam('blabla') from the code accross this block + * Temporary moved those $this->getRequest()->getParam('blabla') from the code across this block * to getBlala() methods to be later set from controller with setters */ diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 64a0c23139c01..151bf5aa9263e 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -629,7 +629,7 @@ protected function _insertData() } /** - * Get new supper attribute id. + * Get new super attribute id. * * @return int */ diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index 71b0297fdd114..680e68b5c4c0f 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -555,7 +555,7 @@ public function setAfterAuthUrl($url) } /** - * Reset core session hosts after reseting session ID + * Reset core session hosts after resetting session ID * * @return $this */ From 9e4cc7b5f093321991a179d826505da27ea977c7 Mon Sep 17 00:00:00 2001 From: Marcel Hauri <marcel@hauri.me> Date: Fri, 1 Jun 2018 08:34:58 +0200 Subject: [PATCH 267/333] [forwardpport] fixes in config module #15511 --- app/code/Magento/Config/Model/Config/Importer.php | 2 +- .../Test/Unit/Block/System/Config/Form/Field/ImageTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Importer.php b/app/code/Magento/Config/Model/Config/Importer.php index e65a90c593e84..a54af2ead5048 100644 --- a/app/code/Magento/Config/Model/Config/Importer.php +++ b/app/code/Magento/Config/Model/Config/Importer.php @@ -124,7 +124,7 @@ public function import(array $data) $this->scopeConfig->clean(); } - $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($changedData, $data) { + $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () use ($changedData) { $this->scope->setCurrentScope(Area::AREA_ADMINHTML); // Invoke saving of new values. diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php index 1679ac75ad02c..8a005a52ab614 100644 --- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php +++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php @@ -72,7 +72,7 @@ public function testGetElementHtmlWithValue() 'showInWebsite' => '1', 'showInStore' => '1', 'label' => null, - 'backend_model' => \Magento\BackendModelConfig\Backend\Image::class, + 'backend_model' => \Magento\Config\Model\Config\Backend\Image::class, 'upload_dir' => [ 'config' => 'system/filesystem/media', 'scope_info' => '1', From 075a64c2c3aad5da6cdcc2fb18061cff6424a67c Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 1 Jun 2018 13:10:25 +0300 Subject: [PATCH 268/333] MAGETWO-90349: Template file 'header.html' is not found. --- .../Test/Unit/Model/AbstractTemplateTest.php | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php index 46f3fecfb8848..35d562fa7e5ec 100644 --- a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php @@ -117,10 +117,11 @@ protected function setUp() /** * Return the model under test with additional methods mocked. * - * @param $mockedMethods array + * @param array $mockedMethods + * @param array $data * @return \Magento\Email\Model\Template|\PHPUnit_Framework_MockObject_MockObject */ - protected function getModelMock(array $mockedMethods = []) + protected function getModelMock(array $mockedMethods = [], array $data = []) { $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); return $this->getMockForAbstractClass( @@ -136,7 +137,8 @@ protected function getModelMock(array $mockedMethods = []) 'scopeConfig' => $this->scopeConfig, 'emailConfig' => $this->emailConfig, 'filterFactory' => $this->filterFactory, - 'templateFactory' => $this->templateFactory + 'templateFactory' => $this->templateFactory, + 'data' => $data, ] ), '', @@ -431,4 +433,17 @@ public function testGetDesignConfig() $expectedConfig = ['area' => 'test_area', 'store' => 2]; $this->assertEquals($expectedConfig, $model->getDesignConfig()->getData()); } + + /** + * @return void + */ + public function testSetForcedAreaWhenAreIsSet(): void + { + $templateId = 'test_template'; + $model = $this->getModelMock([], ['area' => 'frontend']); + + $this->emailConfig->expects($this->never())->method('getTemplateArea'); + + $model->setForcedArea($templateId); + } } From 1113749443782fec52f81e05bdffe25d05301de8 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Fri, 18 May 2018 13:32:38 +0200 Subject: [PATCH 269/333] chore: remove extraneous cursor property --- lib/web/css/source/lib/_buttons.less | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/css/source/lib/_buttons.less b/lib/web/css/source/lib/_buttons.less index 804302fcb69ac..679c4b11fbaac 100644 --- a/lib/web/css/source/lib/_buttons.less +++ b/lib/web/css/source/lib/_buttons.less @@ -266,8 +266,7 @@ &.disabled, &[disabled], fieldset[disabled] & { - cursor: not-allowed; - pointer-events: none; // Disabling of clicks + pointer-events: none; // Disabling of all pointer events .lib-css(opacity, @button__disabled__opacity); } } From 9b1b80e8c69725d91d544003b82344fc6b0380c9 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 1 Jun 2018 14:32:43 +0300 Subject: [PATCH 270/333] MAGETWO-90349: Template file 'header.html' is not found. --- app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php index 35d562fa7e5ec..973d342e7f597 100644 --- a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php @@ -437,7 +437,7 @@ public function testGetDesignConfig() /** * @return void */ - public function testSetForcedAreaWhenAreIsSet(): void + public function testSetForcedAreaWhenAreaIsSet(): void { $templateId = 'test_template'; $model = $this->getModelMock([], ['area' => 'frontend']); From d696233736f0883e5c54f0ba81f635f8e2da8603 Mon Sep 17 00:00:00 2001 From: Ankur Raiyani <ankurvr7003@gmail.com> Date: Fri, 1 Jun 2018 17:28:53 +0530 Subject: [PATCH 271/333] Wrong Last orders amount on dashboard #15660 Wrong order amount on dashboard on Last orders listing when having more than one website with different currencies #15660 --- .../Backend/Block/Widget/Grid/Column/Renderer/Currency.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php index ff0399e4f507f..b3f467ce37c88 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php @@ -68,10 +68,7 @@ public function __construct( $this->_storeManager = $storeManager; $this->_currencyLocator = $currencyLocator; $this->_localeCurrency = $localeCurrency; - $defaultBaseCurrencyCode = $this->_scopeConfig->getValue( - \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, - 'default' - ); + $defaultBaseCurrencyCode = $currencyLocator->getDefaultCurrency($this->_request); $this->_defaultBaseCurrency = $currencyFactory->create()->load($defaultBaseCurrencyCode); } From 45da02260c989f088b4645307e4af42d9b944b00 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 1 Jun 2018 15:53:13 +0300 Subject: [PATCH 272/333] MAGETWO-91807: Error when adding a product to the cart in the backend with the capitalization of the sku --- .../Catalog/Model/ProductRepository.php | 77 ++++++++++++++++--- .../Test/Unit/Model/ProductRepositoryTest.php | 27 ++++--- .../Catalog/Model/ProductRepositoryTest.php | 67 ++++++++++++++++ 3 files changed, 151 insertions(+), 20 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 6a82658342824..a75e5349baa30 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -233,7 +233,8 @@ public function __construct( public function get($sku, $editMode = false, $storeId = null, $forceReload = false) { $cacheKey = $this->getCacheKey([$editMode, $storeId]); - if (!isset($this->instances[$sku][$cacheKey]) || $forceReload) { + $cachedProduct = $this->getProductFromLocalCache($sku, $cacheKey); + if ($cachedProduct === null || $forceReload) { $product = $this->productFactory->create(); $productId = $this->resourceModel->getIdBySku($sku); @@ -250,11 +251,10 @@ public function get($sku, $editMode = false, $storeId = null, $forceReload = fal } $product->load($productId); $this->cacheProduct($cacheKey, $product); + $cachedProduct = $product; } - if (!isset($this->instances[$sku])) { - $sku = trim($sku); - } - return $this->instances[$sku][$cacheKey]; + + return $cachedProduct; } /** @@ -312,7 +312,7 @@ protected function getCacheKey($data) private function cacheProduct($cacheKey, \Magento\Catalog\Api\Data\ProductInterface $product) { $this->instancesById[$product->getId()][$cacheKey] = $product; - $this->instances[$product->getSku()][$cacheKey] = $product; + $this->saveProductInLocalCache($product, $cacheKey); if ($this->cacheLimit && count($this->instances) > $this->cacheLimit) { $offset = round($this->cacheLimit / -2); @@ -338,7 +338,7 @@ protected function initializeProductData(array $productData, $createNew) $product->setWebsiteIds([$this->storeManager->getStore(true)->getWebsiteId()]); } } else { - unset($this->instances[$productData['sku']]); + $this->removeProductFromLocalCache($productData['sku']); $product = $this->get($productData['sku']); } @@ -613,7 +613,7 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO if ($tierPrices !== null) { $product->setData('tier_price', $tierPrices); } - unset($this->instances[$product->getSku()]); + $this->removeProductFromLocalCache($product->getSku()); unset($this->instancesById[$product->getId()]); $this->resourceModel->save($product); } catch (ConnectionException $exception) { @@ -650,8 +650,9 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO $e ); } - unset($this->instances[$product->getSku()]); + $this->removeProductFromLocalCache($product->getSku()); unset($this->instancesById[$product->getId()]); + return $this->get($product->getSku(), false, $product->getStoreId()); } @@ -663,7 +664,7 @@ public function delete(\Magento\Catalog\Api\Data\ProductInterface $product) $sku = $product->getSku(); $productId = $product->getId(); try { - unset($this->instances[$product->getSku()]); + $this->removeProductFromLocalCache($product->getSku()); unset($this->instancesById[$product->getId()]); $this->resourceModel->delete($product); } catch (ValidatorException $e) { @@ -673,8 +674,9 @@ public function delete(\Magento\Catalog\Api\Data\ProductInterface $product) __('The "%1" product couldn\'t be removed.', $sku) ); } - unset($this->instances[$sku]); + $this->removeProductFromLocalCache($sku); unset($this->instancesById[$productId]); + return true; } @@ -796,4 +798,57 @@ private function getCollectionProcessor() } return $this->collectionProcessor; } + + /** + * Gets product from the local cache by SKU. + * + * @param string $sku + * @param string $cacheKey + * @return Product|null + */ + private function getProductFromLocalCache(string $sku, string $cacheKey) + { + $preparedSku = $this->prepareSku($sku); + if (!isset($this->instances[$preparedSku])) { + return null; + } + + return $this->instances[$preparedSku][$cacheKey] ?? null; + } + + /** + * Removes product in the local cache. + * + * @param string $sku + * @return void + */ + private function removeProductFromLocalCache(string $sku) :void + { + $preparedSku = $this->prepareSku($sku); + unset($this->instances[$preparedSku]); + } + + /** + * Saves product in the local cache. + * + * @param Product $product + * @param string $cacheKey + * @return void + */ + private function saveProductInLocalCache(Product $product, string $cacheKey) : void + { + $preparedSku = $this->prepareSku($product->getSku()); + $this->instances[$preparedSku][$cacheKey] = $product; + } + + /** + * Converts SKU to lower case and trims. + * + * @param string $sku + * @return string + */ + private function prepareSku(string $sku): string + { + return mb_strtolower(trim($sku)); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php index a370cbea13c2b..bf5c3d8276295 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php @@ -381,11 +381,11 @@ public function testGetByIdAbsentProduct() public function testGetByIdProductInEditMode() { $productId = 123; - $this->productFactoryMock->expects($this->once())->method('create') - ->will($this->returnValue($this->productMock)); - $this->productMock->expects($this->once())->method('setData')->with('_edit_mode', true); - $this->productMock->expects($this->once())->method('load')->with($productId); + $this->productFactoryMock->method('create')->willReturn($this->productMock); + $this->productMock->method('setData')->with('_edit_mode', true); + $this->productMock->method('load')->with($productId); $this->productMock->expects($this->atLeastOnce())->method('getId')->willReturn($productId); + $this->productMock->method('getSku')->willReturn('simple'); $this->assertEquals($this->productMock, $this->model->getById($productId, true)); } @@ -411,6 +411,7 @@ public function testGetByIdForCacheKeyGenerate($identifier, $editMode, $storeId) } $this->productMock->expects($this->once())->method('load')->with($identifier); $this->productMock->expects($this->atLeastOnce())->method('getId')->willReturn($identifier); + $this->productMock->method('getSku')->willReturn('simple'); $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId)); //Second invocation should just return from cache $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId)); @@ -433,6 +434,7 @@ public function testGetByIdForcedReload() $this->serializerMock->expects($this->exactly(3))->method('serialize'); $this->productMock->expects($this->exactly(4))->method('getId')->willReturn($identifier); + $this->productMock->method('getSku')->willReturn('simple'); $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId)); //second invocation should just return from cache $this->assertEquals($this->productMock, $this->model->getById($identifier, $editMode, $storeId)); @@ -532,6 +534,7 @@ public function testGetByIdWithSetStoreId() $this->productMock->expects($this->once())->method('setData')->with('store_id', $storeId); $this->productMock->expects($this->once())->method('load')->with($productId); $this->productMock->expects($this->atLeastOnce())->method('getId')->willReturn($productId); + $this->productMock->method('getSku')->willReturn('simple'); $this->assertEquals($this->productMock, $this->model->getById($productId, false, $storeId)); } @@ -585,7 +588,8 @@ public function testSaveNew() ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->productMock->expects($this->once())->method('getWebsiteIds')->willReturn([]); + $this->productMock->method('getWebsiteIds')->willReturn([]); + $this->productMock->method('getSku')->willReturn('simple'); $this->assertEquals($this->productMock, $this->model->save($this->productMock)); } @@ -597,7 +601,8 @@ public function testSaveNew() public function testSaveUnableToSaveException() { $this->storeManagerMock->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']); - $this->resourceModelMock->expects($this->exactly(1))->method('getIdBySku')->will($this->returnValue(null)); + $this->resourceModelMock->expects($this->exactly(1)) + ->method('getIdBySku')->willReturn(null); $this->productFactoryMock->expects($this->exactly(2)) ->method('create') ->will($this->returnValue($this->productMock)); @@ -610,7 +615,8 @@ public function testSaveUnableToSaveException() ->expects($this->once()) ->method('toNestedArray') ->will($this->returnValue($this->productData)); - $this->productMock->expects($this->once())->method('getWebsiteIds')->willReturn([]); + $this->productMock->method('getWebsiteIds')->willReturn([]); + $this->productMock->method('getSku')->willReturn('simple'); $this->model->save($this->productMock); } @@ -637,6 +643,7 @@ public function testSaveException() ->method('toNestedArray') ->will($this->returnValue($this->productData)); $this->productMock->expects($this->once())->method('getWebsiteIds')->willReturn([]); + $this->productMock->method('getSku')->willReturn('simple'); $this->model->save($this->productMock); } @@ -661,6 +668,7 @@ public function testSaveInvalidProductException() ->method('toNestedArray') ->will($this->returnValue($this->productData)); $this->productMock->expects($this->once())->method('getWebsiteIds')->willReturn([]); + $this->productMock->method('getSku')->willReturn('simple'); $this->model->save($this->productMock); } @@ -692,6 +700,7 @@ public function testSaveThrowsTemporaryStateExceptionIfDatabaseConnectionErrorOc $this->productMock->expects($this->once()) ->method('getWebsiteIds') ->willReturn([]); + $this->productMock->method('getSku')->willReturn('simple'); $this->model->save($this->productMock); } @@ -734,9 +743,8 @@ public function testGetList() { $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteriaInterface::class); $collectionMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); - $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($collectionMock); - + $this->productMock->method('getSku')->willReturn('simple'); $collectionMock->expects($this->once())->method('addAttributeToSelect')->with('*'); $collectionMock->expects($this->exactly(2))->method('joinAttribute')->withConsecutive( ['status', 'catalog_product/status', 'entity_id', null, 'inner'], @@ -1299,6 +1307,7 @@ public function testSaveWithDifferentWebsites() ]); $this->productMock->expects($this->once())->method('getWebsiteIds')->willReturn([1,2,3]); $this->productMock->expects($this->once())->method('setWebsiteIds')->willReturn([2,3]); + $this->productMock->method('getSku')->willReturn('simple'); $this->assertEquals($this->productMock, $this->model->save($this->productMock)); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php new file mode 100644 index 0000000000000..d6ab99f2ca576 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.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; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Provide tests for ProductRepository model. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class ProductRepositoryTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test subject. + * + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->productRepository = Bootstrap::getObjectManager() + ->get(ProductRepositoryInterface::class); + } + + /** + * Check a case when product should be retrieved with different SKU variations. + * + * @param string $sku + * @return void + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @dataProvider skuDataProvider + */ + public function testGetProduct(string $sku) : void + { + $expectedSku = 'simple'; + $product = $this->productRepository->get($sku); + + self::assertNotEmpty($product); + self::assertEquals($expectedSku, $product->getSku()); + } + + /** + * Get list of SKU variations for the same product. + * + * @return array + */ + public function skuDataProvider(): array + { + return [ + ['sku' => 'simple'], + ['sku' => 'Simple'], + ['sku' => 'simple '], + ]; + } +} From c5eab1c0fc9e0eac46e0d0fc7466973badc5e1fa Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 1 Jun 2018 16:05:03 +0300 Subject: [PATCH 273/333] MAGETWO-90349: Template file 'header.html' is not found. --- .../Test/Unit/Model/AbstractTemplateTest.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php index 973d342e7f597..eeb7b5730dc93 100644 --- a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php @@ -438,11 +438,27 @@ public function testGetDesignConfig() * @return void */ public function testSetForcedAreaWhenAreaIsSet(): void + { + $templateId = 'test_template'; + $model = $this->getModelMock([], ['area' => null]); + + $this->emailConfig->expects($this->once()) + ->method('getTemplateArea') + ->with($templateId); + + $model->setForcedArea($templateId); + } + + /** + * @return void + */ + public function testNotSetForcedAreaWhenAreaIsSet(): void { $templateId = 'test_template'; $model = $this->getModelMock([], ['area' => 'frontend']); - $this->emailConfig->expects($this->never())->method('getTemplateArea'); + $this->emailConfig->expects($this->never()) + ->method('getTemplateArea'); $model->setForcedArea($templateId); } From 662a316cda4f5e951f3b59cd770472d40286ae5f Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 1 Jun 2018 16:07:14 +0300 Subject: [PATCH 274/333] MAGETWO-90349: Template file 'header.html' is not found. --- .../Magento/Email/Test/Unit/Model/AbstractTemplateTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php index eeb7b5730dc93..4f545360616c6 100644 --- a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php @@ -437,7 +437,7 @@ public function testGetDesignConfig() /** * @return void */ - public function testSetForcedAreaWhenAreaIsSet(): void + public function testSetForcedAreaWhenAreaIsNotSet(): void { $templateId = 'test_template'; $model = $this->getModelMock([], ['area' => null]); @@ -452,7 +452,7 @@ public function testSetForcedAreaWhenAreaIsSet(): void /** * @return void */ - public function testNotSetForcedAreaWhenAreaIsSet(): void + public function testSetForcedAreaWhenAreaIsSet(): void { $templateId = 'test_template'; $model = $this->getModelMock([], ['area' => 'frontend']); From d5c395295aa59d8d42e12dca82ec933bad096668 Mon Sep 17 00:00:00 2001 From: Sanjay Patel <sanjay@wagento.com> Date: Sat, 19 May 2018 11:23:56 +0530 Subject: [PATCH 275/333] #14063 - Wrong invoice prefix in multistore setup due to default store id --- .../Magento/Sales/Model/ResourceModel/EntityAbstract.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/ResourceModel/EntityAbstract.php b/app/code/Magento/Sales/Model/ResourceModel/EntityAbstract.php index 1b781890e0f7f..80612277e68d5 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/EntityAbstract.php +++ b/app/code/Magento/Sales/Model/ResourceModel/EntityAbstract.php @@ -123,10 +123,15 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) { /** @var \Magento\Sales\Model\AbstractModel $object */ if ($object instanceof EntityInterface && $object->getIncrementId() == null) { + $store = $object->getStore(); + $storeId = $store->getId(); + if ($storeId === null) { + $storeId = $store->getGroup()->getDefaultStoreId(); + } $object->setIncrementId( $this->sequenceManager->getSequence( $object->getEntityType(), - $object->getStore()->getGroup()->getDefaultStoreId() + $storeId )->getNextValue() ); } From 2feaf3049e3b2397ba54aafcaa9ec5ff8cba0d83 Mon Sep 17 00:00:00 2001 From: Sanjay Patel <sanjay@wagento.com> Date: Wed, 30 May 2018 16:34:16 +0530 Subject: [PATCH 276/333] set correct annotation --- lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php | 4 ++-- .../Magento/Framework/Stdlib/DateTime/TimezoneInterface.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 9f182f3dc00a4..3c2f20fcc186f 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -260,8 +260,8 @@ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null) * @param string|\DateTimeInterface $date * @param int $dateType * @param int $timeType - * @param null $locale - * @param null $timezone + * @param string|null $locale + * @param string|null $timezone * @param string|null $pattern * @return string */ diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php index 047ca9d8b4528..a8b3fb1a81ffe 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/TimezoneInterface.php @@ -124,8 +124,8 @@ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null); * @param string|\DateTimeInterface $date * @param int $dateType * @param int $timeType - * @param null $locale - * @param null $timezone + * @param string|null $locale + * @param string|null $timezone * @param string|null $pattern * @return string */ From 11923bc4439e29bd707226bef8377f2ea458a29a Mon Sep 17 00:00:00 2001 From: Sanjay Patel <sanjay@wagento.com> Date: Sat, 19 May 2018 12:41:34 +0530 Subject: [PATCH 277/333] #12820 - Wrong annotation in _toOptionArray - magento/framework/Data/Collection/AbstractDb.php --- lib/internal/Magento/Framework/Data/Collection/AbstractDb.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php index 4feaf21d2b2e8..63ba6824e5ab9 100644 --- a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php +++ b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php @@ -630,7 +630,7 @@ public function fetchItem() /** * Overridden to use _idFieldName by default. * - * @param null $valueField + * @param string|null $valueField * @param string $labelField * @param array $additional * @return array From 080dba193c1ca8f7e3e17a8dea35fd5e0344dbc7 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 1 Jun 2018 15:34:47 -0500 Subject: [PATCH 278/333] MAGETWO-91434: Default option for 'Status' attribute not being set --- .../Product/Form/Modifier/GeneralTest.php | 89 ++++++++++++++++++- .../Product/Form/Modifier/General.php | 20 ++++- 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php index b4460b314513b..78502ae297b52 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php @@ -5,8 +5,11 @@ */ namespace Magento\Catalog\Test\Unit\Ui\DataProvider\Product\Form\Modifier; -use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\General; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Framework\Stdlib\ArrayManager; /** * Class GeneralTest @@ -15,6 +18,35 @@ */ class GeneralTest extends AbstractModifierTest { + /** + * @var AttributeRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeRepositoryMock; + + /** + * @var General + */ + private $generalModifier; + + protected function setUp() + { + parent::setUp(); + + $this->attributeRepositoryMock = $this->getMockBuilder(AttributeRepositoryInterface::class) + ->getMockForAbstractClass(); + + $arrayManager = $this->objectManager->getObject(ArrayManager::class); + + $this->generalModifier = $this->objectManager->getObject( + General::class, + [ + 'attributeRepository' => $this->attributeRepositoryMock, + 'locator' => $this->locatorMock, + 'arrayManager' => $arrayManager, + ] + ); + } + /** * {@inheritdoc} */ @@ -40,4 +72,59 @@ public function testModifyMeta() ] ])); } + + /** + * @param array $data + * @param int $defaultStatusValue + * @param array $expectedResult + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @dataProvider modifyDataDataProvider + */ + public function testModifyDataNewProduct(array $data, int $defaultStatusValue, array $expectedResult) + { + $attributeMock = $this->getMockBuilder(AttributeInterface::class) + ->getMockForAbstractClass(); + $attributeMock + ->method('getDefaultValue') + ->willReturn($defaultStatusValue); + $this->attributeRepositoryMock + ->method('get') + ->with( + ProductAttributeInterface::ENTITY_TYPE_CODE, + ProductAttributeInterface::CODE_STATUS + ) + ->willReturn($attributeMock); + $this->assertSame($expectedResult, $this->generalModifier->modifyData($data)); + } + + /** + * @return array + */ + public function modifyDataDataProvider(): array + { + return [ + 'With default status value' => [ + 'data' => [], + 'defaultStatusAttributeValue' => 5, + 'expectedResult' => [ + null => [ + General::DATA_SOURCE_DEFAULT => [ + ProductAttributeInterface::CODE_STATUS => 5, + ], + ], + ], + ], + 'Without default status value' => [ + 'data' => [], + 'defaultStatusAttributeValue' => 0, + 'expectedResult' => [ + null => [ + General::DATA_SOURCE_DEFAULT => [ + ProductAttributeInterface::CODE_STATUS => 1, + ], + ], + ], + ], + ]; + } } 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 ea69ebf4dda24..03d4dde311491 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -7,6 +7,7 @@ use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Eav\Api\AttributeRepositoryInterface; use Magento\Ui\Component\Form; use Magento\Framework\Stdlib\ArrayManager; @@ -35,21 +36,31 @@ class General extends AbstractModifier */ private $localeCurrency; + /** + * @var AttributeRepositoryInterface + */ + private $attributeRepository; + /** * @param LocatorInterface $locator * @param ArrayManager $arrayManager + * @param AttributeRepositoryInterface|null $attributeRepository */ public function __construct( LocatorInterface $locator, - ArrayManager $arrayManager + ArrayManager $arrayManager, + AttributeRepositoryInterface $attributeRepository = null ) { $this->locator = $locator; $this->arrayManager = $arrayManager; + $this->attributeRepository = $attributeRepository + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(AttributeRepositoryInterface::class); } /** * {@inheritdoc} * @since 101.0.0 + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function modifyData(array $data) { @@ -58,7 +69,12 @@ public function modifyData(array $data) $modelId = $this->locator->getProduct()->getId(); if (!isset($data[$modelId][static::DATA_SOURCE_DEFAULT][ProductAttributeInterface::CODE_STATUS])) { - $data[$modelId][static::DATA_SOURCE_DEFAULT][ProductAttributeInterface::CODE_STATUS] = '1'; + $attributeStatus = $this->attributeRepository->get( + ProductAttributeInterface::ENTITY_TYPE_CODE, + ProductAttributeInterface::CODE_STATUS + ); + $data[$modelId][static::DATA_SOURCE_DEFAULT][ProductAttributeInterface::CODE_STATUS] = + $attributeStatus->getDefaultValue() ?: 1; } return $data; From 760e889a5ecca1a2c87ea6119914df4802327b1b Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Sun, 27 May 2018 14:34:14 -0500 Subject: [PATCH 279/333] MAGETWO-92247: Verify that product price is displayed on additional storefront if product is disabled for default store view --- .../Section/AdminProductFormSection.xml | 8 + .../ConfigWebUrlOptionsActionGroup.xml | 32 +++ .../Config/Section/GeneralSection.xml | 6 + .../Data/ProductConfigurableAttributeData.xml | 13 ++ ...reateProductConfigurationsPanelSection.xml | 2 + .../AdminProductFormConfigurationsSection.xml | 4 +- ...bleProductPriceAdditionalStoreViewTest.xml | 205 ++++++++++++++++++ .../AdminCreateNewStoreGroupActionGroup.xml | 23 ++ .../AdminCreateWebsiteActionGroup.xml | 21 ++ .../AdminDeleteWebsiteActionGroup.xml | 24 ++ .../Store/Page/AdminSystemStoreGroupPage.xml | 12 + .../Page/AdminSystemStoreWebsitePage.xml | 12 + .../Section/AdminNewStoreGroupSection.xml | 2 +- .../Section/AdminNewWebsiteActionsSection.xml | 12 + 14 files changed, 374 insertions(+), 2 deletions(-) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/ConfigWebUrlOptionsActionGroup.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminCreateWebsiteActionGroup.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminDeleteWebsiteActionGroup.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Page/AdminSystemStoreGroupPage.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Page/AdminSystemStoreWebsitePage.xml create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewWebsiteActionsSection.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml index 926d87e889931..a35fd3cf04479 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml @@ -10,6 +10,9 @@ <section name="AdminProductFormSection"> <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="enableProduct" type="checkbox" selector="input[name='product[status]']"/> + <element name="enableProductLabel" type="checkbox" selector="input[name='product[status]']+label"/> + <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']"/> <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']"/> @@ -31,6 +34,11 @@ <element name="visibility" type="select" selector="//select[@name='product[visibility]']"/> <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> </section> + <section name="ProductInWebsitesSection"> + <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> + <!--<element name="websites" type="checkbox" selector="input[name='product[website_ids][{{var1}}]']" parameterized="true"/>--> + <element name="website" type="checkbox" selector="//label[contains(text(), '{{var1}}')]/parent::div//input[@type='checkbox']" parameterized="true"/> + </section> <section name="ProductDesignSection"> <element name="DesignTab" type="button" selector="//strong[@class='admin__collapsible-title']//span[text()='Design']"/> <element name="LayoutDropdown" type="select" selector="select[name='product[page_layout]']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/ConfigWebUrlOptionsActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/ConfigWebUrlOptionsActionGroup.xml new file mode 100644 index 0000000000000..56c313688fdb6 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/ActionGroup/ConfigWebUrlOptionsActionGroup.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnableWebUrlOptions"> + <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"> + <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/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/GeneralSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/GeneralSection.xml index a8025085204bd..b51cfc1d316e5 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/GeneralSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Config/Section/GeneralSection.xml @@ -20,10 +20,16 @@ <section name="WebSection"> <element name="DefaultLayoutsTab" type="button" selector="#web_default_layouts-head"/> <element name="CheckIfTabExpand" type="button" selector="#web_default_layouts-head:not(.open)"/> + <element name="UrlOptionsTab" type="button" selector="#web_url-head"/> + <element name="CheckIfUrlOptionsTabExpand" type="button" selector="#web_url-head:not(.open)"/> </section> <section name="DefaultLayoutsSection"> <element name="productLayout" type="select" selector="#web_default_layouts_default_product_layout"/> <element name="categoryLayout" type="select" selector="#web_default_layouts_default_category_layout"/> <element name="pageLayout" type="select" selector="#web_default_layouts_default_cms_layout"/> </section> + <section name="UrlOptionsSection"> + <element name="addStoreCodeToUrl" type="select" selector="#web_url_use_store"/> + <element name="systemValueForStoreCode" type="checkbox" selector="#web_url_use_store_inherit"/> + </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ProductConfigurableAttributeData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ProductConfigurableAttributeData.xml index 5a1c0139d56ca..ed8bbc949c550 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ProductConfigurableAttributeData.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ProductConfigurableAttributeData.xml @@ -29,4 +29,17 @@ <data key="name" unique="suffix">Orange</data> <data key="price">99.99</data> </entity> + <entity name="colorDefaultProductAttribute" type="product_attribute"> + <data key="default_label">Color</data> + <data key="input_type">Dropdown</data> + <data key="attribute_quantity">2</data> + </entity> + <entity name="colorDefaultProductAttribute1" type="product_attribute"> + <data key="name">Green</data> + <data key="price">4.00</data> + </entity> + <entity name="colorDefaultProductAttribute2" type="product_attribute"> + <data key="name">Black</data> + <data key="price">5.00</data> + </entity> </entities> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml index a097dfa4ad764..99f6a8842481e 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -24,6 +24,8 @@ <element name="attributeCheckboxByIndex" type="input" selector="li.attribute-option:nth-of-type({{var1}}) input" parameterized="true"/> <element name="applyUniquePricesByAttributeToEachSku" type="radio" selector=".admin__field-label[for='apply-unique-prices-radio']"/> + <element name="applySinglePriceToAllSkus" type="radio" selector=".admin__field-label[for='apply-single-price-radio']"/> + <element name="singlePrice" type="input" selector="#apply-single-price-input"/> <element name="selectAttribute" type="select" selector="#select-each-price" timeout="30"/> <element name="attribute1" type="input" selector="#apply-single-price-input-0"/> <element name="attribute2" type="input" selector="#apply-single-price-input-1"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml index 40d700fd4b75c..43bf9822903be 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/AdminProductFormConfigurationsSection.xml @@ -16,14 +16,16 @@ <element name="currentVariationsPriceCells" type="textarea" selector=".admin__control-fields[data-index='price_container']"/> <element name="currentVariationsQuantityCells" type="textarea" selector=".admin__control-fields[data-index='quantity_container']"/> <element name="currentVariationsAttributesCells" type="textarea" selector=".admin__control-fields[data-index='attributes']"/> + <element name="currentVariationsStatusCells" type="textarea" selector="._no-header[data-index='status']"/> <element name="actionsBtn" type="button" selector="(//button[@class='action-select']/span[contains(text(), 'Select')])[{{var1}}]" parameterized="true"/> <element name="removeProductBtn" type="button" selector="//a[text()='Remove Product']"/> <element name="disableProductBtn" type="button" selector="//a[text()='Disable Product']"/> + <element name="enableProductBtn" type="button" selector="//a[text()='Enable Product']"/> </section> <section name="AdminConfigurableProductFormSection"> <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> <element name="productQuantity" type="input" selector=".admin__control-text[name='product[quantity_and_stock_status][qty]']"/> - <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_per_source_container']"/> + <element name="currentVariationsQuantityCells" type="button" selector="td[data-index='quantity_container']"/> <element name="rowByCode" type="textarea" selector="//span[contains(text(), '{{var1}}-{{var2}}')]//ancestor-or-self::tr" parameterized="true"/> </section> <section name="AdminConfigurableProductSelectAttributesSlideOut"> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml new file mode 100644 index 0000000000000..56a9020c3a14d --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -0,0 +1,205 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="ConfigurableProductPriceAdditionalStoreViewTest"> + <annotations> + <features value="ConfigurableProductPriceStoreFront"/> + <title value="Configurable product prices should not disappear on storefront for additional store"/> + <description value="Configurable product price should not disappear for additional stores on frontEnd if disabled for default store"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-92247"/> + <group value="configurable"/> + <group value="product"/> + </annotations> + <before> + + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + </before> + + <after> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="addnewWebsite"/> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="addNewStoreGroup"/> + + <!--Create Store view --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption userInput="1" selector="{{AdminNewStoreSection.statusDropdown}}" stepKey="enableStoreViewStatus"/> + <click selector="{{AdminStoresMainActionsSection.saveButton}}" stepKey="clickStoreViewSaveButton"/> + <waitForElementVisible selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" stepKey="waitForAcceptNewStoreViewCreationModal" /> + <conditionalClick selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" dependentSelector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" visible="true" stepKey="AcceptNewStoreViewCreation"/> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReolad"/> + <see userInput="You saved the store view." stepKey="seeSaveMessage" /> + + <!--go to admin and open product edit page to disable product all store view --> + <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToProductEditPage"/> + <waitForPageLoad stepKey="waitEditPage"/> + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProductForAllStoreView"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton2"/> + <waitForLoadingMaskToDisappear stepKey="waitForProductPageSave1" /> + <dontSeeCheckboxIsChecked selector="{{AdminProductFormSection.enableProduct}}" stepKey="dontSeeCheckboxEnableProductIsChecked"/> + + <!-- Disable each of the child products for All Store views --> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActionsForFirstVariation1"/> + <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisableChildProduct1"/> + + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('2')}}" stepKey="clickToExpandActionsForSecondVariation1"/> + <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisableChildProduct2"/> + + <!-- Add product to second website --> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsitesSection1"/> + <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForLoadingMaskToDisappear stepKey="waitForProductPageSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + + <!-- switch to the second store view --> + <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher"/> + <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessage"/> + <!--<waitForPageLoad stepKey="waitForStoreViewSwitched"/>--> + <waitForPageLoad time="30" stepKey="waitForPageLoad9"/> + <see userInput="Second Store View" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewName"/> + + <!-- enable the config product for the second store --> + <waitForElementVisible selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="waitForProductEnableSlider"/> + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="enableProductForSecondStoreView"/> + <seeCheckboxIsChecked selector="{{AdminProductFormSection.enableProduct}}" stepKey="seeThatProductIsEnabled"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="enabledConfigProductSecondStore"/> + + <!--go to admin and open product edit page to enable child product for second store view --> + <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="goToProductEditPage2"/> + <waitForPageLoad stepKey="waitEditPage2"/> + <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher1"/> + <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView1"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessage1"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad8"/> + <see userInput="Second Store View" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewName1"/> + + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('1')}}" stepKey="clickToExpandActionsForFirstVariation2"/> + <click selector="{{AdminProductFormConfigurationsSection.enableProductBtn}}" stepKey="clickEnableChildProduct1"/> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtn('2')}}" stepKey="clickToExpandActionsForSecondVariation2"/> + <click selector="{{AdminProductFormConfigurationsSection.enableProductBtn}}" stepKey="clickEnableChildProduct2"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAll"/> + + <!-- assert second store view storefront category list page --> + <amOnPage url="/second_store_view/" stepKey="amOnsecondStoreFront1"/> + <waitForPageLoad stepKey="waitForPageLoad31"/> + <click userInput="$$createCategory.name$$" stepKey="clickOnCategoryName1"/> + <waitForPageLoad stepKey="waitForPageLoad41"/> + <see userInput="$$createConfigProduct.name$$" stepKey="assertProductPresent1"/> + + <!--go to admin and open child product1 and assign it to the second website --> + <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToProduct1EditPage1"/> + <waitForPageLoad stepKey="waitChild1EditPageToLoad"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProduct1InWebsitesSection"/> + <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite1"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveUpdatedChild1Again"/> + + <!--go to admin again and open child product1 and enable for second store view--> + <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToProduct1EditPage"/> + <waitForPageLoad stepKey="waitChild1EditPageToLoad1"/> + <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcherP1"/> + <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView2P1"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessageP1"/> + <waitForPageLoad time="30" stepKey="waitForStoreViewSwitchedP1"/> + <see userInput="Second Store View" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewNameP1"/> + <waitForElementVisible selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="waitForProductEnableSliderP1"/> + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="enableProductForSecondStoreViewP1"/> + <seeCheckboxIsChecked selector="{{AdminProductFormSection.enableProduct}}" stepKey="seeThatProduct1IsEnabled"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="save2UpdatedChild1"/> + + <!--go to admin and open child product2 edit page and assign it to the second website --> + <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToProduct2EditPage"/> + <waitForPageLoad stepKey="waitChild2EditPageToLoad"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProduct2InWebsitesSection"/> + <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite2"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveUpdatedChild2"/> + + <!--go to admin again and open child product2 and enable for second store view--> + <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToProduct2EditPage2"/> + <waitForPageLoad stepKey="waitChild2EditPageToLoad1"/> + <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcherP2"/> + <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView2P2"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessageP2"/> + <waitForPageLoad time="30" stepKey="waitForStoreViewSwitchedP2"/> + <see userInput="Second Store View" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewNameP2"/> + <waitForElementVisible selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="waitForProductEnableSliderP2"/> + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="enableProductForSecondStoreViewP2"/> + <seeCheckboxIsChecked selector="{{AdminProductFormSection.enableProduct}}" stepKey="seeThatProduct2IsEnabled"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="save2UpdatedChild2"/> + + <!-- assert storefront category list page --> + <amOnPage url="/second_store_view/" stepKey="amOnsecondStoreFront"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <click userInput="$$createCategory.name$$" stepKey="clickOnCategoryName"/> + <waitForPageLoad stepKey="waitForPageLoad4"/> + <see userInput="$$createConfigProduct.name$$" stepKey="assertProductPresent"/> + <dontSee userInput="$$createConfigChildProduct1.price$$" stepKey="assertProductPricePresent"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml new file mode 100644 index 0000000000000..83aa1a4735935 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<!-- Admin creates new Store group --> +<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"> + <actionGroup name="AdminCreateNewStoreGroupActionGroup"> + <amOnPage url="{{AdminSystemStoreGroupPage.url}}" stepKey="navigateToNewStoreView"/> + <waitForPageLoad stepKey="waitForPageLoad1" /> + <!--Create Store group --> + <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="Second Website" stepKey="selectWebsite" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="Second Store" stepKey="enterStoreGroupName" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="second_store" stepKey="enterStoreGroupCode" /> + <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="Default Category" stepKey="chooseRootCategory" /> + <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup" /> + <waitForElementVisible selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="waitForStoreGridReload"/> + <see userInput="You saved the store." stepKey="seeSavedMessage" /> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminCreateWebsiteActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminCreateWebsiteActionGroup.xml new file mode 100644 index 0000000000000..c34e6c0814bb8 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminCreateWebsiteActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<!-- Admin creates new custom website --> +<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"> + <actionGroup name="AdminCreateWebsiteActionGroup"> + <amOnPage url="{{AdminSystemStoreWebsitePage.url}}" stepKey="navigateToNewWebsitePage"/> + <waitForPageLoad stepKey="waitForStoresPageLoad"/> + <!--Create Website--> + <fillField selector="{{AdminNewWebsiteSection.name}}" userInput="Second Website" stepKey="enterWebsiteName" /> + <fillField selector="{{AdminNewWebsiteSection.code}}" userInput="second_website" stepKey="enterWebsiteCode" /> + <click selector="{{AdminNewWebsiteActionsSection.saveWebsite}}" stepKey="clickSaveWebsite" /> + <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> + <see userInput="You saved the website." stepKey="seeSavedMessage" /> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminDeleteWebsiteActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminDeleteWebsiteActionGroup.xml new file mode 100644 index 0000000000000..9b61315f0d5b8 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/ActionGroup/AdminDeleteWebsiteActionGroup.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteWebsiteActionGroup"> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="Second Website" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillSearchWebsiteField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <see userInput="Second Website" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="verifyThatCorrectWebsiteFound"/> + <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteWebsiteButtonOnEditWebsitePage"/> + <selectOption userInput="No" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> + <click selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}" stepKey="clickDeleteWebsiteButton"/> + <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> + <see userInput="You deleted the website." stepKey="seeSavedMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Page/AdminSystemStoreGroupPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Page/AdminSystemStoreGroupPage.xml new file mode 100644 index 0000000000000..9c203241283dc --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Page/AdminSystemStoreGroupPage.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminSystemStoreGroupPage" url="admin/system_store/newGroup" module="Magento_Store" area="admin"> + <section name="AdminNewStoreGroupSection"/> + </page> +</pages> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Page/AdminSystemStoreWebsitePage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Page/AdminSystemStoreWebsitePage.xml new file mode 100644 index 0000000000000..6b28b2f04ea09 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Page/AdminSystemStoreWebsitePage.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminSystemStoreWebsitePage" url="admin/system_store/newWebsite" module="Magento_Store" area="admin"> + <section name="AdminNewWebsiteSection"/> + </page> +</pages> \ No newline at end of file diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewStoreGroupSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewStoreGroupSection.xml index 7357ee0771239..be22aa7f8c120 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewStoreGroupSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewStoreGroupSection.xml @@ -10,6 +10,6 @@ <element name="storeGrpWebsiteDropdown" type="select" selector="#group_website_id"/> <element name="storeGrpNameTextField" type="input" selector="#group_name"/> <element name="storeGrpCodeTextField" type="input" selector="#group_code"/> - <element name="storeRootCategoryDropdown" type="button" selector="#group_root_category_id"/> + <element name="storeRootCategoryDropdown" type="select" selector="#group_root_category_id"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewWebsiteActionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewWebsiteActionsSection.xml new file mode 100644 index 0000000000000..18d1ef2642ef8 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminNewWebsiteActionsSection.xml @@ -0,0 +1,12 @@ +<?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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminNewWebsiteActionsSection"> + <element name="saveWebsite" type="button" selector="#save" timeout="30"/> + </section> +</sections> \ No newline at end of file From d2e76685cd2e41567fd3238f45c1819ef717a2d9 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 1 Jun 2018 20:32:14 -0500 Subject: [PATCH 280/333] MAGETWO-92247: Verify that product price is displayed on additional storefront if product is disabled for default store view - skip test --- .../Test/ConfigurableProductPriceAdditionalStoreViewTest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 56a9020c3a14d..0fb1e22e48e05 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -15,8 +15,7 @@ <description value="Configurable product price should not disappear for additional stores on frontEnd if disabled for default store"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-92247"/> - <group value="configurable"/> - <group value="product"/> + <group value="skip"/> </annotations> <before> From 3c0946d36ee827530f410eacbd7d5fb93a725ff8 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Fri, 1 Jun 2018 17:12:50 -0500 Subject: [PATCH 281/333] MAGETWO-91436: Custom Options are corruputed when saving product to a different website - added functional test to cover the usecase --- ...ductWithCustomOptionsSecondWebsiteTest.xml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml new file mode 100644 index 0000000000000..cc33059dd10b6 --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="SaveProductWithCustomOptionsAdditionalWebsiteTest"> + <annotations> + <features value="Save a product with Custom Options and assign to a different website"/> + <stories value="Purchase a product with Custom Options of different types"/> + <title value="You should be able to save a product with custom options assigned to a different website"/> + <description value="Custom Options should not be split when saving the product after assigning to a different website"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-91436"/> + <group value="product"/> + </annotations> + + <after> + <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"/> + + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="addnewWebsite"/> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="addNewStoreGroup"/> + + <!--Create Store view --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption userInput="1" selector="{{AdminNewStoreSection.statusDropdown}}" stepKey="enableStoreViewStatus"/> + <click selector="{{AdminStoresMainActionsSection.saveButton}}" stepKey="clickStoreViewSaveButton"/> + <waitForElementVisible selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" stepKey="waitForAcceptNewStoreViewCreationModal" /> + <conditionalClick selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" dependentSelector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" visible="true" stepKey="AcceptNewStoreViewCreation"/> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReolad"/> + <see userInput="You saved the store view." stepKey="seeSaveMessage" /> + + <!--Create a Simple Product with Custom Options --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + + <!--<click selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" stepKey="openCustomOptionsSection"/>--> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/> + <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> + <waitForPageLoad stepKey="waitAfterAddOption"/> + <fillField selector="input[name='product[options][0][title]']" userInput="Radio Option" stepKey="fillOptionTitle"/> + <click selector=".admin__dynamic-rows[data-index='options'] .action-select" stepKey="openOptionTypeDropDown"/> + <click selector=".admin__dynamic-rows[data-index='options'] .action-menu._active li:nth-of-type(3) li:nth-of-type(2)" stepKey="selectRadioButtonType"/> + + <!--Add Option Values --> + <click selector="{{AdminProductCustomizableOptionsSection.clickAddValue('Radio Option')}}" stepKey="clickAddValue1"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '0')}}" userInput="option 1" stepKey="fillOptionValueTitle1"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice('Radio Option', '0')}}" userInput="5" stepKey="fillOptionValuePrice1"/> + + <click selector="{{AdminProductCustomizableOptionsSection.clickAddValue('Radio Option')}}" stepKey="clickAddValue2"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '1')}}" userInput="option 2" stepKey="fillOptionValueTitle2"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice('Radio Option', '1')}}" userInput="6" stepKey="fillOptionValuePrice2"/> + + <click selector="{{AdminProductCustomizableOptionsSection.clickAddValue('Radio Option')}}" stepKey="clickAddValue3"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '2')}}" userInput="option 3" stepKey="fillOptionValueTitle3"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice('Radio Option', '2')}}" userInput="7" stepKey="fillOptionValuePrice3"/> + + <!--Save the product with custom options --> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <waitForLoadingMaskToDisappear stepKey="waitProductPageSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeProductSavedMessage"/> + + <!-- Add this product to second website --> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsitesSection1"/> + <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForLoadingMaskToDisappear stepKey="waitForProductPagetoSaveAgain"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageAgain"/> + + <click selector="{{AdminProductCustomizableOptionsSection.customezableOptions}}" stepKey="openCustomOptionsSection2"/> + <seeNumberOfElements selector=".admin__dynamic-rows[data-index='values'] tr.data-row" userInput="3" stepKey="see4RowsOfOptions"/> + + </test> +</tests> \ No newline at end of file From eca36b2809a25e42d2f070caca533b163e48bad0 Mon Sep 17 00:00:00 2001 From: Ankur Raiyani <ankur@seepossible.com> Date: Sat, 2 Jun 2018 09:40:00 +0530 Subject: [PATCH 282/333] Merge branch '2.3-develop' of https://github.com/magento/magento2 into 2.3-develop # Conflicts: # app/code/Magento/Sales/etc/di.xml --- app/code/Magento/Sales/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index ab758f4d4c15f..ce2948983edbe 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -686,7 +686,7 @@ <item name="shipping_address" xsi:type="object">ShippingAddressAggregator</item> <item name="shipping_information" xsi:type="string">sales_order.shipping_description</item> <item name="subtotal" xsi:type="string">sales_invoice.base_subtotal</item> - <item name="shipping_and_handling" xsi:type="string">sales_order.base_shipping_amount</item> + <item name="shipping_and_handling" xsi:type="string">sales_invoice.base_shipping_amount</item> <item name="base_grand_total" xsi:type="string">sales_invoice.base_grand_total</item> <item name="grand_total" xsi:type="string">sales_invoice.grand_total</item> <item name="created_at" xsi:type="string">sales_invoice.created_at</item> From ac220b7845735567a75108593cf19456a5b21cb7 Mon Sep 17 00:00:00 2001 From: Ankur Raiyani <ankur@seepossible.com> Date: Sat, 2 Jun 2018 09:49:58 +0530 Subject: [PATCH 283/333] Updated 2.3-develop patch branch --- app/code/Magento/Sales/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index ce2948983edbe..ab758f4d4c15f 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -686,7 +686,7 @@ <item name="shipping_address" xsi:type="object">ShippingAddressAggregator</item> <item name="shipping_information" xsi:type="string">sales_order.shipping_description</item> <item name="subtotal" xsi:type="string">sales_invoice.base_subtotal</item> - <item name="shipping_and_handling" xsi:type="string">sales_invoice.base_shipping_amount</item> + <item name="shipping_and_handling" xsi:type="string">sales_order.base_shipping_amount</item> <item name="base_grand_total" xsi:type="string">sales_invoice.base_grand_total</item> <item name="grand_total" xsi:type="string">sales_invoice.grand_total</item> <item name="created_at" xsi:type="string">sales_invoice.created_at</item> From 12041e1ec05bd78cbfa220d4f98dbf50e1889d6f Mon Sep 17 00:00:00 2001 From: Ankur Raiyani <ankur@seepossible.com> Date: Sat, 2 Jun 2018 09:51:13 +0530 Subject: [PATCH 284/333] Updated 2.3-develop patch branch --- app/code/Magento/Sales/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index ab758f4d4c15f..ce2948983edbe 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -686,7 +686,7 @@ <item name="shipping_address" xsi:type="object">ShippingAddressAggregator</item> <item name="shipping_information" xsi:type="string">sales_order.shipping_description</item> <item name="subtotal" xsi:type="string">sales_invoice.base_subtotal</item> - <item name="shipping_and_handling" xsi:type="string">sales_order.base_shipping_amount</item> + <item name="shipping_and_handling" xsi:type="string">sales_invoice.base_shipping_amount</item> <item name="base_grand_total" xsi:type="string">sales_invoice.base_grand_total</item> <item name="grand_total" xsi:type="string">sales_invoice.grand_total</item> <item name="created_at" xsi:type="string">sales_invoice.created_at</item> From ff02c919a932658795afbb3654c6b0e738dedd47 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sat, 2 Jun 2018 22:52:37 +0300 Subject: [PATCH 285/333] Add missing property, fixed incorrect proprty type, add missing throws to PHPDocs --- app/code/Magento/Ui/Model/Export/ConvertToCsv.php | 10 +++++++++- app/code/Magento/Ui/Model/Export/ConvertToXml.php | 12 +++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/Model/Export/ConvertToCsv.php b/app/code/Magento/Ui/Model/Export/ConvertToCsv.php index e8136c7520054..40b10749db21e 100644 --- a/app/code/Magento/Ui/Model/Export/ConvertToCsv.php +++ b/app/code/Magento/Ui/Model/Export/ConvertToCsv.php @@ -6,8 +6,10 @@ namespace Magento\Ui\Model\Export; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Ui\Component\MassAction\Filter; /** @@ -16,7 +18,7 @@ class ConvertToCsv { /** - * @var DirectoryList + * @var WriteInterface */ protected $directory; @@ -30,11 +32,17 @@ class ConvertToCsv */ protected $pageSize = null; + /** + * @var Filter + */ + protected $filter; + /** * @param Filesystem $filesystem * @param Filter $filter * @param MetadataProvider $metadataProvider * @param int $pageSize + * @throws FileSystemException */ public function __construct( Filesystem $filesystem, diff --git a/app/code/Magento/Ui/Model/Export/ConvertToXml.php b/app/code/Magento/Ui/Model/Export/ConvertToXml.php index fca5f10126765..5f6e45a948f24 100644 --- a/app/code/Magento/Ui/Model/Export/ConvertToXml.php +++ b/app/code/Magento/Ui/Model/Export/ConvertToXml.php @@ -10,8 +10,10 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Convert\Excel; use Magento\Framework\Convert\ExcelFactory; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Ui\Component\MassAction\Filter; /** @@ -20,7 +22,7 @@ class ConvertToXml { /** - * @var DirectoryList + * @var WriteInterface */ protected $directory; @@ -49,12 +51,18 @@ class ConvertToXml */ protected $fields; + /** + * @var Filter + */ + protected $filter; + /** * @param Filesystem $filesystem * @param Filter $filter * @param MetadataProvider $metadataProvider * @param ExcelFactory $excelFactory * @param SearchResultIteratorFactory $iteratorFactory + * @throws FileSystemException */ public function __construct( Filesystem $filesystem, @@ -87,6 +95,7 @@ protected function getOptions() * Returns DB fields list * * @return array + * @throws LocalizedException */ protected function getFields() { @@ -102,6 +111,7 @@ protected function getFields() * * @param DocumentInterface $document * @return array + * @throws LocalizedException */ public function getRowData(DocumentInterface $document) { From f0eb5406d3450c54aedcac24d3c01144da5862bc Mon Sep 17 00:00:00 2001 From: Danny Verkade <danny@cream.nl> Date: Sat, 2 Jun 2018 23:19:09 +0200 Subject: [PATCH 286/333] Fixed typos: - Changed disply to display - Changed addtional to additional - Changed beging to begin --- .../Adminhtml/Order/Create/Sidebar/AbstractSidebar.php | 2 +- .../Ui/view/base/web/js/form/components/fieldset.js | 8 ++++---- .../Magento/Ui/view/base/web/js/form/components/group.js | 8 ++++---- setup/pub/angular-sanitize/angular-sanitize.js | 4 ++-- setup/pub/angular-sanitize/angular-sanitize.min.js.map | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) 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 e0b442036af90..06c6a9eb0652b 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 @@ -71,7 +71,7 @@ public function canDisplay() } /** - * Retrieve disply item qty availability + * Retrieve display item qty availability * * @return false */ diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index 371a0773f9d7e..b729dd3127d90 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -115,14 +115,14 @@ define([ * @returns {Group} Chainable. */ _setClasses: function () { - var addtional = this.additionalClasses, + var additional = this.additionalClasses, classes; - if (_.isString(addtional)) { - addtional = this.additionalClasses.split(' '); + if (_.isString(additional)) { + additional = this.additionalClasses.split(' '); classes = this.additionalClasses = {}; - addtional.forEach(function (name) { + additional.forEach(function (name) { classes[name] = true; }, this); } diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/group.js b/app/code/Magento/Ui/view/base/web/js/form/components/group.js index f6937cffe1f2f..8c5950f7e2fa1 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/group.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/group.js @@ -58,14 +58,14 @@ define([ * @returns {Group} Chainable. */ _setClasses: function () { - var addtional = this.additionalClasses, + var additional = this.additionalClasses, classes; - if (_.isString(addtional)) { - addtional = this.additionalClasses.split(' '); + if (_.isString(additional)) { + additional = this.additionalClasses.split(' '); classes = this.additionalClasses = {}; - addtional.forEach(function (name) { + additional.forEach(function (name) { classes[name] = true; }, this); } diff --git a/setup/pub/angular-sanitize/angular-sanitize.js b/setup/pub/angular-sanitize/angular-sanitize.js index b7ec2820dd7fe..6004460cd17eb 100644 --- a/setup/pub/angular-sanitize/angular-sanitize.js +++ b/setup/pub/angular-sanitize/angular-sanitize.js @@ -159,7 +159,7 @@ END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, BEGIN_TAG_REGEXP = /^</, - BEGING_END_TAGE_REGEXP = /^<\s*\//, + BEGIN_END_TAGE_REGEXP = /^<\s*\//, COMMENT_REGEXP = /<!--(.*?)-->/g, DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i, CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g, @@ -260,7 +260,7 @@ chars = false; } // end tag - } else if ( BEGING_END_TAGE_REGEXP.test(html) ) { + } else if ( BEGIN_END_TAGE_REGEXP.test(html) ) { match = html.match( END_TAG_REGEXP ); if ( match ) { diff --git a/setup/pub/angular-sanitize/angular-sanitize.min.js.map b/setup/pub/angular-sanitize/angular-sanitize.min.js.map index 0c993a6ed2afb..0310ddce9c937 100644 --- a/setup/pub/angular-sanitize/angular-sanitize.min.js.map +++ b/setup/pub/angular-sanitize/angular-sanitize.min.js.map @@ -4,5 +4,5 @@ "lineCount":13, "mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAA6B,CAiJtCC,QAASA,EAAY,CAACC,CAAD,CAAQ,CAC3B,IAAIC,EAAM,EACGC,EAAAC,CAAmBF,CAAnBE,CAAwBN,CAAAO,KAAxBD,CACbH,MAAA,CAAaA,CAAb,CACA,OAAOC,EAAAI,KAAA,CAAS,EAAT,CAJoB,CAmE7BC,QAASA,EAAO,CAACC,CAAD,CAAM,CAAA,IAChBC,EAAM,EAAIC,EAAAA,CAAQF,CAAAG,MAAA,CAAU,GAAV,CAAtB,KAAsCC,CACtC,KAAKA,CAAL,CAAS,CAAT,CAAYA,CAAZ,CAAgBF,CAAAG,OAAhB,CAA8BD,CAAA,EAA9B,CAAmCH,CAAA,CAAIC,CAAA,CAAME,CAAN,CAAJ,CAAA,CAAgB,CAAA,CACnD,OAAOH,EAHa,CAmBtBK,QAASA,EAAU,CAAEC,CAAF,CAAQC,CAAR,CAAkB,CAiFnCC,QAASA,EAAa,CAAEC,CAAF,CAAOC,CAAP,CAAgBC,CAAhB,CAAsBC,CAAtB,CAA8B,CAClDF,CAAA,CAAUrB,CAAAwB,UAAA,CAAkBH,CAAlB,CACV,IAAKI,CAAA,CAAeJ,CAAf,CAAL,CACE,IAAA,CAAQK,CAAAC,KAAA,EAAR,EAAwBC,CAAA,CAAgBF,CAAAC,KAAA,EAAhB,CAAxB,CAAA,CACEE,CAAA,CAAa,EAAb,CAAiBH,CAAAC,KAAA,EAAjB,CAICG,EAAA,CAAwBT,CAAxB,CAAL,EAA0CK,CAAAC,KAAA,EAA1C,EAA0DN,CAA1D,EACEQ,CAAA,CAAa,EAAb,CAAiBR,CAAjB,CAKF,EAFAE,CAEA,CAFQQ,CAAA,CAAcV,CAAd,CAER,EAFmC,CAAC,CAACE,CAErC,GACEG,CAAAM,KAAA,CAAYX,CAAZ,CAEF,KAAIY,EAAQ,EAEZX,EAAAY,QAAA,CAAaC,CAAb,CACE,QAAQ,CAACC,CAAD,CAAQC,CAAR,CAAcC,CAAd,CAAiCC,CAAjC,CAAoDC,CAApD,CAAmE,CAMzEP,CAAA,CAAMI,CAAN,CAAA,CAAcI,CAAA,CALFH,CAKE,EAJTC,CAIS,EAHTC,CAGS,EAFT,EAES,CAN2D,CAD7E,CASItB,EAAAwB,MAAJ,EAAmBxB,CAAAwB,MAAA,CAAerB,CAAf,CAAwBY,CAAxB,CAA+BV,CAA/B,CA5B+B,CA+BpDM,QAASA,EAAW,CAAET,CAAF,CAAOC,CAAP,CAAiB,CAAA,IAC/BsB,EAAM,CADyB,CACtB7B,CAEb,IADAO,CACA,CADUrB,CAAAwB,UAAA,CAAkBH,CAAlB,CACV,CAEE,IAAMsB,CAAN,CAAYjB,CAAAX,OAAZ,CAA2B,CAA3B,CAAqC,CAArC,EAA8B4B,CAA9B,EACOjB,CAAA,CAAOiB,CAAP,CADP,EACuBtB,CADvB,CAAwCsB,CAAA,EAAxC;AAIF,GAAY,CAAZ,EAAKA,CAAL,CAAgB,CAEd,IAAM7B,CAAN,CAAUY,CAAAX,OAAV,CAAyB,CAAzB,CAA4BD,CAA5B,EAAiC6B,CAAjC,CAAsC7B,CAAA,EAAtC,CACMI,CAAA0B,IAAJ,EAAiB1B,CAAA0B,IAAA,CAAalB,CAAA,CAAOZ,CAAP,CAAb,CAGnBY,EAAAX,OAAA,CAAe4B,CAND,CATmB,CAhHF,IAC/BE,CAD+B,CACxB1C,CADwB,CACVuB,EAAQ,EADE,CACEC,EAAOV,CAG5C,KAFAS,CAAAC,KAEA,CAFamB,QAAQ,EAAG,CAAE,MAAOpB,EAAA,CAAOA,CAAAX,OAAP,CAAsB,CAAtB,CAAT,CAExB,CAAQE,CAAR,CAAA,CAAe,CACbd,CAAA,CAAQ,CAAA,CAGR,IAAMuB,CAAAC,KAAA,EAAN,EAAuBoB,CAAA,CAAiBrB,CAAAC,KAAA,EAAjB,CAAvB,CAmDEV,CASA,CATOA,CAAAiB,QAAA,CAAiBc,MAAJ,CAAW,kBAAX,CAAgCtB,CAAAC,KAAA,EAAhC,CAA+C,QAA/C,CAAyD,GAAzD,CAAb,CACL,QAAQ,CAACsB,CAAD,CAAMC,CAAN,CAAW,CACjBA,CAAA,CAAOA,CAAAhB,QAAA,CAAaiB,CAAb,CAA6B,IAA7B,CAAAjB,QAAA,CAA2CkB,CAA3C,CAAyD,IAAzD,CAEHlC,EAAAf,MAAJ,EAAmBe,CAAAf,MAAA,CAAesC,CAAA,CAAeS,CAAf,CAAf,CAEnB,OAAO,EALU,CADd,CASP,CAAArB,CAAA,CAAa,EAAb,CAAiBH,CAAAC,KAAA,EAAjB,CA5DF,KAAyD,CAGvD,GAA8B,CAA9B,GAAKV,CAAAoC,QAAA,CAAa,SAAb,CAAL,CAEER,CAEA,CAFQ5B,CAAAoC,QAAA,CAAa,IAAb,CAAmB,CAAnB,CAER,CAAc,CAAd,EAAKR,CAAL,EAAmB5B,CAAAqC,YAAA,CAAiB,QAAjB,CAAwBT,CAAxB,CAAnB,GAAsDA,CAAtD,GACM3B,CAAAqC,QAEJ,EAFqBrC,CAAAqC,QAAA,CAAiBtC,CAAAuC,UAAA,CAAgB,CAAhB,CAAmBX,CAAnB,CAAjB,CAErB,CADA5B,CACA,CADOA,CAAAuC,UAAA,CAAgBX,CAAhB,CAAwB,CAAxB,CACP,CAAA1C,CAAA,CAAQ,CAAA,CAHV,CAJF,KAUO,IAAKsD,CAAAC,KAAA,CAAoBzC,CAApB,CAAL,CAGL,IAFAmB,CAEA,CAFQnB,CAAAmB,MAAA,CAAYqB,CAAZ,CAER,CACExC,CACA;AADOA,CAAAiB,QAAA,CAAcE,CAAA,CAAM,CAAN,CAAd,CAAyB,EAAzB,CACP,CAAAjC,CAAA,CAAQ,CAAA,CAFV,CAHK,IAQA,IAAKwD,CAAAD,KAAA,CAA4BzC,CAA5B,CAAL,CAGL,IAFAmB,CAEA,CAFQnB,CAAAmB,MAAA,CAAYwB,CAAZ,CAER,CACE3C,CAEA,CAFOA,CAAAuC,UAAA,CAAgBpB,CAAA,CAAM,CAAN,CAAArB,OAAhB,CAEP,CADAqB,CAAA,CAAM,CAAN,CAAAF,QAAA,CAAkB0B,CAAlB,CAAkC/B,CAAlC,CACA,CAAA1B,CAAA,CAAQ,CAAA,CAHV,CAHK,IAUK0D,EAAAH,KAAA,CAAsBzC,CAAtB,CAAL,GACLmB,CADK,CACGnB,CAAAmB,MAAA,CAAY0B,CAAZ,CADH,IAIH7C,CAEA,CAFOA,CAAAuC,UAAA,CAAgBpB,CAAA,CAAM,CAAN,CAAArB,OAAhB,CAEP,CADAqB,CAAA,CAAM,CAAN,CAAAF,QAAA,CAAkB4B,CAAlB,CAAoC3C,CAApC,CACA,CAAAhB,CAAA,CAAQ,CAAA,CANL,CAUFA,EAAL,GACE0C,CAKA,CALQ5B,CAAAoC,QAAA,CAAa,GAAb,CAKR,CAHIH,CAGJ,CAHmB,CAAR,CAAAL,CAAA,CAAY5B,CAAZ,CAAmBA,CAAAuC,UAAA,CAAgB,CAAhB,CAAmBX,CAAnB,CAG9B,CAFA5B,CAEA,CAFe,CAAR,CAAA4B,CAAA,CAAY,EAAZ,CAAiB5B,CAAAuC,UAAA,CAAgBX,CAAhB,CAExB,CAAI3B,CAAAf,MAAJ,EAAmBe,CAAAf,MAAA,CAAesC,CAAA,CAAeS,CAAf,CAAf,CANrB,CAzCuD,CA+DzD,GAAKjC,CAAL,EAAaU,CAAb,CACE,KAAMoC,EAAA,CAAgB,UAAhB,CAC4C9C,CAD5C,CAAN,CAGFU,CAAA,CAAOV,CAvEM,CA2EfY,CAAA,EA/EmC,CA2IrCY,QAASA,EAAc,CAACuB,CAAD,CAAQ,CAC7B,GAAI,CAACA,CAAL,CAAc,MAAO,EAIrB,KAAIC,EAAQC,CAAAC,KAAA,CAAaH,CAAb,CACRI,EAAAA,CAAcH,CAAA,CAAM,CAAN,CAClB,KAAII,EAAaJ,CAAA,CAAM,CAAN,CAEjB,IADIK,CACJ,CADcL,CAAA,CAAM,CAAN,CACd,CACEM,CAAAC,UAKA,CALoBF,CAAApC,QAAA,CAAgB,IAAhB,CAAqB,MAArB,CAKpB,CAAAoC,CAAA,CAAU,aAAA,EAAiBC,EAAjB,CACRA,CAAAE,YADQ,CACgBF,CAAAG,UAE5B,OAAON,EAAP,CAAqBE,CAArB,CAA+BD,CAlBF,CA4B/BM,QAASA,EAAc,CAACX,CAAD,CAAQ,CAC7B,MAAOA,EAAA9B,QAAA,CACG,IADH;AACS,OADT,CAAAA,QAAA,CAEG0C,CAFH,CAE4B,QAAQ,CAACZ,CAAD,CAAO,CAC9C,MAAO,IAAP,CAAcA,CAAAa,WAAA,CAAiB,CAAjB,CAAd,CAAoC,GADU,CAF3C,CAAA3C,QAAA,CAKG,IALH,CAKS,MALT,CAAAA,QAAA,CAMG,IANH,CAMS,MANT,CADsB,CAoB/B7B,QAASA,EAAkB,CAACD,CAAD,CAAM0E,CAAN,CAAmB,CAC5C,IAAIC,EAAS,CAAA,CAAb,CACIC,EAAMhF,CAAAiF,KAAA,CAAa7E,CAAb,CAAkBA,CAAA4B,KAAlB,CACV,OAAO,OACEU,QAAQ,CAACtB,CAAD,CAAMa,CAAN,CAAaV,CAAb,CAAmB,CAChCH,CAAA,CAAMpB,CAAAwB,UAAA,CAAkBJ,CAAlB,CACD2D,EAAAA,CAAL,EAAehC,CAAA,CAAgB3B,CAAhB,CAAf,GACE2D,CADF,CACW3D,CADX,CAGK2D,EAAL,EAAsC,CAAA,CAAtC,GAAeG,CAAA,CAAc9D,CAAd,CAAf,GACE4D,CAAA,CAAI,GAAJ,CAcA,CAbAA,CAAA,CAAI5D,CAAJ,CAaA,CAZApB,CAAAmF,QAAA,CAAgBlD,CAAhB,CAAuB,QAAQ,CAAC+B,CAAD,CAAQoB,CAAR,CAAY,CACzC,IAAIC,EAAKrF,CAAAwB,UAAA,CAAkB4D,CAAlB,CAAT,CACIE,EAAmB,KAAnBA,GAAWlE,CAAXkE,EAAqC,KAArCA,GAA4BD,CAA5BC,EAAyD,YAAzDA,GAAgDD,CAC3B,EAAA,CAAzB,GAAIE,CAAA,CAAWF,CAAX,CAAJ,EACsB,CAAA,CADtB,GACGG,CAAA,CAASH,CAAT,CADH,EAC8B,CAAAP,CAAA,CAAad,CAAb,CAAoBsB,CAApB,CAD9B,GAEEN,CAAA,CAAI,GAAJ,CAIA,CAHAA,CAAA,CAAII,CAAJ,CAGA,CAFAJ,CAAA,CAAI,IAAJ,CAEA,CADAA,CAAA,CAAIL,CAAA,CAAeX,CAAf,CAAJ,CACA,CAAAgB,CAAA,CAAI,GAAJ,CANF,CAHyC,CAA3C,CAYA,CAAAA,CAAA,CAAIzD,CAAA,CAAQ,IAAR,CAAe,GAAnB,CAfF,CALgC,CAD7B,KAwBAqB,QAAQ,CAACxB,CAAD,CAAK,CACdA,CAAA,CAAMpB,CAAAwB,UAAA,CAAkBJ,CAAlB,CACD2D,EAAL,EAAsC,CAAA,CAAtC,GAAeG,CAAA,CAAc9D,CAAd,CAAf,GACE4D,CAAA,CAAI,IAAJ,CAEA,CADAA,CAAA,CAAI5D,CAAJ,CACA,CAAA4D,CAAA,CAAI,GAAJ,CAHF,CAKI5D,EAAJ,EAAW2D,CAAX,GACEA,CADF,CACW,CAAA,CADX,CAPc,CAxBb,OAmCE5E,QAAQ,CAACA,CAAD,CAAO,CACb4E,CAAL;AACEC,CAAA,CAAIL,CAAA,CAAexE,CAAf,CAAJ,CAFgB,CAnCjB,CAHqC,CAha9C,IAAI4D,EAAkB/D,CAAAyF,SAAA,CAAiB,WAAjB,CAAtB,CAwJI3B,EACG,4FAzJP,CA0JEF,EAAiB,2BA1JnB,CA2JEzB,EAAc,yEA3JhB,CA4JE0B,EAAmB,IA5JrB,CA6JEF,EAAyB,SA7J3B,CA8JER,EAAiB,qBA9JnB,CA+JEM,EAAiB,qBA/JnB,CAgKEL,EAAe,yBAhKjB,CAkKEwB,EAA0B,gBAlK5B,CA2KI7C,EAAetB,CAAA,CAAQ,wBAAR,CAIfiF,EAAAA,CAA8BjF,CAAA,CAAQ,gDAAR,CAC9BkF,EAAAA,CAA+BlF,CAAA,CAAQ,OAAR,CADnC,KAEIqB,EAAyB9B,CAAA4F,OAAA,CAAe,EAAf,CACeD,CADf,CAEeD,CAFf,CAF7B,CAOIjE,EAAgBzB,CAAA4F,OAAA,CAAe,EAAf,CAAmBF,CAAnB,CAAgDjF,CAAA,CAAQ,4KAAR,CAAhD,CAPpB;AAYImB,EAAiB5B,CAAA4F,OAAA,CAAe,EAAf,CAAmBD,CAAnB,CAAiDlF,CAAA,CAAQ,2JAAR,CAAjD,CAZrB,CAkBIsC,EAAkBtC,CAAA,CAAQ,cAAR,CAlBtB,CAoBIyE,EAAgBlF,CAAA4F,OAAA,CAAe,EAAf,CACe7D,CADf,CAEeN,CAFf,CAGeG,CAHf,CAIeE,CAJf,CApBpB,CA2BI0D,EAAW/E,CAAA,CAAQ,0CAAR,CA3Bf,CA4BI8E,EAAavF,CAAA4F,OAAA,CAAe,EAAf,CAAmBJ,CAAnB,CAA6B/E,CAAA,CAC1C,ySAD0C,CAA7B,CA5BjB;AA0LI8D,EAAUsB,QAAAC,cAAA,CAAuB,KAAvB,CA1Ld,CA2LI5B,EAAU,wBAsGdlE,EAAA+F,OAAA,CAAe,YAAf,CAA6B,EAA7B,CAAAC,SAAA,CAA0C,WAA1C,CA7UAC,QAA0B,EAAG,CAC3B,IAAAC,KAAA,CAAY,CAAC,eAAD,CAAkB,QAAQ,CAACC,CAAD,CAAgB,CACpD,MAAO,SAAQ,CAAClF,CAAD,CAAO,CACpB,IAAIb,EAAM,EACVY,EAAA,CAAWC,CAAX,CAAiBZ,CAAA,CAAmBD,CAAnB,CAAwB,QAAQ,CAACgG,CAAD,CAAMd,CAAN,CAAe,CAC9D,MAAO,CAAC,SAAA5B,KAAA,CAAeyC,CAAA,CAAcC,CAAd,CAAmBd,CAAnB,CAAf,CADsD,CAA/C,CAAjB,CAGA,OAAOlF,EAAAI,KAAA,CAAS,EAAT,CALa,CAD8B,CAA1C,CADe,CA6U7B,CAuGAR,EAAA+F,OAAA,CAAe,YAAf,CAAAM,OAAA,CAAoC,OAApC,CAA6C,CAAC,WAAD,CAAc,QAAQ,CAACC,CAAD,CAAY,CAAA,IACzEC,EACE,mEAFuE,CAGzEC,EAAgB,UAEpB,OAAO,SAAQ,CAACtD,CAAD,CAAOuD,CAAP,CAAe,CAoB5BC,QAASA,EAAO,CAACxD,CAAD,CAAO,CAChBA,CAAL,EAGAjC,CAAAe,KAAA,CAAU9B,CAAA,CAAagD,CAAb,CAAV,CAJqB,CAOvByD,QAASA,EAAO,CAACC,CAAD,CAAM1D,CAAN,CAAY,CAC1BjC,CAAAe,KAAA,CAAU,KAAV,CACIhC,EAAA6G,UAAA,CAAkBJ,CAAlB,CAAJ;CACExF,CAAAe,KAAA,CAAU,UAAV,CAEA,CADAf,CAAAe,KAAA,CAAUyE,CAAV,CACA,CAAAxF,CAAAe,KAAA,CAAU,IAAV,CAHF,CAKAf,EAAAe,KAAA,CAAU,QAAV,CACAf,EAAAe,KAAA,CAAU4E,CAAV,CACA3F,EAAAe,KAAA,CAAU,IAAV,CACA0E,EAAA,CAAQxD,CAAR,CACAjC,EAAAe,KAAA,CAAU,MAAV,CAX0B,CA1B5B,GAAI,CAACkB,CAAL,CAAW,MAAOA,EAMlB,KALA,IAAId,CAAJ,CACI0E,EAAM5D,CADV,CAEIjC,EAAO,EAFX,CAGI2F,CAHJ,CAII9F,CACJ,CAAQsB,CAAR,CAAgB0E,CAAA1E,MAAA,CAAUmE,CAAV,CAAhB,CAAA,CAEEK,CAMA,CANMxE,CAAA,CAAM,CAAN,CAMN,CAJIA,CAAA,CAAM,CAAN,CAIJ,EAJgBA,CAAA,CAAM,CAAN,CAIhB,GAJ0BwE,CAI1B,CAJgC,SAIhC,CAJ4CA,CAI5C,EAHA9F,CAGA,CAHIsB,CAAAS,MAGJ,CAFA6D,CAAA,CAAQI,CAAAC,OAAA,CAAW,CAAX,CAAcjG,CAAd,CAAR,CAEA,CADA6F,CAAA,CAAQC,CAAR,CAAaxE,CAAA,CAAM,CAAN,CAAAF,QAAA,CAAiBsE,CAAjB,CAAgC,EAAhC,CAAb,CACA,CAAAM,CAAA,CAAMA,CAAAtD,UAAA,CAAc1C,CAAd,CAAkBsB,CAAA,CAAM,CAAN,CAAArB,OAAlB,CAER2F,EAAA,CAAQI,CAAR,CACA,OAAOR,EAAA,CAAUrF,CAAAT,KAAA,CAAU,EAAV,CAAV,CAlBqB,CAL+C,CAAlC,CAA7C,CAzjBsC,CAArC,CAAA,CA0mBET,MA1mBF,CA0mBUA,MAAAC,QA1mBV;", "sources":["angular-sanitize.js"], -"names":["window","angular","undefined","sanitizeText","chars","buf","htmlSanitizeWriter","writer","noop","join","makeMap","str","obj","items","split","i","length","htmlParser","html","handler","parseStartTag","tag","tagName","rest","unary","lowercase","blockElements","stack","last","inlineElements","parseEndTag","optionalEndTagElements","voidElements","push","attrs","replace","ATTR_REGEXP","match","name","doubleQuotedValue","singleQuotedValue","unquotedValue","decodeEntities","start","pos","end","index","stack.last","specialElements","RegExp","all","text","COMMENT_REGEXP","CDATA_REGEXP","indexOf","lastIndexOf","comment","substring","DOCTYPE_REGEXP","test","BEGING_END_TAGE_REGEXP","END_TAG_REGEXP","BEGIN_TAG_REGEXP","START_TAG_REGEXP","$sanitizeMinErr","value","parts","spaceRe","exec","spaceBefore","spaceAfter","content","hiddenPre","innerHTML","textContent","innerText","encodeEntities","NON_ALPHANUMERIC_REGEXP","charCodeAt","uriValidator","ignore","out","bind","validElements","forEach","key","lkey","isImage","validAttrs","uriAttrs","$$minErr","optionalEndTagBlockElements","optionalEndTagInlineElements","extend","document","createElement","module","provider","$SanitizeProvider","$get","$$sanitizeUri","uri","filter","$sanitize","LINKY_URL_REGEXP","MAILTO_REGEXP","target","addText","addLink","url","isDefined","raw","substr"] +"names":["window","angular","undefined","sanitizeText","chars","buf","htmlSanitizeWriter","writer","noop","join","makeMap","str","obj","items","split","i","length","htmlParser","html","handler","parseStartTag","tag","tagName","rest","unary","lowercase","blockElements","stack","last","inlineElements","parseEndTag","optionalEndTagElements","voidElements","push","attrs","replace","ATTR_REGEXP","match","name","doubleQuotedValue","singleQuotedValue","unquotedValue","decodeEntities","start","pos","end","index","stack.last","specialElements","RegExp","all","text","COMMENT_REGEXP","CDATA_REGEXP","indexOf","lastIndexOf","comment","substring","DOCTYPE_REGEXP","test","BEGIN_END_TAGE_REGEXP","END_TAG_REGEXP","BEGIN_TAG_REGEXP","START_TAG_REGEXP","$sanitizeMinErr","value","parts","spaceRe","exec","spaceBefore","spaceAfter","content","hiddenPre","innerHTML","textContent","innerText","encodeEntities","NON_ALPHANUMERIC_REGEXP","charCodeAt","uriValidator","ignore","out","bind","validElements","forEach","key","lkey","isImage","validAttrs","uriAttrs","$$minErr","optionalEndTagBlockElements","optionalEndTagInlineElements","extend","document","createElement","module","provider","$SanitizeProvider","$get","$$sanitizeUri","uri","filter","$sanitize","LINKY_URL_REGEXP","MAILTO_REGEXP","target","addText","addLink","url","isDefined","raw","substr"] } \ No newline at end of file From 04583ac165cd775864e2ace38c5196c12ffc4ae8 Mon Sep 17 00:00:00 2001 From: Amjad M <amjad@codilar.com> Date: Sat, 19 May 2018 13:22:43 +0530 Subject: [PATCH 287/333] fixed Submitting search form (mini) with enter key fires event handlers bound by jquery twice --- app/code/Magento/Search/view/frontend/web/form-mini.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Search/view/frontend/web/form-mini.js b/app/code/Magento/Search/view/frontend/web/form-mini.js index de16305bbbe8d..c064aba86caf9 100644 --- a/app/code/Magento/Search/view/frontend/web/form-mini.js +++ b/app/code/Magento/Search/view/frontend/web/form-mini.js @@ -226,6 +226,7 @@ define([ case $.ui.keyCode.ENTER: this.searchForm.trigger('submit'); + e.preventDefault(); break; case $.ui.keyCode.DOWN: From 09459f6f92b800a8e3de7b91077592e587271df2 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 3 Jun 2018 10:28:54 +0300 Subject: [PATCH 288/333] Fix HTML syntax in report.phtml error template --- pub/errors/default/report.phtml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pub/errors/default/report.phtml b/pub/errors/default/report.phtml index f907e73149d41..546b96605ec1b 100644 --- a/pub/errors/default/report.phtml +++ b/pub/errors/default/report.phtml @@ -36,25 +36,25 @@ </div> <div class="field lastname required"> <label for="lastname" class="label">Last Name</label> - <div class=control"> + <div class="control"> <input type="text" name="lastname" id="lastname" value="<?= $this->postData['lastName'] ?>" title="Last Name" class="required-entry input-text" /> </div> </div> <div class="field email required"> <label for="email_address" class="label">Email Address</label> - <div class=control"> + <div class="control"> <input type="text" name="email" id="email_address" value="<?= $this->postData['email'] ?>" title="Email Address" class="validate-email required-entry input-text" /> </div> </div> <div class="field telephone"> <label for="telephone" class="label">Telephone</label> - <div class=control"> + <div class="control"> <input type="text" name="telephone" id="telephone" value="<?= $this->postData['telephone'] ?>" title="Telephone" class="input-text" /> </div> </div> <div class="field comment"> <label for="comment" class="label">Comment</label> - <div class=control"> + <div class="control"> <textarea name="comment" cols="5" rows="3" class="textarea"><?= $this->postData['comment'] ?></textarea> </div> </div> From 1ad9c5c183fb1e27e4c4a3de450bb1f687cece5e Mon Sep 17 00:00:00 2001 From: saurabh-aureate <saurabh.parekh@aureatelabs.com> Date: Mon, 28 May 2018 15:52:37 +0530 Subject: [PATCH 289/333] Remove extra space from trnslation file --- app/code/Magento/Captcha/i18n/en_US.csv | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/Captcha/i18n/en_US.csv b/app/code/Magento/Captcha/i18n/en_US.csv index 3c56d3f0d393d..480107df8adfe 100644 --- a/app/code/Magento/Captcha/i18n/en_US.csv +++ b/app/code/Magento/Captcha/i18n/en_US.csv @@ -20,11 +20,7 @@ Forms,Forms "Number of Symbols","Number of Symbols" "Please specify 8 symbols at the most. Range allowed (e.g. 3-5)","Please specify 8 symbols at the most. Range allowed (e.g. 3-5)" "Symbols Used in CAPTCHA","Symbols Used in CAPTCHA" -" - Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.<br />Similar looking characters (e.g. ""i"", ""l"", ""1"") decrease chance of correct recognition by customer. - "," - Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.<br />Similar looking characters (e.g. ""i"", ""l"", ""1"") decrease chance of correct recognition by customer. - " +"Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.<br />Similar looking characters (e.g. ""i"", ""l"", ""1"") decrease chance of correct recognition by customer.","Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.<br />Similar looking characters (e.g. ""i"", ""l"", ""1"") decrease chance of correct recognition by customer." "Case Sensitive","Case Sensitive" "Enable CAPTCHA on Storefront","Enable CAPTCHA on Storefront" "CAPTCHA for ""Create user"" and ""Forgot password"" forms is always enabled if chosen.","CAPTCHA for ""Create user"" and ""Forgot password"" forms is always enabled if chosen." From 6301ed4e968ca617aef8424a5d322446859ce2e7 Mon Sep 17 00:00:00 2001 From: Julien ANQUETIL <julien.anquetil@gmail.com> Date: Sun, 3 Jun 2018 09:49:47 +0200 Subject: [PATCH 290/333] [Forwardport] Fix #13415 : Duplicated elements id --- .../view/frontend/web/template/gift-message-form.html | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/GiftMessage/view/frontend/web/template/gift-message-form.html b/app/code/Magento/GiftMessage/view/frontend/web/template/gift-message-form.html index 20739f621ecff..15a36cc0e977a 100644 --- a/app/code/Magento/GiftMessage/view/frontend/web/template/gift-message-form.html +++ b/app/code/Magento/GiftMessage/view/frontend/web/template/gift-message-form.html @@ -12,26 +12,24 @@ <div class="gift-options-content"> <fieldset class="fieldset"> <div class="field field-to"> - <label for="gift-message-whole-to" class="label"> + <label data-bind="attr: {for: 'gift-message-whole-to-' + index }" class="label"> <span data-bind="i18n: 'To:'"></span> </label> <div class="control"> <input type="text" - id="gift-message-whole-to" class="input-text" - data-bind="value: getObservable('recipient')"> + data-bind="value: getObservable('recipient'), attr: { id: 'gift-message-whole-to-' + index }"> </div> </div> <div class="field field-from"> - <label for="gift-message-whole-from" class="label"> + <label data-bind="attr: {for: 'gift-message-whole-from-' + index }" class="label"> <span data-bind="i18n: 'From:'"></span> </label> <div class="control"> <input type="text" - id="gift-message-whole-from" class="input-text" - data-bind="value: getObservable('sender')"> + data-bind="value: getObservable('sender'), attr: { id: 'gift-message-whole-from-' + index }"> </div> </div> <div class="field text"> @@ -46,7 +44,6 @@ </div> </div> </fieldset> - </div> </div> <!-- /ko --> From 83b35872de163a43a6272402810d0b12cd3417b5 Mon Sep 17 00:00:00 2001 From: saurabh-aureate <saurabh.parekh@aureatelabs.com> Date: Fri, 25 May 2018 17:26:19 +0530 Subject: [PATCH 291/333] Use stored value of method instead of calling same method again. --- .../Magento/Directory/view/frontend/templates/currency.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/view/frontend/templates/currency.phtml b/app/code/Magento/Directory/view/frontend/templates/currency.phtml index 30283effe4bb7..c01abca183d6a 100644 --- a/app/code/Magento/Directory/view/frontend/templates/currency.phtml +++ b/app/code/Magento/Directory/view/frontend/templates/currency.phtml @@ -24,7 +24,7 @@ data-mage-init='{"dropdown":{}}' data-toggle="dropdown" data-trigger-keypress-button="true"> - <strong class="language-<?= $block->escapeHtml($block->getCurrentCurrencyCode()) ?>"> + <strong class="language-<?= $block->escapeHtml($currentCurrencyCode) ?>"> <span><?= $block->escapeHtml($currentCurrencyCode) ?> - <?= @$block->escapeHtml($currencies[$currentCurrencyCode]) ?></span> </strong> </div> From 82d254b333920d275012105db915620190b7f9fa Mon Sep 17 00:00:00 2001 From: saurabh-aureate <saurabh.parekh@aureatelabs.com> Date: Fri, 25 May 2018 17:40:12 +0530 Subject: [PATCH 292/333] Use stored value of method instead of calling same method again. --- .../Magento/Marketplace/view/adminhtml/templates/partners.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Marketplace/view/adminhtml/templates/partners.phtml b/app/code/Magento/Marketplace/view/adminhtml/templates/partners.phtml index 309df6f883a49..b63bf9ebd50eb 100644 --- a/app/code/Magento/Marketplace/view/adminhtml/templates/partners.phtml +++ b/app/code/Magento/Marketplace/view/adminhtml/templates/partners.phtml @@ -11,7 +11,7 @@ $partners = $block->getPartners(); ?> <?php if ($partners) : ?> - <?php foreach ($block->getPartners() as $partner) : ?> + <?php foreach ($partners as $partner) : ?> <div class="partner"> <img class="partner-image" From 42f3f2eb1474b1eefc3cce150ce6800f6b648af7 Mon Sep 17 00:00:00 2001 From: saurabh-aureate <saurabh.parekh@aureatelabs.com> Date: Fri, 25 May 2018 19:06:09 +0530 Subject: [PATCH 293/333] Revert "Use stored value of method instead of calling same method again." This reverts commit cf9b1690b13a1698273bbc444720588040acc576. --- .../Magento/Directory/view/frontend/templates/currency.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/view/frontend/templates/currency.phtml b/app/code/Magento/Directory/view/frontend/templates/currency.phtml index c01abca183d6a..30283effe4bb7 100644 --- a/app/code/Magento/Directory/view/frontend/templates/currency.phtml +++ b/app/code/Magento/Directory/view/frontend/templates/currency.phtml @@ -24,7 +24,7 @@ data-mage-init='{"dropdown":{}}' data-toggle="dropdown" data-trigger-keypress-button="true"> - <strong class="language-<?= $block->escapeHtml($currentCurrencyCode) ?>"> + <strong class="language-<?= $block->escapeHtml($block->getCurrentCurrencyCode()) ?>"> <span><?= $block->escapeHtml($currentCurrencyCode) ?> - <?= @$block->escapeHtml($currencies[$currentCurrencyCode]) ?></span> </strong> </div> From 31e49bcd0c2d5bb0a743e0c462ef62230b153f20 Mon Sep 17 00:00:00 2001 From: saurabh-aureate <saurabh.parekh@aureatelabs.com> Date: Fri, 25 May 2018 18:22:29 +0530 Subject: [PATCH 294/333] Typo correction --- app/code/Magento/Catalog/Model/Product/Type/AbstractType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 0ab1fbab471e6..d3f0c8be6f649 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -252,7 +252,7 @@ public function getChildrenIds($parentId, $required = true) } /** - * Retrieve parent ids array by requered child + * Retrieve parent ids array by required child * * @param int|array $childId * @return array From 14bf8af3db1683cbf4aeb5ca5924d8fd119e6d5a Mon Sep 17 00:00:00 2001 From: hitesh-wagento <hitesh@wagento.com> Date: Fri, 1 Jun 2018 11:28:21 +0530 Subject: [PATCH 295/333] --- lib/web/css/source/lib/_navigation.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/web/css/source/lib/_navigation.less b/lib/web/css/source/lib/_navigation.less index 6ed3a9671ab13..56aa2e7ef86b9 100644 --- a/lib/web/css/source/lib/_navigation.less +++ b/lib/web/css/source/lib/_navigation.less @@ -305,6 +305,10 @@ .lib-css(border-top, none); } + li.level1 { + position: relative; + } + .level0 { .lib-css(margin, @_nav-level0-item-margin); display: inline-block; From b2fd3436e33b9666cd193969bd577a61f7c1e117 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sun, 3 Jun 2018 12:34:30 +0200 Subject: [PATCH 296/333] Fixed return type of getImageData --- app/code/Magento/Wishlist/CustomerData/Wishlist.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/CustomerData/Wishlist.php b/app/code/Magento/Wishlist/CustomerData/Wishlist.php index 9583a83a39428..85aff60ac6370 100644 --- a/app/code/Magento/Wishlist/CustomerData/Wishlist.php +++ b/app/code/Magento/Wishlist/CustomerData/Wishlist.php @@ -144,7 +144,7 @@ protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) * Retrieve product image data * * @param \Magento\Catalog\Model\Product $product - * @return \Magento\Catalog\Block\Product\Image + * @return array * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function getImageData($product) From eaa585d36791c5ddb7073f0c7332f9c06f238bba Mon Sep 17 00:00:00 2001 From: amittiwari024 <amit.tiwari@aureatelabs.com> Date: Sat, 19 May 2018 14:38:56 +0530 Subject: [PATCH 297/333] Refactor javascript code of button split widget --- .../adminhtml/templates/widget/button/split.phtml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/button/split.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/button/split.phtml index 27127e54e5be2..a115777624e91 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/button/split.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/button/split.phtml @@ -41,11 +41,11 @@ <?php endif; ?> </div> -<script> -require(['jquery'], function($){ - $('.actions-split') - .on('click.splitDefault', '.action-default', function() { - $(this).siblings('.dropdown-menu').find('.item-default').trigger('click'); - }); -}); +<script type="text/x-magento-init"> + { + ".actions-split": { + "Magento_Ui/js/grid/controls/button/split": {} + } + } </script> + From a9f5a1d180c6ffe601c5ad95ad6475e75448383e Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Thu, 17 May 2018 18:20:52 +0200 Subject: [PATCH 298/333] fix: support multiple minisearch widget instances --- app/code/Magento/Search/view/frontend/web/form-mini.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Search/view/frontend/web/form-mini.js b/app/code/Magento/Search/view/frontend/web/form-mini.js index de16305bbbe8d..3ab44c2595de9 100644 --- a/app/code/Magento/Search/view/frontend/web/form-mini.js +++ b/app/code/Magento/Search/view/frontend/web/form-mini.js @@ -55,7 +55,7 @@ define([ this.autoComplete = $(this.options.destinationSelector); this.searchForm = $(this.options.formSelector); this.submitBtn = this.searchForm.find(this.options.submitBtn)[0]; - this.searchLabel = $(this.options.searchLabel); + this.searchLabel = this.searchForm.find(this.options.searchLabel); this.isExpandable = this.options.isExpandable; _.bindAll(this, '_onKeyDown', '_onPropertyChange', '_onSubmit'); From 31cbafe3f571c9f876d20d4421d64e078a22beec Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 4 Jun 2018 10:17:41 +0300 Subject: [PATCH 299/333] MAGETWO-91807: Error when adding a product to the cart in the backend with the capitalization of the sku --- app/code/Magento/Catalog/Model/ProductRepository.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index a75e5349baa30..03ddab3d44547 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -809,9 +809,6 @@ private function getCollectionProcessor() private function getProductFromLocalCache(string $sku, string $cacheKey) { $preparedSku = $this->prepareSku($sku); - if (!isset($this->instances[$preparedSku])) { - return null; - } return $this->instances[$preparedSku][$cacheKey] ?? null; } From 6bc77cbcfb0d9fe49c682953f1c1b4048a1a81ac Mon Sep 17 00:00:00 2001 From: Anna Voelkl <voelkl@e-conomix.at> Date: Fri, 25 May 2018 11:21:14 +0200 Subject: [PATCH 300/333] art: Fix typo in method name --- .../Magento/Multishipping/Block/Checkout/Overview.php | 11 ++++++++++- .../view/frontend/templates/checkout/overview.phtml | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Multishipping/Block/Checkout/Overview.php b/app/code/Magento/Multishipping/Block/Checkout/Overview.php index 2197598489358..f44eae0a04ab8 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/Overview.php +++ b/app/code/Magento/Multishipping/Block/Checkout/Overview.php @@ -319,9 +319,18 @@ public function getQuote() } /** - * @return mixed + * @deprecated + * typo in method name, see getBillingAddressTotals() */ public function getBillinAddressTotals() + { + return $this->getBillingAddressTotals(); + } + + /** + * @return mixed + */ + public function getBillingAddressTotals() { $address = $this->getQuote()->getBillingAddress(); return $this->getShippingAddressTotals($address); diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml index d4d446a7567db..4590b7c584085 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml @@ -186,7 +186,7 @@ <?php endforeach; ?> </tbody> <tfoot> - <?= /* @noEscape */ $block->renderTotals($block->getBillinAddressTotals()); ?> + <?= /* @noEscape */ $block->renderTotals($block->getBillingAddressTotals()); ?> </tfoot> </table> </div> From c494d735a94feb7db8527bf9df994a82baa0e9c8 Mon Sep 17 00:00:00 2001 From: Anna Voelkl <voelkl@e-conomix.at> Date: Fri, 25 May 2018 15:47:25 +0200 Subject: [PATCH 301/333] Revert ":pencil2: Fix typos in Multishipping and User module" This reverts commit aa96d022adeff10ad42ab782ca7abb3233f1718d. --- .../Multishipping/Block/Checkout/AbstractMultishipping.php | 2 +- app/code/Magento/Multishipping/Block/Checkout/Shipping.php | 2 +- app/code/Magento/User/Model/Backend/Config/ObserverConfig.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php b/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php index d3c17a8d7c8de..0de66ccd505a4 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php +++ b/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php @@ -5,7 +5,7 @@ */ /** - * Multishipping checkout base abstract block + * Mustishipping checkout base abstract block * * @author Magento Core Team <core@magentocommerce.com> */ diff --git a/app/code/Magento/Multishipping/Block/Checkout/Shipping.php b/app/code/Magento/Multishipping/Block/Checkout/Shipping.php index ef1aa6301b23d..77981c736b9e9 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/Shipping.php +++ b/app/code/Magento/Multishipping/Block/Checkout/Shipping.php @@ -9,7 +9,7 @@ use Magento\Quote\Model\Quote\Address; /** - * Multishipping checkout shipping + * Mustishipping checkout shipping * * @api * @author Magento Core Team <core@magentocommerce.com> diff --git a/app/code/Magento/User/Model/Backend/Config/ObserverConfig.php b/app/code/Magento/User/Model/Backend/Config/ObserverConfig.php index 6d921dfdcdd65..c1b7e6a1110f7 100644 --- a/app/code/Magento/User/Model/Backend/Config/ObserverConfig.php +++ b/app/code/Magento/User/Model/Backend/Config/ObserverConfig.php @@ -72,7 +72,7 @@ public function getAdminPasswordLifetime() } /** - * Get admin maximum security failures from config + * Get admin maxiumum security failures from config * * @return int */ From 6e2dea6436ec61ff14e82371bb0456e2450632db Mon Sep 17 00:00:00 2001 From: Anna Voelkl <voelkl@e-conomix.at> Date: Fri, 25 May 2018 15:49:25 +0200 Subject: [PATCH 302/333] bug: Add phpdoc return type --- app/code/Magento/Multishipping/Block/Checkout/Overview.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Multishipping/Block/Checkout/Overview.php b/app/code/Magento/Multishipping/Block/Checkout/Overview.php index f44eae0a04ab8..c77fadfd2d4f1 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/Overview.php +++ b/app/code/Magento/Multishipping/Block/Checkout/Overview.php @@ -321,6 +321,7 @@ public function getQuote() /** * @deprecated * typo in method name, see getBillingAddressTotals() + * @return mixed */ public function getBillinAddressTotals() { From 9850d9c3444322e9005f9446ae63e7e901ea3e77 Mon Sep 17 00:00:00 2001 From: David Manners <dmanners87@gmail.com> Date: Fri, 25 May 2018 16:08:38 +0200 Subject: [PATCH 303/333] Remove unneeded white space --- app/code/Magento/Multishipping/Block/Checkout/Overview.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Multishipping/Block/Checkout/Overview.php b/app/code/Magento/Multishipping/Block/Checkout/Overview.php index c77fadfd2d4f1..5963e62e948f9 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/Overview.php +++ b/app/code/Magento/Multishipping/Block/Checkout/Overview.php @@ -321,7 +321,7 @@ public function getQuote() /** * @deprecated * typo in method name, see getBillingAddressTotals() - * @return mixed + * @return mixed */ public function getBillinAddressTotals() { From 365bfb4c81914041d063d99b3a87fe8d96fcc6c9 Mon Sep 17 00:00:00 2001 From: hitesh-wagento <hitesh@wagento.com> Date: Wed, 23 May 2018 15:41:52 +0530 Subject: [PATCH 304/333] --- .../backend/Magento_Ui/web/css/source/module/_data-grid.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9480161248d15..0c2d6ad8786ca 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 @@ -304,7 +304,7 @@ body._in-resize { .action-menu { left: auto; min-width: 10rem; - right: auto; + right: 0; text-align: left; top: auto; z-index: 1; From 6711694e180078c714f9216ac0b83cef74ac0a6f Mon Sep 17 00:00:00 2001 From: Nimesh Patel <patelnimesh1988@gmail.com> Date: Tue, 22 May 2018 14:48:04 +0530 Subject: [PATCH 305/333] Refactor JavsScript for UrlRewrite module edit page --- .../view/adminhtml/templates/edit.phtml | 17 ++++++++--------- .../web/js/url-rewrite-validation.js | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/UrlRewrite/view/adminhtml/web/js/url-rewrite-validation.js diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/templates/edit.phtml b/app/code/Magento/UrlRewrite/view/adminhtml/templates/edit.phtml index 09ac4a5990f6b..3ff25a7c84521 100644 --- a/app/code/Magento/UrlRewrite/view/adminhtml/templates/edit.phtml +++ b/app/code/Magento/UrlRewrite/view/adminhtml/templates/edit.phtml @@ -10,14 +10,13 @@ ?> <?= $block->getChildHtml() ?> <?php if ($block->getChildBlock('form')): ?> -<script> -require([ - 'jquery', - 'mage/backend/form', - 'mage/backend/validation' -], function($){ - $('#edit_form').form() - .validation({validationUrl: '<?= $block->escapeUrl($block->getValidationUrl()) ?>'}); -}); +<script type="text/x-magento-init"> + { + "#edit_form": { + "Magento_UrlRewrite/js/url-rewrite-validation" : { + "url": "<?= $block->escapeUrl($block->getValidationUrl()) ?>" + } + } + } </script> <?php endif; ?> diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/web/js/url-rewrite-validation.js b/app/code/Magento/UrlRewrite/view/adminhtml/web/js/url-rewrite-validation.js new file mode 100644 index 0000000000000..548f712bacadd --- /dev/null +++ b/app/code/Magento/UrlRewrite/view/adminhtml/web/js/url-rewrite-validation.js @@ -0,0 +1,19 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'mage/backend/form', + 'mage/backend/validation' +], function ($) { + 'use strict'; + + return function (data, element) { + + $(element).form().validation({ + validationUrl: data.url + }); + }; +}); From 868c07765923faccf6b15b8f60ddb2eea74181d3 Mon Sep 17 00:00:00 2001 From: Vova Yatsyuk <vova.yatsyuk@gmail.com> Date: Mon, 21 May 2018 17:18:22 +0300 Subject: [PATCH 306/333] Prevent multiple add-to-cart initializations in case of ajax loaded content --- .../Catalog/view/frontend/web/js/catalog-add-to-cart.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index b2da91c3b55c1..8fcac2f9f1d65 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -38,6 +38,11 @@ define([ _bindSubmit: function () { var self = this; + if (this.element.data('catalog-addtocart-initialized')) { + return; + } + + this.element.data('catalog-addtocart-initialized', 1); this.element.on('submit', function (e) { e.preventDefault(); self.submitForm($(this)); From 3e21eddf6123b5397a2056a188bc223f61783d59 Mon Sep 17 00:00:00 2001 From: Rahul Kachhadiya <rbkachhadiya@gmail.com> Date: Sat, 19 May 2018 16:17:09 +0530 Subject: [PATCH 307/333] Added language translation in template files --- .../Braintree/view/adminhtml/templates/form/cc.phtml | 2 +- app/code/Magento/Multishipping/i18n/en_US.csv | 2 ++ .../view/frontend/templates/checkout/results.phtml | 6 +++--- .../view/frontend/templates/checkout/success.phtml | 2 +- .../Paypal/view/adminhtml/templates/transparent/form.phtml | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml b/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml index 13249cd8e0e80..535a5a852fe70 100644 --- a/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml +++ b/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml @@ -84,7 +84,7 @@ $ccType = $block->getInfoData('cc_type'); name="payment[is_active_payment_token_enabler]" class="admin__control-checkbox"/> <label class="label" for="<?= /* @noEscape */ $code ?>_vault"> - <span><?= $block->escapeHtml('Save for later use.') ?></span> + <span><?= $block->escapeHtml(__('Save for later use.')) ?></span> </label> </div> <?php endif; ?> diff --git a/app/code/Magento/Multishipping/i18n/en_US.csv b/app/code/Magento/Multishipping/i18n/en_US.csv index 1e3e1880758ee..43cc785c56eab 100644 --- a/app/code/Magento/Multishipping/i18n/en_US.csv +++ b/app/code/Magento/Multishipping/i18n/en_US.csv @@ -88,3 +88,5 @@ Options,Options "Review Order","Review Order" "Select Shipping Method","Select Shipping Method" "We received your order!","We received your order!" +"Ship to:","Ship to:" +"Error:","Error:" \ No newline at end of file diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/results.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/results.phtml index d6fdef6ae5f9a..dacf96f9c0baf 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/results.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/results.phtml @@ -32,7 +32,7 @@ $orderIds = $block->getOrderIds(); <?php $shippingAddress = $block->getOrderShippingAddress($orderId); ?> <div class="shipping-item"> <?php if ($shippingAddress) : ?> - <span class="shipping-label"><?= $block->escapeHtml('Ship to:'); ?></span> + <span class="shipping-label"><?= $block->escapeHtml(__('Ship to:')); ?></span> <span class="shipping-address"> <?= $block->escapeHtml($block->formatOrderShippingAddress($shippingAddress)); ?> </span> @@ -65,7 +65,7 @@ $orderIds = $block->getOrderIds(); <dl class="shipping-item"> <dt class="shipping-block"> <?php if ($block->isShippingAddress($address)) : ?> - <span class="shipping-label"><?= $block->escapeHtml('Ship to:'); ?></span> + <span class="shipping-label"><?= $block->escapeHtml(__('Ship to:')); ?></span> <span class="shipping-address"> <?= $block->escapeHtml($block->formatQuoteShippingAddress($address)); ?> </span> @@ -76,7 +76,7 @@ $orderIds = $block->getOrderIds(); <?php endif; ?> </dt> <dd class="error-block"> - <span class="error-label"><?= $block->escapeHtml('Error:'); ?></span> + <span class="error-label"><?= $block->escapeHtml(__('Error:')); ?></span> <span class="error-description"> <?= /* @noEscape */ $block->getAddressError($address); ?> </span> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/success.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/success.phtml index c8e7c375089cd..57c4afaee6541 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/success.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/success.phtml @@ -22,7 +22,7 @@ <?php $shippingAddress = $block->getCheckoutData()->getOrderShippingAddress($orderId); ?> <div class="shipping-item"> <?php if ($shippingAddress) : ?> - <span class="shipping-label"><?= $block->escapeHtml('Ship to:'); ?></span> + <span class="shipping-label"><?= $block->escapeHtml(__('Ship to:')); ?></span> <span class="shipping-address"> <?= $block->escapeHtml( $block->getCheckoutData()->formatOrderShippingAddress($shippingAddress) diff --git a/app/code/Magento/Paypal/view/adminhtml/templates/transparent/form.phtml b/app/code/Magento/Paypal/view/adminhtml/templates/transparent/form.phtml index cdd4779a2fd87..532fa88c4986a 100644 --- a/app/code/Magento/Paypal/view/adminhtml/templates/transparent/form.phtml +++ b/app/code/Magento/Paypal/view/adminhtml/templates/transparent/form.phtml @@ -135,7 +135,7 @@ $ccExpMonth = $block->getInfoData('cc_exp_month'); name="payment[is_active_payment_token_enabler]" class="admin__control-checkbox"/> <label class="admin__field-label" for="<?= /* @noEscape */ $code ?>_vault"> - <span><?= $block->escapeHtml('Save for later use.') ?></span> + <span><?= $block->escapeHtml(__('Save for later use.')) ?></span> </label> </div> <?php endif; ?> From 81289132743af3db8426d5e7d307c308811fa8d1 Mon Sep 17 00:00:00 2001 From: Nimesh Patel <patelnimesh1988@gmail.com> Date: Fri, 18 May 2018 13:25:11 +0530 Subject: [PATCH 308/333] Refactor javscript for customer logout --- .../view/frontend/templates/logout.phtml | 17 ++++++++--------- .../view/frontend/web/js/logout-redirect.js | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/Customer/view/frontend/web/js/logout-redirect.js diff --git a/app/code/Magento/Customer/view/frontend/templates/logout.phtml b/app/code/Magento/Customer/view/frontend/templates/logout.phtml index 43665045ce3e2..5a99b7d931b9b 100644 --- a/app/code/Magento/Customer/view/frontend/templates/logout.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/logout.phtml @@ -7,13 +7,12 @@ /** @var \Magento\Framework\View\Element\Template $block */ ?> <p><?= $block->escapeHtml(__('You have signed out and will go to our homepage in 5 seconds.')) ?></p> -<script> -require([ - "jquery", - "mage/mage" -], function($){ - - $($.mage.redirect("<?= $block->escapeJs($block->escapeUrl($block->getUrl())) ?>", "assign", 5000)); - -}); +<script type="text/x-magento-init"> + { + "*": { + "Magento_Customer/js/logout-redirect": { + "url": "<?= $block->escapeJs($block->escapeUrl($block->getUrl())) ?>" + } + } + } </script> diff --git a/app/code/Magento/Customer/view/frontend/web/js/logout-redirect.js b/app/code/Magento/Customer/view/frontend/web/js/logout-redirect.js new file mode 100644 index 0000000000000..c8d06e51fc39e --- /dev/null +++ b/app/code/Magento/Customer/view/frontend/web/js/logout-redirect.js @@ -0,0 +1,15 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + "jquery", + "mage/mage" +], function ($) { + 'use strict'; + + return function (data) { + $($.mage.redirect(data.url, "assign", 5000)); + }; +}); From 29223323076fae76c1c3f0b8d3de657088e60a5e Mon Sep 17 00:00:00 2001 From: Nimesh Patel <patelnimesh1988@gmail.com> Date: Fri, 18 May 2018 16:41:59 +0530 Subject: [PATCH 309/333] Fixed Invalid quote mark issue --- .../Customer/view/frontend/web/js/logout-redirect.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/logout-redirect.js b/app/code/Magento/Customer/view/frontend/web/js/logout-redirect.js index c8d06e51fc39e..6792626df6a08 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/logout-redirect.js +++ b/app/code/Magento/Customer/view/frontend/web/js/logout-redirect.js @@ -4,12 +4,12 @@ */ define([ - "jquery", - "mage/mage" + 'jquery', + 'mage/mage' ], function ($) { 'use strict'; return function (data) { - $($.mage.redirect(data.url, "assign", 5000)); + $($.mage.redirect(data.url, 'assign', 5000)); }; }); From 486f7a72c37ea04a5eeb1978bf2a8af0be9f0943 Mon Sep 17 00:00:00 2001 From: Alex Gusev <flancer64@gmail.com> Date: Tue, 15 May 2018 09:58:24 +0300 Subject: [PATCH 310/333] Error 500 in Module Manager There is an error in Module Manager (http://store.com/setup/index.php/moduleGrid/modules) if some module has no "moduleName" property: ``` { "name": "amzn\/amazon-pay-and-login-magento-2-module", "type": "magento2-module", "version": "2.0.4" } ``` --- setup/src/Magento/Setup/Model/Grid/Module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Model/Grid/Module.php b/setup/src/Magento/Setup/Model/Grid/Module.php index c5e7c514df57c..c1b5dad5d9508 100644 --- a/setup/src/Magento/Setup/Model/Grid/Module.php +++ b/setup/src/Magento/Setup/Model/Grid/Module.php @@ -178,7 +178,7 @@ private function getModulesInfo(array $moduleList) private function addGeneralInfo(array $items) { foreach ($items as &$item) { - $item['moduleName'] = $item['moduleName'] ?: $this->packageInfo->getModuleName($item['name']); + $item['moduleName'] = isset($item['moduleName']) ? $item['moduleName'] : $this->packageInfo->getModuleName($item['name']); $item['enable'] = $this->moduleList->has($item['moduleName']); $vendorSource = $item['name'] == self::UNKNOWN_PACKAGE_NAME ? $item['moduleName'] : $item['name']; $item['vendor'] = ucfirst(current(preg_split('%[/_]%', $vendorSource))); From 1b5d1d1da3c9a4c01351f5c5ac34da9691c355a5 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 21 May 2018 17:25:09 +0300 Subject: [PATCH 311/333] Fixed according to review --- setup/src/Magento/Setup/Model/Grid/Module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Model/Grid/Module.php b/setup/src/Magento/Setup/Model/Grid/Module.php index c1b5dad5d9508..fd9dc3285de3e 100644 --- a/setup/src/Magento/Setup/Model/Grid/Module.php +++ b/setup/src/Magento/Setup/Model/Grid/Module.php @@ -178,7 +178,7 @@ private function getModulesInfo(array $moduleList) private function addGeneralInfo(array $items) { foreach ($items as &$item) { - $item['moduleName'] = isset($item['moduleName']) ? $item['moduleName'] : $this->packageInfo->getModuleName($item['name']); + $item['moduleName'] = $item['moduleName'] ?? $this->packageInfo->getModuleName($item['name']); $item['enable'] = $this->moduleList->has($item['moduleName']); $vendorSource = $item['name'] == self::UNKNOWN_PACKAGE_NAME ? $item['moduleName'] : $item['name']; $item['vendor'] = ucfirst(current(preg_split('%[/_]%', $vendorSource))); From 037b97cb7a3062326121bc35947d5eeaae53d566 Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Mon, 14 May 2018 11:25:13 +0200 Subject: [PATCH 312/333] Move buttons definition to separate file --- .../Magento/Ui/view/base/web/js/form/adapter.js | 10 +++------- .../Ui/view/base/web/js/form/adapter/buttons.js | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js diff --git a/app/code/Magento/Ui/view/base/web/js/form/adapter.js b/app/code/Magento/Ui/view/base/web/js/form/adapter.js index 0be9af06f9579..b623ce11603dd 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/adapter.js +++ b/app/code/Magento/Ui/view/base/web/js/form/adapter.js @@ -9,15 +9,11 @@ define([ 'jquery', 'underscore' -], function ($, _) { + './adapter/buttons' +], function ($, _, buttons) { 'use strict'; - var buttons = { - 'reset': '#reset', - 'save': '#save', - 'saveAndContinue': '#save_and_continue' - }, - selectorPrefix = '', + var selectorPrefix = '', eventPrefix; /** diff --git a/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js b/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js new file mode 100644 index 0000000000000..f1138608f3233 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js @@ -0,0 +1,15 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define(function () { + return { + 'reset': '#reset', + 'save': '#save', + 'saveAndContinue': '#save_and_continue' + } +}); From fdb70a08ae43da8ad5601a9731886d7513690207 Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Mon, 14 May 2018 16:05:13 +0200 Subject: [PATCH 313/333] Change relative path to absolute path for testability --- app/code/Magento/Ui/view/base/web/js/form/adapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/adapter.js b/app/code/Magento/Ui/view/base/web/js/form/adapter.js index b623ce11603dd..f864026259766 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/adapter.js +++ b/app/code/Magento/Ui/view/base/web/js/form/adapter.js @@ -9,7 +9,7 @@ define([ 'jquery', 'underscore' - './adapter/buttons' + 'Magento_Ui/js/form/adapter/buttons' ], function ($, _, buttons) { 'use strict'; From c70ba5b2967b705ed80dbc20b3f8a0670d20060e Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Mon, 14 May 2018 16:05:47 +0200 Subject: [PATCH 314/333] Add strict mode --- app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js b/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js index f1138608f3233..fc43e6cdbb071 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js +++ b/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js @@ -7,6 +7,8 @@ * @api */ define(function () { + 'use strict'; + return { 'reset': '#reset', 'save': '#save', From a71b17808dc928e5a3c49c97052b29fa4efbdb2c Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Mon, 14 May 2018 18:13:00 +0200 Subject: [PATCH 315/333] Fix missing comma --- app/code/Magento/Ui/view/base/web/js/form/adapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/adapter.js b/app/code/Magento/Ui/view/base/web/js/form/adapter.js index f864026259766..660d3382cb97c 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/adapter.js +++ b/app/code/Magento/Ui/view/base/web/js/form/adapter.js @@ -8,7 +8,7 @@ */ define([ 'jquery', - 'underscore' + 'underscore', 'Magento_Ui/js/form/adapter/buttons' ], function ($, _, buttons) { 'use strict'; From 526911c13a257357173ffc5bcfcc05dd2625e28b Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Tue, 15 May 2018 07:30:29 +0200 Subject: [PATCH 316/333] Make sure colons are in right place --- app/code/Magento/Ui/view/base/web/js/form/adapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/adapter.js b/app/code/Magento/Ui/view/base/web/js/form/adapter.js index 660d3382cb97c..200aaa487107d 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/adapter.js +++ b/app/code/Magento/Ui/view/base/web/js/form/adapter.js @@ -13,8 +13,8 @@ define([ ], function ($, _, buttons) { 'use strict'; - var selectorPrefix = '', - eventPrefix; + var selectorPrefix = ''; + var eventPrefix; /** * Initialize listener. From 4b27bd81dca12b98f24155bb73cc10cbff2dafcb Mon Sep 17 00:00:00 2001 From: Jisse Reitsma <info@yireo.com> Date: Tue, 15 May 2018 16:06:52 +0200 Subject: [PATCH 317/333] Fix missing column because of eslint --- app/code/Magento/Ui/view/base/web/js/form/adapter.js | 4 ++-- app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/adapter.js b/app/code/Magento/Ui/view/base/web/js/form/adapter.js index 200aaa487107d..660d3382cb97c 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/adapter.js +++ b/app/code/Magento/Ui/view/base/web/js/form/adapter.js @@ -13,8 +13,8 @@ define([ ], function ($, _, buttons) { 'use strict'; - var selectorPrefix = ''; - var eventPrefix; + var selectorPrefix = '', + eventPrefix; /** * Initialize listener. diff --git a/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js b/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js index fc43e6cdbb071..2b939e25da479 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js +++ b/app/code/Magento/Ui/view/base/web/js/form/adapter/buttons.js @@ -13,5 +13,5 @@ define(function () { 'reset': '#reset', 'save': '#save', 'saveAndContinue': '#save_and_continue' - } + }; }); From bcdd8f2a6fdc722828b3c0d3ac72fd19df1ae226 Mon Sep 17 00:00:00 2001 From: Saurabh Parekh <saurabh.parekh@aureatelabs.com> Date: Sat, 19 May 2018 13:35:23 +0530 Subject: [PATCH 318/333] Function unnecessarily called multiple time --- .../Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml b/app/code/Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml index 7dca29263bae3..0ba3c7ed2d7d6 100644 --- a/app/code/Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml +++ b/app/code/Magento/CurrencySymbol/view/adminhtml/templates/grid.phtml @@ -13,8 +13,6 @@ */ ?> -<?php $block->getCurrencySymbolsData();?> - <form id="currency-symbols-form" action="<?= /* @escapeNotVerified */ $block->getFormActionUrl() ?>" method="post"> <input name="form_key" type="hidden" value="<?= /* @escapeNotVerified */ $block->getFormKey() ?>" /> <fieldset class="admin__fieldset"> From ecc5f7b4ae8c43f588710bb6bc7c6183ccaf2538 Mon Sep 17 00:00:00 2001 From: hitesh-wagento <hitesh@wagento.com> Date: Mon, 4 Jun 2018 16:25:36 +0530 Subject: [PATCH 319/333] [Forwardport] [Resolved : Menu widget submenu alignment] --- lib/web/css/source/lib/_navigation.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/web/css/source/lib/_navigation.less b/lib/web/css/source/lib/_navigation.less index 6ed3a9671ab13..56aa2e7ef86b9 100644 --- a/lib/web/css/source/lib/_navigation.less +++ b/lib/web/css/source/lib/_navigation.less @@ -305,6 +305,10 @@ .lib-css(border-top, none); } + li.level1 { + position: relative; + } + .level0 { .lib-css(margin, @_nav-level0-item-margin); display: inline-block; From c2765a77db48e5d2819500c68b51947ebd7e8bf0 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Mon, 4 Jun 2018 16:33:02 +0300 Subject: [PATCH 320/333] Update AbstractMultishipping.php Fix typo in doc block. --- .../Multishipping/Block/Checkout/AbstractMultishipping.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php b/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php index 0de66ccd505a4..d3c17a8d7c8de 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php +++ b/app/code/Magento/Multishipping/Block/Checkout/AbstractMultishipping.php @@ -5,7 +5,7 @@ */ /** - * Mustishipping checkout base abstract block + * Multishipping checkout base abstract block * * @author Magento Core Team <core@magentocommerce.com> */ From 402d5655e14e5ee73a36f54cb282acd267b7f43e Mon Sep 17 00:00:00 2001 From: Roman Ganin <rganin@magento.com> Date: Mon, 21 May 2018 13:11:50 -0500 Subject: [PATCH 321/333] MAGETWO-91958: EE install fails on 7.2.5 with fatal error --- .../Setup/Declaration/Schema/Db/DDLTriggerInterface.php | 2 +- .../Schema/Db/MySQL/DDL/Triggers/MigrateDataBetweenShards.php | 2 +- .../Schema/Db/MySQL/DDL/Triggers/MigrateDataFrom.php | 2 +- .../Db/MySQL/DDL/Triggers/MigrateDataFromAnotherTable.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/DDLTriggerInterface.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/DDLTriggerInterface.php index 8d5e03000316f..596e66367c727 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/DDLTriggerInterface.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/DDLTriggerInterface.php @@ -42,5 +42,5 @@ public function isApplicable(string $statement) : bool ; * @param ElementHistory $elementHistory * @return callable */ - public function getCallback(ElementHistory $elementHistory) : Callable; + public function getCallback(ElementHistory $elementHistory) : callable; } diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataBetweenShards.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataBetweenShards.php index ad69ff92c8a5a..4c1b2b8373c52 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataBetweenShards.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataBetweenShards.php @@ -62,7 +62,7 @@ public function isApplicable(string $statement) : bool /** * @inheritdoc */ - public function getCallback(ElementHistory $elementHistory) : Callable + public function getCallback(ElementHistory $elementHistory) : callable { /** @var Table $newTable */ $newTable = $elementHistory->getNew(); diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataFrom.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataFrom.php index 5ad5c2cd6a209..cb969fb9367f2 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataFrom.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataFrom.php @@ -50,7 +50,7 @@ public function isApplicable(string $statement) : bool /** * @inheritdoc */ - public function getCallback(ElementHistory $columnHistory) : Callable + public function getCallback(ElementHistory $columnHistory) : callable { /** @var Column $column */ $column = $columnHistory->getNew(); diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataFromAnotherTable.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataFromAnotherTable.php index 6c426723ab370..0f97fc9d6f243 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataFromAnotherTable.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DDL/Triggers/MigrateDataFromAnotherTable.php @@ -49,7 +49,7 @@ public function isApplicable(string $statement) : bool /** * @inheritdoc */ - public function getCallback(ElementHistory $tableHistory) : Callable + public function getCallback(ElementHistory $tableHistory) : callable { /** @var Table $table */ $table = $tableHistory->getNew(); From eaf8caab717b7fd77974446a22c6f26058a1551b Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@magento.com> Date: Mon, 4 Jun 2018 16:29:21 -0500 Subject: [PATCH 322/333] MAGETWO-91434: Default option for Status attribute not being set - added functional test to cover bug fix --- .../Section/AdminProductFormSection.xml | 1 + ...ctStatusAttributeDisabledByDefaultTest.xml | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml index 926d87e889931..e329ae5e639ab 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductFormSection.xml @@ -10,6 +10,7 @@ <section name="AdminProductFormSection"> <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="enableProduct" type="checkbox" selector="input[name='product[status]']"/> <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']"/> <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminProductStatusAttributeDisabledByDefaultTest.xml new file mode 100644 index 0000000000000..f4c7ab28b22dd --- /dev/null +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminProductStatusAttributeDisabledByDefaultTest.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="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminProductStatusAttributeDisabledByDefaultTest"> + <annotations> + <title value="Verify the default option value for product Status attribute is set correctly during product creation"/> + <description value="The default option value for product Status attribute is set correctly during product creation"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-92424"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + </before> + <after> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttribute"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid1"/> + <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="Enable Product" stepKey="setAttributeLabel1"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid1"/> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow1"/> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{AdminNewAttributePanel.isDefault('1')}}" stepKey="resetOptionForStatusAttribute"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute1"/> + <waitForPageLoad stepKey="waitForSaveAttribute1"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache1"/> + + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttribute"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="Enable Product" stepKey="setAttributeLabel"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{AdminNewAttributePanel.isDefault('2')}}" stepKey="chooseDisabledOptionForStatus"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> + <waitForPageLoad stepKey="waitForAttributeToSave"/> + <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad time="30" stepKey="waitForProductGridPageToLoad"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickOnAddSimpleProduct"/> + <waitForPageLoad stepKey="waitForProductEditToLoad"/> + <dontSeeCheckboxIsChecked selector="{{AdminProductFormSection.enableProduct}}" stepKey="dontSeeCheckboxEnableProductIsChecked"/> + + </test> +</tests> \ No newline at end of file From 9fa64255f77609f8b845f3d0b9f503e19a3a6828 Mon Sep 17 00:00:00 2001 From: hitesh-wagento <hitesh@wagento.com> Date: Mon, 4 Jun 2018 11:40:13 +0530 Subject: [PATCH 323/333] --- .../frontend/Magento/luma/web/css/source/_forms.less | 4 ---- .../frontend/Magento/luma/web/css/source/_theme.less | 7 +++++++ lib/web/css/source/lib/variables/_forms.less | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/design/frontend/Magento/luma/web/css/source/_forms.less b/app/design/frontend/Magento/luma/web/css/source/_forms.less index 1c9ffb1606b26..7c5027aef113b 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_forms.less +++ b/app/design/frontend/Magento/luma/web/css/source/_forms.less @@ -91,11 +91,7 @@ .select-styling() { .lib-css(appearance, none, 1); appearance: none; - background: @select__background url('../images/select-bg.svg') no-repeat 100% 45%; background-size: 30px 60px; - border: 1px solid @border-color__base; - height: 32px; - padding-right: 25px; text-indent: .01em; text-overflow: ''; diff --git a/app/design/frontend/Magento/luma/web/css/source/_theme.less b/app/design/frontend/Magento/luma/web/css/source/_theme.less index b94ffc08b77ec..957e622d6a0bb 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_theme.less +++ b/app/design/frontend/Magento/luma/web/css/source/_theme.less @@ -166,6 +166,13 @@ // Forms // --------------------------------------------- + +// Select +@select__background: @form-element-input__background url('../images/select-bg.svg') no-repeat 100% 45%; +@select__border: 1px solid @border-color__base; +@select__height: 32px; +@select__padding: 4px 25px @indent__xs @indent__s; + // Form fieldset @form-fieldset-legend__font-size: 18px; @form-fieldset-legend__font-weight: @font-weight__light; diff --git a/lib/web/css/source/lib/variables/_forms.less b/lib/web/css/source/lib/variables/_forms.less index 69d0146fb3555..fff6141c67fd5 100644 --- a/lib/web/css/source/lib/variables/_forms.less +++ b/lib/web/css/source/lib/variables/_forms.less @@ -121,7 +121,7 @@ @select__disabled__font-style: @form-element-input__disabled__font-style; // Focus state -@select__focus__background: @form-element-input__focus__background; +@select__focus__background: false; @select__focus__border: @form-element-input__focus__border; @select__focus__color: @form-element-input__focus__color; @select__focus__font-style: @form-element-input__focus__font-style; From 27e9d2f406c7fb8d3c5391db86ca97917e30b4bb Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Thu, 17 May 2018 18:20:52 +0200 Subject: [PATCH 324/333] fix: support multiple minisearch widget instances --- app/code/Magento/Search/view/frontend/web/form-mini.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Search/view/frontend/web/form-mini.js b/app/code/Magento/Search/view/frontend/web/form-mini.js index de16305bbbe8d..3ab44c2595de9 100644 --- a/app/code/Magento/Search/view/frontend/web/form-mini.js +++ b/app/code/Magento/Search/view/frontend/web/form-mini.js @@ -55,7 +55,7 @@ define([ this.autoComplete = $(this.options.destinationSelector); this.searchForm = $(this.options.formSelector); this.submitBtn = this.searchForm.find(this.options.submitBtn)[0]; - this.searchLabel = $(this.options.searchLabel); + this.searchLabel = this.searchForm.find(this.options.searchLabel); this.isExpandable = this.options.isExpandable; _.bindAll(this, '_onKeyDown', '_onPropertyChange', '_onSubmit'); From 279655ad94a9de13d8d953d1f1d205cb5de9a0ac Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 5 Jun 2018 11:15:13 +0300 Subject: [PATCH 325/333] MAGETWO-90787: Swagger does not render correctly for many POST/PUT operations --- .../frontend/layout/swagger_index_index.xml | 35 +- .../frontend/templates/swagger-ui/index.phtml | 39 +- .../frontend/web/swagger-ui/css/print.css | 1172 - .../frontend/web/swagger-ui/css/reset.css | 125 - .../frontend/web/swagger-ui/css/screen.css | 1279 - .../frontend/web/swagger-ui/css/style.css | 270 +- .../web/swagger-ui/css/swagger-ui.css | 2 + .../web/swagger-ui/css/typography.css | 26 - .../fonts/droid-sans-v6-latin-700.eot | Bin 22922 -> 0 bytes .../fonts/droid-sans-v6-latin-700.svg | 411 - .../fonts/droid-sans-v6-latin-700.ttf | Bin 40513 -> 0 bytes .../fonts/droid-sans-v6-latin-700.woff | Bin 25992 -> 0 bytes .../fonts/droid-sans-v6-latin-700.woff2 | Bin 11480 -> 0 bytes .../fonts/droid-sans-v6-latin-regular.eot | Bin 22008 -> 0 bytes .../fonts/droid-sans-v6-latin-regular.svg | 403 - .../fonts/droid-sans-v6-latin-regular.ttf | Bin 39069 -> 0 bytes .../fonts/droid-sans-v6-latin-regular.woff | Bin 24868 -> 0 bytes .../fonts/droid-sans-v6-latin-regular.woff2 | Bin 11304 -> 0 bytes .../web/swagger-ui/images/explorer_icons.png | Bin 2474 -> 0 bytes .../web/swagger-ui/images/favicon-16x16.png | Bin 449 -> 0 bytes .../web/swagger-ui/images/favicon-32x32.png | Bin 1141 -> 0 bytes .../web/swagger-ui/images/favicon.ico | Bin 5430 -> 0 bytes .../web/swagger-ui/images/pet_store_api.png | Bin 637 -> 0 bytes .../web/swagger-ui/images/throbber.gif | Bin 6122 -> 0 bytes .../web/swagger-ui/images/wordnik_api.png | Bin 672 -> 0 bytes .../web/swagger-ui/js/lang/translator.js | 13 +- .../web/swagger-ui/js/lib/backbone-min.js | 15 - .../js/lib/handlebars.min-v4.0.10.js | 29 - .../swagger-ui/js/lib/highlight.9.1.0.pack.js | 1 - .../js/lib/highlight.9.1.0.pack_extended.js | 1 - .../web/swagger-ui/js/lib/jquery-1.8.0.min.js | 2 - .../swagger-ui/js/lib/jquery.ba-bbq.min.js | 18 - .../swagger-ui/js/lib/jquery.slideto.min.js | 1 - .../swagger-ui/js/lib/jquery.wiggle.min.js | 8 - .../web/swagger-ui/js/lib/jsoneditor.min.js | 5 - .../web/swagger-ui/js/lib/lodash.min.js | 2 - .../frontend/web/swagger-ui/js/lib/marked.js | 1274 - .../js/lib/object-assign-pollyfill.js | 1 - .../web/swagger-ui/js/lib/swagger-oauth.js | 290 - .../web/swagger-ui/js/lib/underscore-min.js | 6 - .../web/swagger-ui/js/lib/underscore-min.map | 1 - .../web/swagger-ui/js/magento-swagger.js | 95 +- .../web/swagger-ui/js/swagger-ui-bundle.js | 99 + .../js/swagger-ui-standalone-preset.js | 13 + .../frontend/web/swagger-ui/js/swagger-ui.js | 25344 ---------------- .../Constraint/AssertApiInfoTitleOnPage.php | 2 +- .../AssertEndpointContentDisplay.php | 4 +- .../AssertServiceContentDisplay.php | 4 +- .../Swagger/Test/Page/SwaggerUiPage.php | 38 +- .../Test/Js/_files/blacklist/magento.txt | 16 +- 50 files changed, 274 insertions(+), 30770 deletions(-) delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/css/print.css delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/css/reset.css delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/css/screen.css create mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/css/swagger-ui.css delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/css/typography.css delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-700.eot delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-700.svg delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-700.ttf delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-700.woff delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-700.woff2 delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-regular.eot delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-regular.svg delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-regular.woff delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2 delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/images/explorer_icons.png delete mode 100755 app/code/Magento/Swagger/view/frontend/web/swagger-ui/images/favicon-16x16.png delete mode 100755 app/code/Magento/Swagger/view/frontend/web/swagger-ui/images/favicon-32x32.png delete mode 100755 app/code/Magento/Swagger/view/frontend/web/swagger-ui/images/favicon.ico delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/images/pet_store_api.png delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/images/throbber.gif delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/images/wordnik_api.png delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/backbone-min.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/handlebars.min-v4.0.10.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/highlight.9.1.0.pack.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/highlight.9.1.0.pack_extended.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/jquery-1.8.0.min.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/jquery.ba-bbq.min.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/jquery.slideto.min.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/jquery.wiggle.min.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/jsoneditor.min.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/lodash.min.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/marked.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/object-assign-pollyfill.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/swagger-oauth.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/underscore-min.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/lib/underscore-min.map create mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-bundle.js create mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui-standalone-preset.js delete mode 100644 app/code/Magento/Swagger/view/frontend/web/swagger-ui/js/swagger-ui.js 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 345f063a7aaa3..f14df1c70a790 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 @@ -10,32 +10,18 @@ <title>Swagger UI - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + @@ -43,6 +29,7 @@ + diff --git a/app/code/Magento/Swagger/view/frontend/templates/swagger-ui/index.phtml b/app/code/Magento/Swagger/view/frontend/templates/swagger-ui/index.phtml index 27b3767f274bc..b20da68734579 100644 --- a/app/code/Magento/Swagger/view/frontend/templates/swagger-ui/index.phtml +++ b/app/code/Magento/Swagger/view/frontend/templates/swagger-ui/index.phtml @@ -12,11 +12,48 @@ * Modified by Magento, Modifications Copyright © Magento, Inc. All rights reserved. */ -/** @var \Magento\Swagger\Block\Index $block */ +/** @var \Magento\Swagger\Block\Index $block + * + * @codingStandardsIgnoreFile + */ $schemaUrl = $block->getSchemaUrl(); ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +