From 7d4465670326ad09d15985091d40834f6ec040f9 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Fri, 5 Jan 2018 18:08:22 +0200 Subject: [PATCH 001/240] magento/magento2#12970: Can't add grouped product, with out of stock sub product, to cart. --- .../Model/Product/Type/Grouped.php | 2 +- .../_files/product_virtual_out_of_stock.php | 20 ++ .../product_virtual_out_of_stock_rollback.php | 26 +++ .../Model/Product/Type/GroupedTest.php | 214 ++++++++++++++++-- .../product_grouped_with_out_of_stock.php | 75 ++++++ ...uct_grouped_with_out_of_stock_rollback.php | 10 + 6 files changed, 331 insertions(+), 16 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php create mode 100644 dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index e1c97c8912cf8..c97a62d9df456 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -341,7 +341,7 @@ protected function getProductInfo(\Magento\Framework\DataObject $buyRequest, $pr if ($isStrictProcessMode && !$subProduct->getQty()) { return __('Please specify the quantity of product(s).')->render(); } - $productsInfo[$subProduct->getId()] = intval($subProduct->getQty()); + $productsInfo[$subProduct->getId()] = $subProduct->isSalable() ? intval($subProduct->getQty()) : 0; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock.php new file mode 100644 index 0000000000000..cff2e83ed002e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock.php @@ -0,0 +1,20 @@ +create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL) + ->setId(31) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Virtual Product Out') + ->setSku('virtual-product-out') + ->setPrice(10) + ->setTaxClassId(0) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData(['is_in_stock' => 0]) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock_rollback.php new file mode 100644 index 0000000000000..8055ca4a25569 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock_rollback.php @@ -0,0 +1,26 @@ +get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('virtual-product-out', false, null, true); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index dcf4565873ea5..554a20d8a18a7 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -5,8 +5,19 @@ */ namespace Magento\GroupedProduct\Model\Product\Type; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\CatalogInventory\Model\Configuration; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\Value; + class GroupedTest extends \PHPUnit\Framework\TestCase { + /** + * @var ReinitableConfigInterface + */ + private $reinitableConfig; + /** * @var \Magento\Framework\ObjectManagerInterface */ @@ -20,16 +31,21 @@ class GroupedTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->_productType = $this->objectManager->get(\Magento\Catalog\Model\Product\Type::class); + $this->reinitableConfig = $this->objectManager->get(ReinitableConfigInterface::class); + } + + protected function tearDown() + { + $this->dropConfigValue(Configuration::XML_PATH_SHOW_OUT_OF_STOCK); } public function testFactory() { $product = new \Magento\Framework\DataObject(); - $product->setTypeId(\Magento\GroupedProduct\Model\Product\Type\Grouped::TYPE_CODE); + $product->setTypeId(Grouped::TYPE_CODE); $type = $this->_productType->factory($product); - $this->assertInstanceOf(\Magento\GroupedProduct\Model\Product\Type\Grouped::class, $type); + $this->assertInstanceOf(Grouped::class, $type); } /** @@ -38,12 +54,12 @@ public function testFactory() */ public function testGetAssociatedProducts() { - $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - /** @var \Magento\Catalog\Model\Product $product */ + /** @var Product $product */ $product = $productRepository->get('grouped-product'); $type = $product->getTypeInstance(); - $this->assertInstanceOf(\Magento\GroupedProduct\Model\Product\Type\Grouped::class, $type); + $this->assertInstanceOf(Grouped::class, $type); $associatedProducts = $type->getAssociatedProducts($product); $this->assertCount(2, $associatedProducts); @@ -53,7 +69,7 @@ public function testGetAssociatedProducts() } /** - * @param \Magento\Catalog\Model\Product $product + * @param Product $product */ private function assertProductInfo($product) { @@ -92,25 +108,25 @@ public function testPrepareProduct() \Magento\Framework\DataObject::class, ['data' => ['value' => ['qty' => 2]]] ); - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get('grouped-product'); - /** @var \Magento\GroupedProduct\Model\Product\Type\Grouped $type */ - $type = $this->objectManager->get(\Magento\GroupedProduct\Model\Product\Type\Grouped::class); + /** @var Grouped $type */ + $type = $this->objectManager->get(Grouped::class); $processModes = [ - \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_FULL, - \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_LITE + Grouped::PROCESS_MODE_FULL, + Grouped::PROCESS_MODE_LITE ]; $expectedData = [ - \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_FULL => [ + Grouped::PROCESS_MODE_FULL => [ 1 => '{"super_product_config":{"product_type":"grouped","product_id":"' . $product->getId() . '"}}', 21 => '{"super_product_config":{"product_type":"grouped","product_id":"' . $product->getId() . '"}}', ], - \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_LITE => [ + Grouped::PROCESS_MODE_LITE => [ $product->getId() => '{"value":{"qty":2}}', ] ]; @@ -127,4 +143,172 @@ public function testPrepareProduct() } } } + + /** + * Test adding grouped product to cart then one of subproducts is out of stock. + * + * @magentoDataFixture Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + * @dataProvider outOfStockSubProductDataProvider + * @param bool $outOfStockShown + * @param array $data + * @param array $expected + */ + public function testOutOfStockSubProduct(bool $outOfStockShown, array $data, array $expected) + { + $this->changeConfigValue(Configuration::XML_PATH_SHOW_OUT_OF_STOCK, $outOfStockShown); + $buyRequest = new \Magento\Framework\DataObject($data); + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var Product $product */ + $product = $productRepository->get('grouped-product'); + /** @var Grouped $groupedProduct */ + $groupedProduct = $this->objectManager->get(Grouped::class); + $actual = $groupedProduct->prepareForCartAdvanced($buyRequest, $product, Grouped::PROCESS_MODE_FULL); + self::assertEquals( + count($expected), + count($actual) + ); + /** @var array $element */ + foreach ($expected as $number => $element) { + foreach ($element as $key => $value) { + self::assertEquals( + $value, + $actual[$number]->getData($key), + "Failed asserting that value $key matches expected" + ); + } + } + + } + + /** + * Data provider for testOutOfStockSubProduct. + * + * @return array + */ + public function outOfStockSubProductDataProvider() + { + return [ + 'Out of stock product are shown #1' => [ + true, + [ + 'product' => 3, + 'qty' => 1, + 'super_group' => [ + 1 => 4, + 21 => 5 + ], + ], + [ + [ + 'sku' => 'virtual-product', + 'cart_qty' => 5 + ], + [ + 'sku' => 'simple', + 'cart_qty' => 4 + ], + ] + ], + 'Out of stock product are shown #2' => [ + true, + [ + 'product' => 3, + 'qty' => 1, + 'super_group' => [ + 1 => 0, + ], + ], + [ + [ + 'sku' => 'virtual-product', + 'cart_qty' => 2 // This is a default quantity. + ], + ] + ], + 'Out of stock product are hidden #1' => [ + false, + [ + 'product' => 3, + 'qty' => 1, + 'super_group' => [ + 1 => 4, + 21 => 5 + ], + ], + [ + [ + 'sku' => 'virtual-product', + 'cart_qty' => 5 + ], + [ + 'sku' => 'simple', + 'cart_qty' => 4 + ], + ] + ], + 'Out of stock product are hidden #2' => [ + false, + [ + 'product' => 3, + 'qty' => 1, + 'super_group' => [ + 1 => 0, + ], + ], + [ + [ + 'sku' => 'virtual-product', + 'cart_qty' => 2 // This is a default quantity. + ], + ] + ], + ]; + } + + /** + * Write config value to database. + * + * @param string $path + * @param string $value + * @param string $scope + * @param int $scopeId + */ + private function changeConfigValue(string $path, string $value, string $scope = 'default', int $scopeId = 0) + { + $configValue = $this->objectManager->create(Value::class); + $configValue->setPath($path) + ->setValue($value) + ->setScope($scope) + ->setScopeId($scopeId) + ->save(); + $this->reinitConfig(); + } + + /** + * Delete config value from database. + * + * @param string $path + */ + private function dropConfigValue(string $path) + { + $configValue = $this->objectManager->create(Value::class); + try { + $configValue->load($path, 'path'); + $configValue->delete(); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + // do nothing + } + $this->reinitConfig(); + } + + /** + * Reinit config. + */ + private function reinitConfig() + { + $this->reinitableConfig->reinit(); + } } diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php new file mode 100644 index 0000000000000..b9f9046c4244c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php @@ -0,0 +1,75 @@ +\get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->isObjectNew(true); +$product->setTypeId( + \Magento\GroupedProduct\Model\Product\Type\Grouped::TYPE_CODE +)->setAttributeSetId( + 4 +)->setWebsiteIds( + [1] +)->setName( + 'Grouped Product' +)->setSku( + 'grouped-product' +)->setPrice( + 100 +)->setTaxClassId( + 0 +)->setVisibility( + \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH +)->setStatus( + \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED +); + +$newLinks = []; +$productLinkFactory = $objectManager->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class); + +/** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ +$productLink = $productLinkFactory->create(); +$linkedProduct = $productRepository->getById(1); +$productLink->setSku($product->getSku()) + ->setLinkType('associated') + ->setLinkedProductSku($linkedProduct->getSku()) + ->setLinkedProductType($linkedProduct->getTypeId()) + ->setPosition(1) + ->getExtensionAttributes() + ->setQty(1); +$newLinks[] = $productLink; + +$subProductsSkus = ['virtual-product', 'virtual-product-out']; +foreach ($subProductsSkus as $sku) { + /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ + $productLink = $productLinkFactory->create(); + $linkedProduct = $productRepository->get($sku); + $productLink->setSku($product->getSku()) + ->setLinkType('associated') + ->setLinkedProductSku($sku) + ->setLinkedProductType($linkedProduct->getTypeId()) + ->getExtensionAttributes() + ->setQty(2); + $newLinks[] = $productLink; +} +$product->setProductLinks($newLinks); +$product->save(); + +/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php new file mode 100644 index 0000000000000..48e7d495f8985 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php @@ -0,0 +1,10 @@ +\ Date: Fri, 5 Jan 2018 18:40:48 +0200 Subject: [PATCH 002/240] magento/magento2#12970: Can't add grouped product, with out of stock sub product, to cart. --- .../Magento/GroupedProduct/Model/Product/Type/GroupedTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index 554a20d8a18a7..cd45cb816ffbc 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -180,7 +180,6 @@ public function testOutOfStockSubProduct(bool $outOfStockShown, array $data, arr ); } } - } /** From da05e921b80ac0766b3965d1e693e7da742abb72 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Tue, 9 Jan 2018 14:19:44 +0200 Subject: [PATCH 003/240] magento/magento2#12970: Can't add grouped product, with out of stock sub product, to cart. --- .../Magento/GroupedProduct/Model/Product/Type/GroupedTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index cd45cb816ffbc..e177f77378985 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -145,7 +145,7 @@ public function testPrepareProduct() } /** - * Test adding grouped product to cart then one of subproducts is out of stock. + * Test adding grouped product to cart when one of subproducts is out of stock. * * @magentoDataFixture Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php * @magentoAppArea frontend From 40600b9aea44981db11b8094fd9b78ba5a91c904 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Tue, 9 Jan 2018 14:22:48 +0200 Subject: [PATCH 004/240] magento/magento2#12970: Can't add grouped product, with out of stock sub product, to cart. --- .../Model/Product/Type/GroupedTest.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index e177f77378985..12221498eba4d 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -197,7 +197,7 @@ public function outOfStockSubProductDataProvider() 'qty' => 1, 'super_group' => [ 1 => 4, - 21 => 5 + 21 => 5, ], ], [ @@ -207,9 +207,9 @@ public function outOfStockSubProductDataProvider() ], [ 'sku' => 'simple', - 'cart_qty' => 4 + 'cart_qty' => 4, ], - ] + ], ], 'Out of stock product are shown #2' => [ true, @@ -223,9 +223,9 @@ public function outOfStockSubProductDataProvider() [ [ 'sku' => 'virtual-product', - 'cart_qty' => 2 // This is a default quantity. + 'cart_qty' => 2, // This is a default quantity. ], - ] + ], ], 'Out of stock product are hidden #1' => [ false, @@ -234,19 +234,19 @@ public function outOfStockSubProductDataProvider() 'qty' => 1, 'super_group' => [ 1 => 4, - 21 => 5 + 21 => 5, ], ], [ [ 'sku' => 'virtual-product', - 'cart_qty' => 5 + 'cart_qty' => 5, ], [ 'sku' => 'simple', - 'cart_qty' => 4 + 'cart_qty' => 4, ], - ] + ], ], 'Out of stock product are hidden #2' => [ false, @@ -260,9 +260,9 @@ public function outOfStockSubProductDataProvider() [ [ 'sku' => 'virtual-product', - 'cart_qty' => 2 // This is a default quantity. + 'cart_qty' => 2, // This is a default quantity. ], - ] + ], ], ]; } From 63c7fc9b795d9ad284f546b80f8ae6a0c64b46f6 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Tue, 9 Jan 2018 14:24:38 +0200 Subject: [PATCH 005/240] magento/magento2#12970: Can't add grouped product, with out of stock sub product, to cart. --- .../Magento/GroupedProduct/Model/Product/Type/GroupedTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index 12221498eba4d..475e3dcfab33f 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -203,7 +203,7 @@ public function outOfStockSubProductDataProvider() [ [ 'sku' => 'virtual-product', - 'cart_qty' => 5 + 'cart_qty' => 5, ], [ 'sku' => 'simple', From 26b6a27ca619468f33165b4481bcc4bce9a56f95 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Mon, 2 Apr 2018 11:41:39 +0300 Subject: [PATCH 006/240] magento/magento2#12970: Can't add grouped product, with out of stock sub product, to cart. --- .../Magento/GroupedProduct/Model/Product/Type/GroupedTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index 475e3dcfab33f..7e1dc4246e3fb 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -150,7 +150,7 @@ public function testPrepareProduct() * @magentoDataFixture Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php * @magentoAppArea frontend * @magentoAppIsolation enabled - * @magentoDbIsolation disabled + * @magentoDbIsolation enabled * @dataProvider outOfStockSubProductDataProvider * @param bool $outOfStockShown * @param array $data From 274fee981c33ee22b296f99e1ca6cc009d7abaa2 Mon Sep 17 00:00:00 2001 From: Sunil Patel Date: Sat, 19 May 2018 15:50:34 +0530 Subject: [PATCH 007/240] 15259 : Unable to disable without providing Industry value --- app/code/Magento/Analytics/etc/adminhtml/system.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml index 889517e629e04..5d80917fc4daa 100644 --- a/app/code/Magento/Analytics/etc/adminhtml/system.xml +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -36,6 +36,9 @@ Magento\Analytics\Model\Config\Source\Vertical Magento\Analytics\Model\Config\Backend\Vertical Magento\Analytics\Block\Adminhtml\System\Config\Vertical + + 1 + From 99c14084df3eaa2272019447ceba8c14c439859f Mon Sep 17 00:00:00 2001 From: iGerchak <34220204+iGerchak@users.noreply.github.com> Date: Tue, 17 Jul 2018 18:32:18 +0300 Subject: [PATCH 008/240] Fix blocked a frame with origin --- .../view/frontend/web/js/page-cache.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js index fccc8510ffc70..08e23d0f4d3f5 100644 --- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js +++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js @@ -53,7 +53,22 @@ define([ } } - $(element).contents().each(function (index, el) { + // rewrite jQuery contents() + var contents = function (element) { + return $.map(element, function (elem) { + try { + return $.nodeName(elem, "iframe") ? + elem.contentDocument || (elem.contentWindow ? elem.contentWindow.document : []) : + $.merge([], elem.childNodes); + } catch (e) { + return []; + } + }); + }; + + var elementContents = contents($(element)); + + $.each(elementContents, function (index, el) { switch (el.nodeType) { case 1: // ELEMENT_NODE lookup(el); From d79cfa9009034d8df1a37587ecbed2d62225d3d6 Mon Sep 17 00:00:00 2001 From: Matthew Muscat Date: Thu, 19 Jul 2018 22:46:18 +1000 Subject: [PATCH 009/240] Resolve incorrect scope code selection when the requested scopeCode is null - resolves an issue whereby the incorrect scope could be returned when attempting to resolve a scope with a requested scopeCode of null - simplify the scope resolver logic to a avoid multiple if conditional checks - move scope interface class to an alias of the class - update docblock on the expected values available for scopeCode Signed-off-by: Matthew Muscat --- .../Framework/App/Config/ScopeCodeResolver.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php b/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php index 321997afdba95..8876f751f7c3d 100644 --- a/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php +++ b/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\App\Config; +use Magento\Framework\App\ScopeInterface; use Magento\Framework\App\ScopeResolverPool; /** @@ -34,7 +35,7 @@ public function __construct(ScopeResolverPool $scopeResolverPool) * Resolve scope code * * @param string $scopeType - * @param string $scopeCode + * @param string|null $scopeCode * @return string */ public function resolve($scopeType, $scopeCode) @@ -42,20 +43,25 @@ public function resolve($scopeType, $scopeCode) if (isset($this->resolvedScopeCodes[$scopeType][$scopeCode])) { return $this->resolvedScopeCodes[$scopeType][$scopeCode]; } - if (($scopeCode === null || is_numeric($scopeCode)) - && $scopeType !== ScopeConfigInterface::SCOPE_TYPE_DEFAULT - ) { + + if ($scopeType !== ScopeConfigInterface::SCOPE_TYPE_DEFAULT) { $scopeResolver = $this->scopeResolverPool->get($scopeType); $resolverScopeCode = $scopeResolver->getScope($scopeCode); - } else { + } + else { $resolverScopeCode = $scopeCode; } - if ($resolverScopeCode instanceof \Magento\Framework\App\ScopeInterface) { + if ($resolverScopeCode instanceof ScopeInterface) { $resolverScopeCode = $resolverScopeCode->getCode(); } + if ($scopeCode == null) { + $scopeCode = $resolverScopeCode; + } + $this->resolvedScopeCodes[$scopeType][$scopeCode] = $resolverScopeCode; + return $resolverScopeCode; } From a9c483885aaa4f47fa8eb1b8174e10cd6f7b78dd Mon Sep 17 00:00:00 2001 From: Matthew Muscat Date: Fri, 20 Jul 2018 11:56:16 +1000 Subject: [PATCH 010/240] update travis-ci for phpcs static test --- .../Magento/Framework/App/Config/ScopeCodeResolver.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php b/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php index 8876f751f7c3d..ea26b736eca98 100644 --- a/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php +++ b/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php @@ -47,8 +47,7 @@ public function resolve($scopeType, $scopeCode) if ($scopeType !== ScopeConfigInterface::SCOPE_TYPE_DEFAULT) { $scopeResolver = $this->scopeResolverPool->get($scopeType); $resolverScopeCode = $scopeResolver->getScope($scopeCode); - } - else { + } else { $resolverScopeCode = $scopeCode; } From 93edb382ab25fa01b7e50a76ccee0b6936de2679 Mon Sep 17 00:00:00 2001 From: Matthew Muscat Date: Fri, 20 Jul 2018 18:49:11 +1000 Subject: [PATCH 011/240] Update null conditional check --- lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php b/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php index ea26b736eca98..681af35944695 100644 --- a/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php +++ b/lib/internal/Magento/Framework/App/Config/ScopeCodeResolver.php @@ -55,7 +55,7 @@ public function resolve($scopeType, $scopeCode) $resolverScopeCode = $resolverScopeCode->getCode(); } - if ($scopeCode == null) { + if ($scopeCode === null) { $scopeCode = $resolverScopeCode; } From 5fa4d5f3176c47a0f598ce158c9ddc3977ef0f94 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" Date: Wed, 18 Jul 2018 19:41:47 +0300 Subject: [PATCH 012/240] magento/magento2#14510: Creating custom customer attribute with default value 0 will cause not saving value for customer entity. - fix default value check in case of type conversion; - add boolean attribute default value assignment; --- .../Catalog/Model/Product/Attribute/Backend/Boolean.php | 4 +++- .../Eav/Model/Entity/Attribute/AbstractAttribute.php | 2 +- .../Eav/Model/Entity/Attribute/Backend/AbstractBackend.php | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Boolean.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Boolean.php index c2108b0273bdb..dbefb09f06bd1 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Boolean.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Boolean.php @@ -25,7 +25,9 @@ public function beforeSave($object) $attributeCode = $this->getAttribute()->getName(); if ($object->getData('use_config_' . $attributeCode)) { $object->setData($attributeCode, BooleanSource::VALUE_USE_CONFIG); + return $this; } - return $this; + + return parent::beforeSave($object); } } diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index 0c97c7f5e2788..418c5b76b99d0 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -383,7 +383,7 @@ public function getIsVisibleOnFront() } /** - * @return string|int|bool|float + * @return string|null * @codeCoverageIgnore */ public function getDefaultValue() diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/AbstractBackend.php b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/AbstractBackend.php index 38e7b883f4ea5..e4bf1892d7222 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Backend/AbstractBackend.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Backend/AbstractBackend.php @@ -203,12 +203,12 @@ public function getEntityValueId($entity) /** * Retrieve default value * - * @return mixed + * @return string */ public function getDefaultValue() { if ($this->_defaultValue === null) { - if ($this->getAttribute()->getDefaultValue()) { + if ($this->getAttribute()->getDefaultValue() !== null) { $this->_defaultValue = $this->getAttribute()->getDefaultValue(); } else { $this->_defaultValue = ""; @@ -280,7 +280,7 @@ public function afterLoad($object) public function beforeSave($object) { $attrCode = $this->getAttribute()->getAttributeCode(); - if (!$object->hasData($attrCode) && $this->getDefaultValue()) { + if (!$object->hasData($attrCode) && $this->getDefaultValue() !== '') { $object->setData($attrCode, $this->getDefaultValue()); } From 0698aa6f369836f29c4c6cb841528d515bbc3ca8 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" Date: Sun, 22 Jul 2018 00:12:09 +0300 Subject: [PATCH 013/240] magento/magento2#14510: Creating custom customer attribute with default value 0 will cause not saving value for customer entity. - remove default value for 'links_exist' attribute of Downloadable Product; --- .../Downloadable/Setup/UpgradeData.php | 58 +++++++++++++++++++ app/code/Magento/Downloadable/etc/module.xml | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Downloadable/Setup/UpgradeData.php diff --git a/app/code/Magento/Downloadable/Setup/UpgradeData.php b/app/code/Magento/Downloadable/Setup/UpgradeData.php new file mode 100644 index 0000000000000..0f509ed32cbf2 --- /dev/null +++ b/app/code/Magento/Downloadable/Setup/UpgradeData.php @@ -0,0 +1,58 @@ +eavSetupFactory = $eavSetupFactory; + } + + /** + * {@inheritdoc} + */ + public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + $setup->startSetup(); + + if (version_compare($context->getVersion(), '2.0.3', '<')) { + /** @var EavSetup $eavSetup */ + $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); + // remove default value + $eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'links_exist', + 'default_value', + null + ); + } + + $setup->endSetup(); + } +} diff --git a/app/code/Magento/Downloadable/etc/module.xml b/app/code/Magento/Downloadable/etc/module.xml index 4c4e165feb014..fb13121335730 100644 --- a/app/code/Magento/Downloadable/etc/module.xml +++ b/app/code/Magento/Downloadable/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + From c63d1e636629a0035e23f54cdaa8b0f087f3802b Mon Sep 17 00:00:00 2001 From: Magently Date: Fri, 7 Sep 2018 16:42:27 +0200 Subject: [PATCH 014/240] Don't format Special Price value for Bundle Product --- .../Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 2266b84a7e873..41ef898dd4276 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 @@ -32,6 +32,7 @@ use Magento\Ui\DataProvider\Mapper\FormElement as FormElementMapper; use Magento\Ui\DataProvider\Mapper\MetaProperties as MetaPropertiesMapper; use Magento\Eav\Model\ResourceModel\Entity\Attribute\CollectionFactory as AttributeCollectionFactory; +use \Magento\Catalog\Model\Product\Type as ProductType; /** * Class Eav @@ -399,7 +400,11 @@ public function modifyData(array $data) foreach ($attributes as $attribute) { if (null !== ($attributeValue = $this->setupAttributeData($attribute))) { if ($attribute->getFrontendInput() === 'price' && is_scalar($attributeValue)) { - $attributeValue = $this->formatPrice($attributeValue); + if ($this->locator->getProduct()->getTypeId() !== ProductType::TYPE_BUNDLE + || $attribute->getAttributeCode() !== ProductAttributeInterface::CODE_SPECIAL_PRICE + ) { + $attributeValue = $this->formatPrice($attributeValue); + } } $data[$productId][self::DATA_SOURCE_DEFAULT][$attribute->getAttributeCode()] = $attributeValue; } From 9a4f260d33ea4603e34b112a4d72d934700de3de Mon Sep 17 00:00:00 2001 From: prakashpatel07 Date: Sat, 8 Sep 2018 09:46:18 +0000 Subject: [PATCH 015/240] Fixed Last Logged-in date when customer authenticate via REST API. --- .../Integration/Model/CustomerTokenService.php | 16 +++++++++++++--- .../Integration/etc/webapi_rest/events.xml | 12 ++++++++++++ .../Integration/etc/webapi_soap/events.xml | 12 ++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Integration/etc/webapi_rest/events.xml create mode 100644 app/code/Magento/Integration/etc/webapi_soap/events.xml diff --git a/app/code/Magento/Integration/Model/CustomerTokenService.php b/app/code/Magento/Integration/Model/CustomerTokenService.php index 947ca6f9f8821..c719179947624 100644 --- a/app/code/Magento/Integration/Model/CustomerTokenService.php +++ b/app/code/Magento/Integration/Model/CustomerTokenService.php @@ -14,6 +14,7 @@ use Magento\Integration\Model\ResourceModel\Oauth\Token\CollectionFactory as TokenCollectionFactory; use Magento\Integration\Model\Oauth\Token\RequestThrottler; use Magento\Framework\Exception\AuthenticationException; +use Magento\Framework\Event\ManagerInterface; class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServiceInterface { @@ -48,6 +49,11 @@ class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServ */ private $requestThrottler; + /** + * @var Magento\Framework\Event\ManagerInterface + */ + private $eventManager; + /** * Initialize service * @@ -55,16 +61,19 @@ class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServ * @param AccountManagementInterface $accountManagement * @param TokenCollectionFactory $tokenModelCollectionFactory * @param \Magento\Integration\Model\CredentialsValidator $validatorHelper + * @param \Magento\Framework\Event\ManagerInterface $eventManager */ public function __construct( TokenModelFactory $tokenModelFactory, AccountManagementInterface $accountManagement, - TokenCollectionFactory $tokenModelCollectionFactory, - CredentialsValidator $validatorHelper + TokenCollectionFactory $tokenModelCollectionFactory, + CredentialsValidator $validatorHelper, + ManagerInterface $eventManager ) { $this->tokenModelFactory = $tokenModelFactory; $this->accountManagement = $accountManagement; $this->tokenModelCollectionFactory = $tokenModelCollectionFactory; + $this->eventManager = $eventManager; $this->validatorHelper = $validatorHelper; } @@ -82,7 +91,8 @@ public function createCustomerAccessToken($username, $password) throw new AuthenticationException( __('You did not sign in correctly or your account is temporarily disabled.') ); - } + } + $this->eventManager->dispatch('customer_login', ['customer' => $customerDataObject]); $this->getRequestThrottler()->resetAuthenticationFailuresCount($username, RequestThrottler::USER_TYPE_CUSTOMER); return $this->tokenModelFactory->create()->createCustomerToken($customerDataObject->getId())->getToken(); } diff --git a/app/code/Magento/Integration/etc/webapi_rest/events.xml b/app/code/Magento/Integration/etc/webapi_rest/events.xml new file mode 100644 index 0000000000000..e978698734277 --- /dev/null +++ b/app/code/Magento/Integration/etc/webapi_rest/events.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/code/Magento/Integration/etc/webapi_soap/events.xml b/app/code/Magento/Integration/etc/webapi_soap/events.xml new file mode 100644 index 0000000000000..e978698734277 --- /dev/null +++ b/app/code/Magento/Integration/etc/webapi_soap/events.xml @@ -0,0 +1,12 @@ + + + + + + + From 55047c71ec782b86026450628c594ba414744c9c Mon Sep 17 00:00:00 2001 From: prakashpatel07 Date: Thu, 13 Sep 2018 10:16:29 +0000 Subject: [PATCH 016/240] Add unit test coverage for this changes. --- .../Magento/Integration/Model/CustomerTokenService.php | 4 ++-- .../Test/Unit/Model/CustomerTokenServiceTest.php | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Integration/Model/CustomerTokenService.php b/app/code/Magento/Integration/Model/CustomerTokenService.php index c719179947624..03253821eebc0 100644 --- a/app/code/Magento/Integration/Model/CustomerTokenService.php +++ b/app/code/Magento/Integration/Model/CustomerTokenService.php @@ -66,15 +66,15 @@ class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServ public function __construct( TokenModelFactory $tokenModelFactory, AccountManagementInterface $accountManagement, - TokenCollectionFactory $tokenModelCollectionFactory, + TokenCollectionFactory $tokenModelCollectionFactory, CredentialsValidator $validatorHelper, ManagerInterface $eventManager ) { $this->tokenModelFactory = $tokenModelFactory; $this->accountManagement = $accountManagement; $this->tokenModelCollectionFactory = $tokenModelCollectionFactory; - $this->eventManager = $eventManager; $this->validatorHelper = $validatorHelper; + $this->eventManager = $eventManager; } /** diff --git a/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php b/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php index ecd4788545c0a..55a8c1a2064bf 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php @@ -32,6 +32,9 @@ class CustomerTokenServiceTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Integration\Model\Oauth\Token|\PHPUnit_Framework_MockObject_MockObject */ private $_tokenMock; + /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $manager; + protected function setUp() { $this->_tokenFactoryMock = $this->getMockBuilder(\Magento\Integration\Model\Oauth\TokenFactory::class) @@ -67,11 +70,14 @@ protected function setUp() \Magento\Integration\Model\CredentialsValidator::class )->disableOriginalConstructor()->getMock(); + $this->manager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); + $this->_tokenService = new \Magento\Integration\Model\CustomerTokenService( $this->_tokenFactoryMock, $this->_accountManagementMock, $this->_tokenModelCollectionFactoryMock, - $this->validatorHelperMock + $this->validatorHelperMock, + $this->manager ); } From da2c92027aaf3e9ac00d161af88ba6874037ffe9 Mon Sep 17 00:00:00 2001 From: prakashpatel07 Date: Thu, 13 Sep 2018 11:31:45 +0000 Subject: [PATCH 017/240] Removed white spaces from code. --- app/code/Magento/Integration/Model/CustomerTokenService.php | 2 +- .../Integration/Test/Unit/Model/CustomerTokenServiceTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Integration/Model/CustomerTokenService.php b/app/code/Magento/Integration/Model/CustomerTokenService.php index 03253821eebc0..1433361e3a9b9 100644 --- a/app/code/Magento/Integration/Model/CustomerTokenService.php +++ b/app/code/Magento/Integration/Model/CustomerTokenService.php @@ -91,7 +91,7 @@ public function createCustomerAccessToken($username, $password) throw new AuthenticationException( __('You did not sign in correctly or your account is temporarily disabled.') ); - } + } $this->eventManager->dispatch('customer_login', ['customer' => $customerDataObject]); $this->getRequestThrottler()->resetAuthenticationFailuresCount($username, RequestThrottler::USER_TYPE_CUSTOMER); return $this->tokenModelFactory->create()->createCustomerToken($customerDataObject->getId())->getToken(); diff --git a/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php b/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php index 55a8c1a2064bf..170e7e42d919e 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php @@ -71,7 +71,7 @@ protected function setUp() )->disableOriginalConstructor()->getMock(); $this->manager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - + $this->_tokenService = new \Magento\Integration\Model\CustomerTokenService( $this->_tokenFactoryMock, $this->_accountManagementMock, From 85660e6ad8dfad744d97c591c8bd543c9289422e Mon Sep 17 00:00:00 2001 From: Ihor Sviziev Date: Fri, 14 Sep 2018 08:51:24 +0300 Subject: [PATCH 018/240] magento/magento2#16887 Fix blocked a frame with origin Add logging of exception --- app/code/Magento/PageCache/view/frontend/web/js/page-cache.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js index 08e23d0f4d3f5..e455102d3bf7e 100644 --- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js +++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js @@ -6,6 +6,7 @@ define([ 'jquery', 'domReady', + 'consoleLogger', 'jquery/ui', 'mage/cookies' ], function ($, domReady) { @@ -61,6 +62,7 @@ define([ elem.contentDocument || (elem.contentWindow ? elem.contentWindow.document : []) : $.merge([], elem.childNodes); } catch (e) { + consoleLogger.error(e); return []; } }); From 3bf066a5a5d5ba17d8b0c38bbc981af974d2b244 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev Date: Fri, 14 Sep 2018 08:53:55 +0300 Subject: [PATCH 019/240] magento/magento2#16887 Fix blocked a frame with origin Add logging of exception --- app/code/Magento/PageCache/view/frontend/web/js/page-cache.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js index e455102d3bf7e..2a43ff29a3593 100644 --- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js +++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js @@ -9,7 +9,7 @@ define([ 'consoleLogger', 'jquery/ui', 'mage/cookies' -], function ($, domReady) { +], function ($, domReady, consoleLogger) { 'use strict'; /** From 36570de1c7c7045d4a6a89e3b589412da0ff6bb6 Mon Sep 17 00:00:00 2001 From: Magently Date: Fri, 14 Sep 2018 11:12:02 +0200 Subject: [PATCH 020/240] Improve cyclomatic complexity in Eav modifier --- .../Product/Form/Modifier/Eav.php | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) 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 41ef898dd4276..61cf6bb19b66e 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 @@ -399,12 +399,11 @@ public function modifyData(array $data) foreach ($attributes as $attribute) { if (null !== ($attributeValue = $this->setupAttributeData($attribute))) { - if ($attribute->getFrontendInput() === 'price' && is_scalar($attributeValue)) { - if ($this->locator->getProduct()->getTypeId() !== ProductType::TYPE_BUNDLE - || $attribute->getAttributeCode() !== ProductAttributeInterface::CODE_SPECIAL_PRICE - ) { - $attributeValue = $this->formatPrice($attributeValue); - } + if ($attribute->getFrontendInput() === 'price' + && is_scalar($attributeValue) + && !$this->isBundleSpecialPrice($attribute) + ) { + $attributeValue = $this->formatPrice($attributeValue); } $data[$productId][self::DATA_SOURCE_DEFAULT][$attribute->getAttributeCode()] = $attributeValue; } @@ -414,6 +413,18 @@ public function modifyData(array $data) return $data; } + /** + * Obtain if current product is bundle and given attribute is special_price + * + * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @return bool + */ + private function isBundleSpecialPrice(ProductAttributeInterface $attribute) + { + return $this->locator->getProduct()->getTypeId() === ProductType::TYPE_BUNDLE + && $attribute->getAttributeCode() === ProductAttributeInterface::CODE_SPECIAL_PRICE; + } + /** * Resolve data persistence * From 1cb5445345c03f7cea1e2e0aba0d835731683416 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Tue, 18 Sep 2018 12:45:54 +0300 Subject: [PATCH 021/240] MAGETWO-86344: REST API extension_attributes for configurable products is empty when using search criteria on products --- .../Catalog/Model/ProductRepository.php | 67 +++++++++++++++---- .../Api/ProductRepositoryInterfaceTest.php | 5 ++ .../Magento/Catalog/_files/product_simple.php | 7 ++ setup/performance-toolkit/benchmark.jmx | 14 ++-- 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index dd941680b8a4d..738ac17838f78 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -18,6 +18,7 @@ use Magento\Framework\DB\Adapter\ConnectionException; use Magento\Framework\DB\Adapter\DeadlockException; use Magento\Framework\DB\Adapter\LockWaitException; +use Magento\Framework\EntityManager\Operation\Read\ReadExtensions; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; @@ -26,6 +27,7 @@ use Magento\Framework\Exception\ValidatorException; /** + * Product Repository. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ @@ -155,6 +157,11 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa */ private $serializer; + /** + * @var ReadExtensions + */ + private $readExtensions; + /** * ProductRepository constructor. * @param ProductFactory $productFactory @@ -180,6 +187,7 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa * @param CollectionProcessorInterface $collectionProcessor [optional] * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @param int $cacheLimit [optional] + * @param ReadExtensions|null $readExtensions * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -206,7 +214,8 @@ public function __construct( \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor, CollectionProcessorInterface $collectionProcessor = null, \Magento\Framework\Serialize\Serializer\Json $serializer = null, - $cacheLimit = 1000 + $cacheLimit = 1000, + ReadExtensions $readExtensions = null ) { $this->productFactory = $productFactory; $this->collectionFactory = $collectionFactory; @@ -229,12 +238,14 @@ public function __construct( $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); $this->cacheLimit = (int)$cacheLimit; + $this->readExtensions = $readExtensions ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(ReadExtensions::class); } /** - * {@inheritdoc} + * @inheritdoc */ - public function get($sku, $editMode = false, $storeId = null, $forceReload = false) + public function get($sku, $editMode = false, $storeId = null, $forceReload = false): ProductInterface { $cacheKey = $this->getCacheKey([$editMode, $storeId]); $cachedProduct = $this->getProductFromLocalCache($sku, $cacheKey); @@ -249,13 +260,14 @@ public function get($sku, $editMode = false, $storeId = null, $forceReload = fal $this->cacheProduct($cacheKey, $product); $cachedProduct = $product; } + return $cachedProduct; } /** - * {@inheritdoc} + * @inheritdoc */ - public function getById($productId, $editMode = false, $storeId = null, $forceReload = false) + public function getById($productId, $editMode = false, $storeId = null, $forceReload = false): ProductInterface { $cacheKey = $this->getCacheKey([$editMode, $storeId]); if (!isset($this->instancesById[$productId][$cacheKey]) || $forceReload) { @@ -272,6 +284,7 @@ public function getById($productId, $editMode = false, $storeId = null, $forceRe } $this->cacheProduct($cacheKey, $product); } + return $this->instancesById[$productId][$cacheKey]; } @@ -281,7 +294,7 @@ public function getById($productId, $editMode = false, $storeId = null, $forceRe * @param array $data * @return string */ - protected function getCacheKey($data) + protected function getCacheKey($data): string { $serializeData = []; foreach ($data as $key => $value) { @@ -292,6 +305,7 @@ protected function getCacheKey($data) } } $serializeData = $this->serializer->serialize($serializeData); + return sha1($serializeData); } @@ -322,7 +336,7 @@ private function cacheProduct($cacheKey, ProductInterface $product) * @return ProductInterface|Product * @throws NoSuchEntityException */ - protected function initializeProductData(array $productData, $createNew) + protected function initializeProductData(array $productData, $createNew): ProductInterface { unset($productData['media_gallery']); if ($createNew) { @@ -349,6 +363,8 @@ protected function initializeProductData(array $productData, $createNew) } /** + * Assign product to websites. + * * @param \Magento\Catalog\Model\Product $product * @return void */ @@ -371,7 +387,7 @@ private function assignProductToWebsites(\Magento\Catalog\Model\Product $product * @return $this * @throws NoSuchEntityException */ - private function processLinks(ProductInterface $product, $newLinks) + private function processLinks(ProductInterface $product, $newLinks): ProductRepository { if ($newLinks === null) { // If product links were not specified, don't do anything @@ -418,11 +434,12 @@ private function processLinks(ProductInterface $product, $newLinks) } $product->setProductLinks($newLinks); + return $this; } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -494,7 +511,7 @@ public function save(ProductInterface $product, $saveOptions = false) } /** - * {@inheritdoc} + * @inheritdoc */ public function delete(ProductInterface $product) { @@ -513,20 +530,22 @@ public function delete(ProductInterface $product) } $this->removeProductFromLocalCache($sku); unset($this->instancesById[$productId]); + return true; } /** - * {@inheritdoc} + * @inheritdoc */ public function deleteById($sku) { $product = $this->get($sku); + return $this->delete($product); } /** - * {@inheritdoc} + * @inheritdoc */ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria) { @@ -543,6 +562,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr $collection->load(); $collection->addCategoryIds(); + $this->addExtensionAttributes($collection); $searchResult = $this->searchResultsFactory->create(); $searchResult->setSearchCriteria($searchCriteria); $searchResult->setItems($collection->getItems()); @@ -553,7 +573,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr $this->getCacheKey( [ false, - $product->hasData(\Magento\Catalog\Model\Product::STORE_ID) ? $product->getStoreId() : null + $product->getStoreId() ] ), $product @@ -563,6 +583,21 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr return $searchResult; } + /** + * Add extension attributes to loaded items. + * + * @param Collection $collection + * @return Collection + */ + private function addExtensionAttributes(Collection $collection): Collection + { + foreach ($collection->getItems() as $item) { + $this->readExtensions->execute($item); + } + + return $collection; + } + /** * Helper function that adds a FilterGroup to the collection. * @@ -608,6 +643,8 @@ public function cleanCache() } /** + * Retrieve media gallery processor. + * * @return ProductRepository\MediaGalleryProcessor */ private function getMediaGalleryProcessor() @@ -616,6 +653,7 @@ private function getMediaGalleryProcessor() $this->mediaGalleryProcessor = \Magento\Framework\App\ObjectManager::getInstance() ->get(ProductRepository\MediaGalleryProcessor::class); } + return $this->mediaGalleryProcessor; } @@ -625,13 +663,14 @@ private function getMediaGalleryProcessor() * @deprecated 101.1.0 * @return CollectionProcessorInterface */ - private function getCollectionProcessor() + private function getCollectionProcessor(): CollectionProcessorInterface { if (!$this->collectionProcessor) { $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get( 'Magento\Catalog\Model\Api\SearchCriteria\ProductCollectionProcessor' ); } + return $this->collectionProcessor; } diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 8edb3ab333891..4d26e2b3b7f01 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -864,6 +864,7 @@ public function testGetList() $this->assertTrue(count($response['items']) > 0); $this->assertNotNull($response['items'][0]['sku']); + $this->assertNotNull($response['items'][0][ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['website_ids']); $this->assertEquals('simple', $response['items'][0]['sku']); $index = null; @@ -922,6 +923,7 @@ public function testGetListWithFilteringByWebsite() $this->assertTrue(count($response['items']) == 1); $this->assertTrue(isset($response['items'][0]['sku'])); $this->assertEquals('simple-2', $response['items'][0]['sku']); + $this->assertNotNull($response['items'][0][ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['website_ids']); } /** @@ -1068,6 +1070,9 @@ public function testGetListWithMultipleFilterGroupsAndSortingAndPagination() $this->assertEquals(3, $searchResult['total_count']); $this->assertEquals(1, count($searchResult['items'])); $this->assertEquals('search_product_4', $searchResult['items'][0][ProductInterface::SKU]); + $this->assertNotNull( + $searchResult['items'][0][ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['website_ids'] + ); } /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php index b3e5e96c18cac..0d07b8c913e14 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php @@ -5,6 +5,7 @@ */ use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; \Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(); @@ -19,10 +20,15 @@ $tierPriceFactory = $objectManager->get(\Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory::class); /** @var $tpExtensionAttributes */ $tpExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +/** @var $productExtensionAttributes */ +$productExtensionAttributesFactory = $objectManager->get(ProductExtensionInterfaceFactory::class); $adminWebsite = $objectManager->get(\Magento\Store\Api\WebsiteRepositoryInterface::class)->get('admin'); $tierPriceExtensionAttributes1 = $tpExtensionAttributesFactory->create() ->setWebsiteId($adminWebsite->getId()); +$productExtensionAttributesWebsiteIds = $productExtensionAttributesFactory->create( + ['website_ids' => $adminWebsite->getId()] +); $tierPrices[] = $tierPriceFactory->create( [ @@ -82,6 +88,7 @@ ->setTaxClassId(0) ->setTierPrices($tierPrices) ->setDescription('Description with html tag') + ->setExtensionAttributes($productExtensionAttributesWebsiteIds) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 8f8a72673d858..452664611168d 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -1360,7 +1360,7 @@ adminCategoryIdsList.add(vars.get("category_id")); false configurable_product_ids - \"id\":(\d+), + \"id\":(\d+),\"sku\" $1$ -1 @@ -1433,7 +1433,7 @@ productList.add(productMap); - + mpaf/tool/fragments/ce/setup/extract_configurable_products_for_edit.jmx @@ -1570,7 +1570,7 @@ productList.add(productMap); false configurable_product_for_edit_ids - \"id\":(\d+), + \"id\":(\d+),\"sku\" $1$ -1 @@ -1642,7 +1642,7 @@ editProductList.add(editProductMap); - + mpaf/tool/fragments/ce/setup/extract_simple_products_for_edit.jmx @@ -1793,7 +1793,7 @@ editProductList.add(editProductMap); false simple_product_for_edit_ids - \"id\":(\d+), + \"id\":(\d+),\"sku\" $1$ -1 @@ -1865,7 +1865,7 @@ editProductList.add(editProductMap); - + mpaf/tool/fragments/ce/setup/extract_simple_products.jmx @@ -2009,7 +2009,7 @@ editProductList.add(editProductMap); false simple_product_ids - \"id\":(\d+), + \"id\":(\d+),\"sku\" $1$ -1 From 2be64c95f1f47307b422ab49431b7d81c0b12dcd Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Tue, 18 Sep 2018 16:06:08 +0300 Subject: [PATCH 022/240] MAGETWO-94260: PayPal Billing Address for Registered Customers --- .../Model/Ui/PayPal/ConfigProvider.php | 8 +- .../Model/Ui/PayPal/ConfigProviderTest.php | 11 +- .../js/view/payment/method-renderer/paypal.js | 12 +- .../Magento/Paypal/Model/Express/Checkout.php | 87 ++++++------ .../Paypal/Model/Express/CheckoutTest.php | 129 ++++++++++++++---- 5 files changed, 172 insertions(+), 75 deletions(-) diff --git a/app/code/Magento/Braintree/Model/Ui/PayPal/ConfigProvider.php b/app/code/Magento/Braintree/Model/Ui/PayPal/ConfigProvider.php index e06b913db8ef4..4e422c795095f 100644 --- a/app/code/Magento/Braintree/Model/Ui/PayPal/ConfigProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/PayPal/ConfigProvider.php @@ -41,12 +41,14 @@ public function __construct(Config $config, ResolverInterface $resolver) } /** - * Retrieve assoc array of checkout configuration + * Retrieve assoc array of checkout configuration. * * @return array */ - public function getConfig() + public function getConfig(): array { + $requireBillingAddressAll = \Magento\Paypal\Model\Config::REQUIRE_BILLING_ADDRESS_ALL; + return [ 'payment' => [ self::PAYPAL_CODE => [ @@ -60,6 +62,8 @@ public function getConfig() 'vaultCode' => self::PAYPAL_VAULT_CODE, 'skipOrderReview' => $this->config->isSkipOrderReview(), 'paymentIcon' => $this->config->getPayPalIcon(), + 'isRequiredBillingAddress' => + (int)$this->config->isRequiredBillingAddress() === $requireBillingAddressAll, ] ] ]; diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/ConfigProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/ConfigProviderTest.php index 22f7f46bd98f1..d3dba400507f6 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/ConfigProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/PayPal/ConfigProviderTest.php @@ -47,9 +47,10 @@ protected function setUp() } /** - * Run test getConfig method + * Run test getConfig method. * * @param array $expected + * @return void * @dataProvider getConfigDataProvider */ public function testGetConfig($expected) @@ -77,13 +78,16 @@ public function testGetConfig($expected) 'width' => 30, 'height' => 26, 'url' => 'https://icon.test.url' ]); + $this->config->method('isRequiredBillingAddress') + ->willReturn(1); + self::assertEquals($expected, $this->configProvider->getConfig()); } /** * @return array */ - public function getConfigDataProvider() + public function getConfigDataProvider(): array { return [ [ @@ -101,7 +105,8 @@ public function getConfigDataProvider() 'skipOrderReview' => false, 'paymentIcon' => [ 'width' => 30, 'height' => 26, 'url' => 'https://icon.test.url' - ] + ], + 'isRequiredBillingAddress' => true, ] ] ] diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js index 253f3530701bc..c60504f2c44ce 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js @@ -206,7 +206,9 @@ define([ beforePlaceOrder: function (data) { this.setPaymentMethodNonce(data.nonce); - if (quote.billingAddress() === null && typeof data.details.billingAddress !== 'undefined') { + if ((this.isRequiredBillingAddress() || quote.billingAddress() === null) && + typeof data.details.billingAddress !== 'undefined' + ) { this.setBillingAddress(data.details, data.details.billingAddress); } @@ -264,6 +266,14 @@ define([ return window.checkoutConfig.payment[this.getCode()].isAllowShippingAddressOverride; }, + /** + * Is billing address required from PayPal side. + * @returns {Boolean} + */ + isRequiredBillingAddress: function () { + return window.checkoutConfig.payment[this.getCode()].isRequiredBillingAddress; + }, + /** * Get configuration for PayPal * @returns {Object} diff --git a/app/code/Magento/Paypal/Model/Express/Checkout.php b/app/code/Magento/Paypal/Model/Express/Checkout.php index 2e09d2bb6698b..a69cbd9daee8a 100644 --- a/app/code/Magento/Paypal/Model/Express/Checkout.php +++ b/app/code/Magento/Paypal/Model/Express/Checkout.php @@ -14,8 +14,8 @@ use Magento\Sales\Model\Order\Email\Sender\OrderSender; /** - * Wrapper that performs Paypal Express and Checkout communication - * Use current Paypal Express method instance + * Wrapper that performs Paypal Express and Checkout communication. + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -24,6 +24,7 @@ class Checkout { /** * Cache ID prefix for "pal" lookup + * * @var string */ const PAL_CACHE_ID = 'paypal_express_checkout_pal'; @@ -364,12 +365,13 @@ public function __construct( } /** - * Checkout with PayPal image URL getter - * Spares API calls of getting "pal" variable, by putting it into cache per store view + * Checkout with PayPal image URL getter. + * + * Spares API calls of getting "pal" variable, by putting it into cache per store view. * * @return string */ - public function getCheckoutShortcutImageUrl() + public function getCheckoutShortcutImageUrl(): string { // get "pal" thing from cache or lookup it via API $pal = null; @@ -597,8 +599,8 @@ public function canSkipOrderReviewStep() /** * Update quote when returned from PayPal - * rewrite billing address by paypal - * save old billing address for new customer + * + * Rewrite billing address by paypal, save old billing address for new customer, and * export shipping address in case address absence * * @param string $token @@ -614,14 +616,15 @@ public function returnFromPaypal($token) $this->ignoreAddressValidation(); + // check if we came from the Express Checkout button + $isButton = (bool)$quote->getPayment()->getAdditionalInformation(self::PAYMENT_INFO_BUTTON); + // import shipping address $exportedShippingAddress = $this->_getApi()->getExportedShippingAddress(); if (!$quote->getIsVirtual()) { $shippingAddress = $quote->getShippingAddress(); if ($shippingAddress) { - if ($exportedShippingAddress - && $quote->getPayment()->getAdditionalInformation(self::PAYMENT_INFO_BUTTON) == 1 - ) { + if ($exportedShippingAddress && $isButton) { $this->_setExportedAddressData($shippingAddress, $exportedShippingAddress); // PayPal doesn't provide detailed shipping info: prefix, middlename, lastname, suffix $shippingAddress->setPrefix(null); @@ -649,12 +652,11 @@ public function returnFromPaypal($token) } // import billing address - $portBillingFromShipping = $quote->getPayment()->getAdditionalInformation(self::PAYMENT_INFO_BUTTON) == 1 - && $this->_config->getValue( - 'requireBillingAddress' - ) != \Magento\Paypal\Model\Config::REQUIRE_BILLING_ADDRESS_ALL - && !$quote->isVirtual(); - if ($portBillingFromShipping) { + $requireBillingAddress = (int)$this->_config->getValue( + 'requireBillingAddress' + ) === \Magento\Paypal\Model\Config::REQUIRE_BILLING_ADDRESS_ALL; + + if ($isButton && !$requireBillingAddress && !$quote->isVirtual()) { $billingAddress = clone $shippingAddress; $billingAddress->unsAddressId()->unsAddressType()->setCustomerAddressId(null); $data = $billingAddress->getData(); @@ -662,11 +664,17 @@ public function returnFromPaypal($token) $quote->getBillingAddress()->addData($data); $quote->getShippingAddress()->setSameAsBilling(1); } else { - $billingAddress = $quote->getBillingAddress(); + $billingAddress = $quote->getBillingAddress()->setCustomerAddressId(null); } $exportedBillingAddress = $this->_getApi()->getExportedBillingAddress(); - $this->_setExportedAddressData($billingAddress, $exportedBillingAddress); + // Since country is required field for billing and shipping address, + // we consider the address information to be empty if country is empty. + $isEmptyAddress = ($billingAddress->getCountryId() === null); + + if ($requireBillingAddress || $isEmptyAddress) { + $this->_setExportedAddressData($billingAddress, $exportedBillingAddress); + } $billingAddress->setCustomerNote($exportedBillingAddress->getData('note')); $quote->setBillingAddress($billingAddress); $quote->setCheckoutMethod($this->getCheckoutMethod()); @@ -894,7 +902,7 @@ public function getCheckoutMethod() } /** - * Sets address data from exported address + * Sets address data from exported address. * * @param Address $address * @param array $exportedAddress @@ -902,17 +910,6 @@ public function getCheckoutMethod() */ protected function _setExportedAddressData($address, $exportedAddress) { - // Exported data is more priority if we came from Express Checkout button - $isButton = (bool)$this->_quote->getPayment()->getAdditionalInformation(self::PAYMENT_INFO_BUTTON); - - // Since country is required field for billing and shipping address, - // we consider the address information to be empty if country is empty. - $isEmptyAddress = ($address->getCountryId() === null); - - if (!$isButton && !$isEmptyAddress) { - return; - } - foreach ($exportedAddress->getExportedKeys() as $key) { $data = $exportedAddress->getData($key); if (!empty($data)) { @@ -949,9 +946,11 @@ protected function _setBillingAgreementRequest() } /** + * Get api + * * @return \Magento\Paypal\Model\Api\Nvp */ - protected function _getApi() + protected function _getApi(): \Magento\Paypal\Model\Api\Nvp { if (null === $this->_api) { $this->_api = $this->_apiTypeFactory->create($this->_apiType)->setConfigObject($this->_config); @@ -960,9 +959,10 @@ protected function _getApi() } /** - * Attempt to collect address shipping rates and return them for further usage in instant update API - * Returns empty array if it was impossible to obtain any shipping rate - * If there are shipping rates obtained, the method must return one of them as default. + * Attempt to collect address shipping rates and return them for further usage in instant update API. + * + * Returns empty array if it was impossible to obtain any shipping rate and + * if there are shipping rates obtained, the method must return one of them as default. * * @param Address $address * @param bool $mayReturnEmpty @@ -1043,16 +1043,16 @@ protected function _prepareShippingOptions(Address $address, $mayReturnEmpty = f } /** - * Compare two shipping options based on their amounts + * Compare two shipping options based on their amounts. * - * This function is used as a callback comparison function in shipping options sorting process - * @see self::_prepareShippingOptions() + * This function is used as a callback comparison function in shipping options sorting process. * + * @see self::_prepareShippingOptions() * @param \Magento\Framework\DataObject $option1 * @param \Magento\Framework\DataObject $option2 * @return int */ - protected static function cmpShippingOptions(DataObject $option1, DataObject $option2) + protected static function cmpShippingOptions(DataObject $option1, DataObject $option2): int { if ($option1->getAmount() == $option2->getAmount()) { return 0; @@ -1061,7 +1061,8 @@ protected static function cmpShippingOptions(DataObject $option1, DataObject $op } /** - * Try to find whether the code provided by PayPal corresponds to any of possible shipping rates + * Try to find whether the code provided by PayPal corresponds to any of possible shipping rates. + * * This method was created only because PayPal has issues with returning the selected code. * If in future the issue is fixed, we don't need to attempt to match it. It would be enough to set the method code * before collecting shipping rates @@ -1070,7 +1071,7 @@ protected static function cmpShippingOptions(DataObject $option1, DataObject $op * @param string $selectedCode * @return string */ - protected function _matchShippingMethodCode(Address $address, $selectedCode) + protected function _matchShippingMethodCode(Address $address, $selectedCode): string { $options = $this->_prepareShippingOptions($address, false); foreach ($options as $option) { @@ -1086,7 +1087,8 @@ protected function _matchShippingMethodCode(Address $address, $selectedCode) } /** - * Create payment redirect url + * Create payment redirect url. + * * @param bool|null $button * @param string $token * @return void @@ -1109,7 +1111,8 @@ public function getCustomerSession() } /** - * Set shipping options to api + * Set shipping options to api. + * * @param \Magento\Paypal\Model\Cart $cart * @param \Magento\Quote\Model\Quote\Address|null $address * @return void diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php index 412ecd80847c1..96a1d3f6e9e62 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php @@ -293,7 +293,9 @@ public function testReturnFromPaypal() /** * The case when handling address data from Paypal button. * System's address fields are replacing from export Paypal data. + * Billing and Shipping address are the same. * + * @return void * @magentoDataFixture Magento/Paypal/_files/quote_payment_express_with_customer.php * @magentoAppIsolation enabled * @magentoDbIsolation enabled @@ -307,39 +309,96 @@ public function testReturnFromPaypalButton() $this->checkoutModel->returnFromPaypal('token'); $shippingAddress = $quote->getShippingAddress(); - - $prefix = ''; - $this->assertEquals([$prefix . $this->getExportedData()['street']], $shippingAddress->getStreet()); - $this->assertEquals($prefix . $this->getExportedData()['firstname'], $shippingAddress->getFirstname()); - $this->assertEquals($prefix . $this->getExportedData()['city'], $shippingAddress->getCity()); - $this->assertEquals($prefix . $this->getExportedData()['telephone'], $shippingAddress->getTelephone()); - $this->assertEquals($prefix . $this->getExportedData()['email'], $shippingAddress->getEmail()); + $billingAddress = $quote->getBillingAddress(); + $exportedShippingData = $this->getExportedData()['shipping']; + + $this->assertEquals([$exportedShippingData['street']], $shippingAddress->getStreet()); + $this->assertEquals($exportedShippingData['firstname'], $shippingAddress->getFirstname()); + $this->assertEquals($exportedShippingData['city'], $shippingAddress->getCity()); + $this->assertEquals($exportedShippingData['telephone'], $shippingAddress->getTelephone()); + $this->assertEquals($exportedShippingData['email'], $shippingAddress->getEmail()); + + $this->assertEquals([$exportedShippingData['street']], $billingAddress->getStreet()); + $this->assertEquals($exportedShippingData['firstname'], $billingAddress->getFirstname()); + $this->assertEquals($exportedShippingData['city'], $billingAddress->getCity()); + $this->assertEquals($exportedShippingData['telephone'], $billingAddress->getTelephone()); + $this->assertEquals($exportedShippingData['email'], $billingAddress->getEmail()); } /** * The case when handling address data from the checkout. * System's address fields are not replacing from export PayPal data. + * Billing and Shipping address are the same * + * @return void * @magentoDataFixture Magento/Paypal/_files/quote_payment_express_with_customer.php * @magentoAppIsolation enabled * @magentoDbIsolation enabled */ public function testReturnFromPaypalIfCheckout() { + $prefix = 'exported'; $quote = $this->getFixtureQuote(); - $this->prepareCheckoutModel($quote); + $this->prepareCheckoutModel($quote, $prefix); $quote->getPayment()->setAdditionalInformation(Checkout::PAYMENT_INFO_BUTTON, 0); + $originalShippingAddress = $quote->getShippingAddress(); + $originalBillingAddress = $quote->getBillingAddress(); + $this->checkoutModel->returnFromPaypal('token'); $shippingAddress = $quote->getShippingAddress(); + $billingAddress = $quote->getBillingAddress(); + $this->assertEquals($originalShippingAddress->getStreet(), $shippingAddress->getStreet()); + $this->assertEquals($originalShippingAddress->getFirstname(), $shippingAddress->getFirstname()); + $this->assertEquals($originalShippingAddress->getCity(), $shippingAddress->getCity()); + $this->assertEquals($originalShippingAddress->getTelephone(), $shippingAddress->getTelephone()); + + $this->assertEquals($originalBillingAddress->getStreet(), $billingAddress->getStreet()); + $this->assertEquals($originalBillingAddress->getFirstname(), $billingAddress->getFirstname()); + $this->assertEquals($originalBillingAddress->getCity(), $billingAddress->getCity()); + $this->assertEquals($originalBillingAddress->getTelephone(), $billingAddress->getTelephone()); + } + + /** + * The case when handling address data from the checkout. + * System's address fields are replacing billing address from export PayPal data. + * Billing and Shipping address are different + * + * @return void + * @magentoDataFixture Magento/Paypal/_files/quote_payment_express_with_customer.php + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ + public function testReturnFromPaypalIfCheckoutWithReturnBillingAddress() + { $prefix = 'exported'; + $quote = $this->getFixtureQuote(); + $this->paypalConfig->expects($this->exactly(2)) + ->method('getValue') + ->with('requireBillingAddress') + ->willReturn(1); + $this->prepareCheckoutModel($quote, $prefix); + $quote->getPayment()->setAdditionalInformation(Checkout::PAYMENT_INFO_BUTTON, 0); + + $originalShippingAddress = $quote->getShippingAddress(); + + $this->checkoutModel->returnFromPaypal('token'); + + $shippingAddress = $quote->getShippingAddress(); + $billingAddress = $quote->getBillingAddress(); + $exportedBillingData = $this->getExportedData()['billing']; - $this->assertNotEquals([$prefix . $this->getExportedData()['street']], $shippingAddress->getStreet()); - $this->assertNotEquals($prefix . $this->getExportedData()['firstname'], $shippingAddress->getFirstname()); - $this->assertNotEquals($prefix . $this->getExportedData()['city'], $shippingAddress->getCity()); - $this->assertNotEquals($prefix . $this->getExportedData()['telephone'], $shippingAddress->getTelephone()); + $this->assertEquals($originalShippingAddress->getStreet(), $shippingAddress->getStreet()); + $this->assertEquals($originalShippingAddress->getFirstname(), $shippingAddress->getFirstname()); + $this->assertEquals($originalShippingAddress->getCity(), $shippingAddress->getCity()); + $this->assertEquals($originalShippingAddress->getTelephone(), $shippingAddress->getTelephone()); + + $this->assertEquals([$prefix . $exportedBillingData['street']], $billingAddress->getStreet()); + $this->assertEquals($prefix . $exportedBillingData['firstname'], $billingAddress->getFirstname()); + $this->assertEquals($prefix . $exportedBillingData['city'], $billingAddress->getCity()); + $this->assertEquals($prefix . $exportedBillingData['telephone'], $billingAddress->getTelephone()); } /** @@ -347,6 +406,7 @@ public function testReturnFromPaypalIfCheckout() * Customer add virtual product to quote and place order using PayPal Express method. * After return from PayPal quote billing address have to be updated by PayPal Express address. * + * @return void * @magentoDataFixture Magento/Paypal/_files/virtual_quote_with_empty_billing_address.php * @magentoConfigFixture current_store payment/paypal_express/active 1 * @magentoDbIsolation enabled @@ -361,7 +421,7 @@ public function testReturnFromPaypalForCustomerWithEmptyAddresses() $billingAddress = $quote->getBillingAddress(); - $this->performQuoteAddressAssertions($billingAddress, $this->getExportedData()); + $this->performQuoteAddressAssertions($billingAddress, $this->getExportedData()['billing']); } /** @@ -430,8 +490,9 @@ private function performQuoteAddressAssertions(Address $address, array $expected * Initialize a checkout model mock. * * @param Quote $quote + * @return void */ - private function prepareCheckoutModel(Quote $quote) + private function prepareCheckoutModel(Quote $quote, $prefix = '') { $this->checkoutModel = $this->objectManager->create( Checkout::class, @@ -442,11 +503,11 @@ private function prepareCheckoutModel(Quote $quote) ] ); - $exportedBillingAddress = $this->getExportedAddressFixture($this->getExportedData()); + $exportedBillingAddress = $this->getExportedAddressFixture($this->getExportedData()['billing'], $prefix); $this->api->method('getExportedBillingAddress') ->will($this->returnValue($exportedBillingAddress)); - $exportedShippingAddress = $this->getExportedAddressFixture($this->getExportedData()); + $exportedShippingAddress = $this->getExportedAddressFixture($this->getExportedData()['shipping'], $prefix); $this->api->method('getExportedShippingAddress') ->will($this->returnValue($exportedShippingAddress)); @@ -459,19 +520,33 @@ private function prepareCheckoutModel(Quote $quote) * * @return array */ - private function getExportedData() + private function getExportedData(): array { return [ - 'email' => 'customer@example.com', - 'firstname' => 'John', - 'lastname' => 'Doe', - 'country' => 'US', - 'region' => 'Colorado', - 'region_id' => '13', - 'city' => 'Denver', - 'street' => '66 Pearl St', - 'postcode' => '80203', - 'telephone' => '555-555-555', + 'shipping' => [ + 'email' => 'customer@example.com', + 'firstname' => 'John', + 'lastname' => 'Doe', + 'country' => 'US', + 'region' => 'Colorado', + 'region_id' => '13', + 'city' => 'Denver', + 'street' => '66 Pearl St', + 'postcode' => '80203', + 'telephone' => '555-555-555' + ], + 'billing' => [ + 'email' => 'customer@example.com', + 'firstname' => 'Jane', + 'lastname' => 'Doe', + 'country' => 'US', + 'region' => 'Texas', + 'region_id' => '13', + 'city' => 'Austin', + 'street' => '1100 Congress Ave', + 'postcode' => '78701', + 'telephone' => '555-555-555' + ] ]; } From f8968c28cae24769bb6ff9079c78e7526df1df92 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Wed, 19 Sep 2018 09:59:36 +0300 Subject: [PATCH 023/240] MAGETWO-86344: REST API extension_attributes for configurable products is empty when using search criteria on products --- app/code/Magento/Catalog/Model/ProductRepository.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 738ac17838f78..fb6e8a6c8a311 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -245,7 +245,7 @@ public function __construct( /** * @inheritdoc */ - public function get($sku, $editMode = false, $storeId = null, $forceReload = false): ProductInterface + public function get($sku, $editMode = false, $storeId = null, $forceReload = false) { $cacheKey = $this->getCacheKey([$editMode, $storeId]); $cachedProduct = $this->getProductFromLocalCache($sku, $cacheKey); @@ -267,7 +267,7 @@ public function get($sku, $editMode = false, $storeId = null, $forceReload = fal /** * @inheritdoc */ - public function getById($productId, $editMode = false, $storeId = null, $forceReload = false): ProductInterface + public function getById($productId, $editMode = false, $storeId = null, $forceReload = false) { $cacheKey = $this->getCacheKey([$editMode, $storeId]); if (!isset($this->instancesById[$productId][$cacheKey]) || $forceReload) { @@ -294,7 +294,7 @@ public function getById($productId, $editMode = false, $storeId = null, $forceRe * @param array $data * @return string */ - protected function getCacheKey($data): string + protected function getCacheKey($data) { $serializeData = []; foreach ($data as $key => $value) { @@ -336,7 +336,7 @@ private function cacheProduct($cacheKey, ProductInterface $product) * @return ProductInterface|Product * @throws NoSuchEntityException */ - protected function initializeProductData(array $productData, $createNew): ProductInterface + protected function initializeProductData(array $productData, $createNew) { unset($productData['media_gallery']); if ($createNew) { @@ -387,7 +387,7 @@ private function assignProductToWebsites(\Magento\Catalog\Model\Product $product * @return $this * @throws NoSuchEntityException */ - private function processLinks(ProductInterface $product, $newLinks): ProductRepository + private function processLinks(ProductInterface $product, $newLinks) { if ($newLinks === null) { // If product links were not specified, don't do anything @@ -663,7 +663,7 @@ private function getMediaGalleryProcessor() * @deprecated 101.1.0 * @return CollectionProcessorInterface */ - private function getCollectionProcessor(): CollectionProcessorInterface + private function getCollectionProcessor() { if (!$this->collectionProcessor) { $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get( From ae9a3cb30acc7605ed6794cee0a9ccc7822a3944 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun Date: Fri, 21 Sep 2018 15:44:02 +0300 Subject: [PATCH 024/240] MAGETWO-86098: Cart Price Rule for configurable products --- ...minCategoryProductAttributeActionGroup.xml | 13 ++ .../Page/AdminProductAttributeEditPage.xml | 1 + ...itAttributeStorefrontPropertiesSection.xml | 15 ++ .../AdminProductAttributeGridSection.xml | 1 + .../Section/CheckoutCartSummarySection.xml | 2 + .../Magento/ConfigurableProduct/composer.json | 1 + .../Magento/ConfigurableProduct/etc/di.xml | 7 + .../Model/Rule/Condition/Product.php | 128 +++++++++++++ .../Model/Rule/Condition/Product/Combine.php | 71 +++++++ .../Test/Mftf/Data/SalesRuleData.xml | 9 + .../AdminCartPriceRulesFormSection.xml | 7 + .../Section/StorefrontDiscountSection.xml | 15 ++ ...artPriceRuleForConfigurableProductTest.xml | 174 ++++++++++++++++++ .../Unit/Model/Rule/Condition/ProductTest.php | 4 +- 14 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminEditAttributeStorefrontPropertiesSection.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml index b1300f63d967c..7fc35a8dd0f10 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml @@ -25,4 +25,17 @@ + + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml index 3d7e79343155b..e7de264dc2b75 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml @@ -11,5 +11,6 @@
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditAttributeStorefrontPropertiesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditAttributeStorefrontPropertiesSection.xml new file mode 100644 index 0000000000000..e430998f1e2be --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditAttributeStorefrontPropertiesSection.xml @@ -0,0 +1,15 @@ + + + + +
+ + +
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index bb944270a10a9..7fe22d46f25fb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -16,5 +16,6 @@ +
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index 80f6cfa03d969..e597e55aaa003 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -17,5 +17,7 @@ + +
diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json index 33ee30a25b012..eae9dac046b23 100644 --- a/app/code/Magento/ConfigurableProduct/composer.json +++ b/app/code/Magento/ConfigurableProduct/composer.json @@ -19,6 +19,7 @@ "suggest": { "magento/module-webapi": "100.2.*", "magento/module-sales": "101.0.*", + "magento/module-sales-rule": "101.0.*", "magento/module-product-video": "100.2.*", "magento/module-configurable-sample-data": "Sample Data version:100.2.*", "magento/module-product-links-sample-data": "Sample Data version:100.2.*" diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index dfbad0dd5a764..1dbb0969687d5 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -228,4 +228,11 @@ + + + + false + + + diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php index d2e7cabe473f4..e54823c78684a 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php @@ -25,6 +25,134 @@ protected function _addSpecialAttributes(array &$attributes) $attributes['quote_item_qty'] = __('Quantity in cart'); $attributes['quote_item_price'] = __('Price in cart'); $attributes['quote_item_row_total'] = __('Row total in cart'); + + $attributes['parent::category_ids'] = __('Category (Parent only)'); + $attributes['children::category_ids'] = __('Category (Children Only)'); + } + + /** + * Retrieve attribute + * + * @return string + */ + public function getAttribute(): string + { + $attribute = $this->getData('attribute'); + if (strpos($attribute, '::') !== false) { + list (, $attribute) = explode('::', $attribute); + } + + return $attribute; + } + + /** + * @inheritdoc + */ + public function getAttributeName() + { + $attribute = $this->getAttribute(); + if ($this->getAttributeScope()) { + $attribute = $this->getAttributeScope() . '::' . $attribute; + } + + return $this->getAttributeOption($attribute); + } + + /** + * @inheritdoc + */ + public function loadAttributeOptions() + { + $productAttributes = $this->_productResource->loadAllAttributes()->getAttributesByCode(); + + $attributes = []; + foreach ($productAttributes as $attribute) { + /* @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ + if (!$attribute->isAllowedForRuleCondition() + || !$attribute->getDataUsingMethod($this->_isUsedForRuleProperty) + ) { + continue; + } + $frontLabel = $attribute->getFrontendLabel(); + $attributes[$attribute->getAttributeCode()] = $frontLabel; + $attributes['parent::' . $attribute->getAttributeCode()] = $frontLabel . __('(Parent Only)'); + $attributes['children::' . $attribute->getAttributeCode()] = $frontLabel . __('(Children Only)'); + } + + $this->_addSpecialAttributes($attributes); + + asort($attributes); + $this->setAttributeOption($attributes); + + return $this; + } + + /** + * @inheritdoc + */ + public function getAttributeElementHtml() + { + $html = parent::getAttributeElementHtml() . + $this->getAttributeScopeElement()->getHtml(); + + return $html; + } + + /** + * Retrieve form element for scope element + * + * @return \Magento\Framework\Data\Form\Element\AbstractElement + */ + private function getAttributeScopeElement(): \Magento\Framework\Data\Form\Element\AbstractElement + { + return $this->getForm()->addField( + $this->getPrefix() . '__' . $this->getId() . '__attribute_scope', + 'hidden', + [ + 'name' => $this->elementName . '[' . $this->getPrefix() . '][' . $this->getId() . '][attribute_scope]', + 'value' => $this->getAttributeScope(), + 'no_span' => true, + 'class' => 'hidden', + 'data-form-part' => $this->getFormName() + ] + ); + } + + /** + * Set attribute value + * + * @param string $value + * @return void + */ + public function setAttribute(string $value) + { + if (strpos($value, '::') !== false) { + list($scope, $attribute) = explode('::', $value); + $this->setData('attribute_scope', $scope); + $this->setData('attribute', $attribute); + } else { + $this->setData('attribute', $value); + } + } + + /** + * @inheritdoc + */ + public function loadArray($arr) + { + parent::loadArray($arr); + $this->setAttributeScope(isset($arr['attribute_scope']) ? $arr['attribute_scope'] : null); + return $this; + } + + /** + * @inheritdoc + */ + public function asArray(array $arrAttributes = []) + { + $out = parent::asArray($arrAttributes); + $out['attribute_scope'] = $this->getAttributeScope(); + return $out; } /** diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php index b5ac02e67b1e1..975f26c90b382 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php @@ -8,6 +8,8 @@ use Magento\Catalog\Model\ResourceModel\Product\Collection; /** + * Combine conditions for product. + * * @api * @since 100.0.2 */ @@ -85,4 +87,73 @@ public function collectValidatedAttributes($productCollection) } return $this; } + + /** + * @inheritdoc + */ + protected function _isValid($entity) + { + if (!$this->getConditions()) { + return true; + } + + $all = $this->getAggregator() === 'all'; + $true = (bool)$this->getValue(); + + foreach ($this->getConditions() as $cond) { + if ($entity instanceof \Magento\Framework\Model\AbstractModel) { + $validated = $this->validateEntity($cond, $entity); + } else { + $validated = $cond->validateByEntityId($entity); + } + if ($all && $validated !== $true) { + return false; + } elseif (!$all && $validated === $true) { + return true; + } + } + return $all ? true : false; + } + + /** + * Validate entity. + * + * @param mixed $cond + * @param \Magento\Framework\Model\AbstractModel $entity + * @return bool + */ + private function validateEntity($cond, \Magento\Framework\Model\AbstractModel $entity): bool + { + $true = (bool)$this->getValue(); + $validated = !$true; + foreach ($this->retrieveValidateEntities($cond->getAttributeScope(), $entity) as $validateEntity) { + $validated = $cond->validate($validateEntity); + if ($validated === $true) { + break; + } + } + + return $validated; + } + + /** + * Retrieve entities for validation by attribute scope + * + * @param string $attributeScope + * @param \Magento\Framework\Model\AbstractModel $entity + * @return \Magento\Framework\Model\AbstractModel[] + */ + private function retrieveValidateEntities($attributeScope, \Magento\Framework\Model\AbstractModel $entity): array + { + if ($attributeScope === 'parent') { + $validateEntities = [$entity]; + } elseif ($attributeScope === 'children') { + $validateEntities = $entity->getChildren() ?: [$entity]; + } else { + $validateEntities = $entity->getChildren() ?: []; + $validateEntities[] = $entity; + } + + return $validateEntities; + } } diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index d769e18c0cec3..035567491d588 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -45,4 +45,13 @@ Percent of product price discount 50 + + SimpleSalesRule + Main Website + 'NOT LOGGED IN' + true + Specific Coupon + ABCD + 70 + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index 86b97d6b724a6..95fafa37c4042 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -16,6 +16,8 @@ + + @@ -23,5 +25,10 @@ + + + + +
diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml new file mode 100644 index 0000000000000..d438c4d3a7f2f --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml @@ -0,0 +1,15 @@ + + + +
+ + + +
+
diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml new file mode 100644 index 0000000000000..8522e2efc5d7b --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -0,0 +1,174 @@ + + + + + + + + + + <description value="Checking Cart Price Rule for configurable products"/> + <severity value="BLOCKER"/> + <testCaseId value="MAGETWO-95121"/> + <group value="SalesRule"/> + </annotations> + + <before> + <!--Login to Admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create the category --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Create the configurable product and add it to the category --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <!-- Add the attribute we just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <!-- Get the option of the attribute we created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <!-- Create a simple product and give it the attribute with option --> + <createData entity="SimpleOption" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="SimpleOption" stepKey="createConfigChildProduct2"> + <field key="sku">SimpleTwoOption</field> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <!-- Create the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <!-- Add simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!--Set attribute sku property Use for Promo Rule Conditions = Yes and save attribute--> + <actionGroup ref="navigateToProductAttribute" stepKey="goToProductAttributeSkuPage"> + <argument name="attributeCode" value="sku"/> + </actionGroup> + <click selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> + <scrollTo selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="ScrollToUseForPromoRuleConditions"/> + <selectOption selector="{{AdminEditAttributeStorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="Yes" stepKey="changeAttributeProperty"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveAttribute"/> + <waitForPageLoad stepKey="waitForAttributeIsSaved"/> + + <!-- Create cart price rule --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SalesRuleWithSkuInActions.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="{{SalesRuleWithSkuInActions.websites}}" stepKey="selectWebsites"/> + <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="{{SalesRuleWithSkuInActions.coupon_type}}" stepKey="selectCouponType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{SalesRuleWithSkuInActions.coupon_code}}" stepKey="fillCouponCOde"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="{{SalesRuleWithSkuInActions.discount_amount}}" stepKey="fillDiscountAmount"/> + <scrollTo selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="ScrollToApplyRuleForConditions"/> + <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="AddConditionForRule"/> + <waitForPageLoad stepKey="waitForDropDownOpened"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="SKU(Children Only)" stepKey="selectAttribute"/> + <waitForPageLoad stepKey="waitForOperatorOpened"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('is')}}" stepKey="clickToChooseCondition"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.operator}}" userInput="is not one of" stepKey="selectOperator"/> + <waitForPageLoad stepKey="waitForOperatorOpened1"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption"/> + <waitForPageLoad stepKey="waitForConditionOpened2"/> + <fillField selector="{{AdminCartPriceRulesFormSection.optionInput}}" userInput="$$createConfigChildProduct1.sku$$" stepKey="selectOption"/> + <waitForPageLoad stepKey="waitForPageLoaded"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + </before> + + <after> + <!--Remove SalesRule--> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteSalesRule"> + <argument name="ruleName" value="{{SalesRuleWithSkuInActions.name}}"/> + </actionGroup> + <!--Return default value to attribute sku--> + <actionGroup ref="navigateToProductAttribute" stepKey="goToProductAttributeSkuPage"> + <argument name="attributeCode" value="sku"/> + </actionGroup> + <click selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab1"/> + <scrollTo selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="ScrollToUseForPromoRuleConditions"/> + <selectOption selector="{{AdminEditAttributeStorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="No" stepKey="changeAttributeProperty"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveAttribute"/> + <waitForPageLoad stepKey="waitForAttributeIsSaved"/> + + <!--Remove configurable product and it's children--> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> + <!--Logout from Admin--> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Add the configureble product with first option to the cart --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.sku$$)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + userInput="$$getConfigAttributeOption1.label$$" stepKey="selectOption1"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <!--Add the configureble product with second option to the cart --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.sku$$)}}" stepKey="goToProductPage1"/> + <waitForPageLoad stepKey="waitForProductPageLoad1"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + userInput="$$getConfigAttributeOption2.label$$" stepKey="selectOption2"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart1"/> + <waitForPageLoad stepKey="waitForAddToCart1"/> + + <!--View and edit cart--> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="storefrontOpenCartFromMinicart"/> + <scrollTo selector="{{DiscountSection.discountTab}}" stepKey="scrollToDiscountTab"/> + <click selector="{{DiscountSection.discountTab}}" stepKey="openDiscountTab" /> + <fillField selector="{{DiscountSection.couponInput}}" userInput="{{SalesRuleWithSkuInActions.coupon_code}}" stepKey="fillCouponCode" /> + <click selector="{{DiscountSection.applyCodeBtn}}" stepKey="applyCode"/> + <waitForPageLoad stepKey="waitForPageLoaded1"/> + <see userInput="You used coupon code" stepKey="assertText"/> + <!--Verify values--> + <grabTextFrom selector="{{CheckoutCartSummarySection.itemDiscount}}" stepKey="getDiscount"/> + <grabTextFrom selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="getSubtotal"/> + <assertEquals stepKey="checkDescount"> + <expectedResult type="string">-$7.00</expectedResult> + <actualResult type="variable">$getDiscount</actualResult> + </assertEquals> + <assertEquals stepKey="checkSubtotal"> + <expectedResult type="string">$20.00</expectedResult> + <actualResult type="variable">$getSubtotal</actualResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php index b217c4bdf8c8b..929cdc1e1ec7e 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -295,8 +295,8 @@ public function testQuoteLocaleFormatPrice($isValid, $conditionValue, $operator ->method('getProduct') ->willReturn($product); - $this->model->setAttribute('quote_item_price') - ->setOperator($operator); + $this->model->setAttribute('quote_item_price'); + $this->model->setData('operator', $operator); $this->assertEquals($isValid, $this->model->setValue($conditionValue)->validate($item)); } From b7f7093f538e4c4ee446b725d4f23a6b8b606d1d Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@adobe.com> Date: Fri, 21 Sep 2018 17:23:34 +0300 Subject: [PATCH 025/240] MAGETWO-69650: Simple product with flat tables enabled not showing correct price in shopping cart --- .../Model/Indexer/Product/Flat/FlatTableBuilder.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php index fbe0d4b550fa6..abe1b3eedbc84 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php @@ -341,6 +341,12 @@ protected function _updateTemporaryTableByStoreValues( if (!empty($changedIds)) { $select->where($this->_connection->quoteInto('et.entity_id IN (?)', $changedIds)); } + + /* + * According to \Magento\Framework\DB\SelectRendererInterface select rendering may be updated + * so we need to trigger select renderer for correct update + */ + $select->assemble(); $sql = $select->crossUpdateFromSelect(['et' => $temporaryFlatTableName]); $this->_connection->query($sql); } @@ -355,6 +361,7 @@ protected function _updateTemporaryTableByStoreValues( if (!empty($changedIds)) { $select->where($this->_connection->quoteInto('et.entity_id IN (?)', $changedIds)); } + $select->assemble(); $sql = $select->crossUpdateFromSelect(['et' => $temporaryFlatTableName]); $this->_connection->query($sql); } From 865da722ce23c79656817aaa87b81ab7dae797f0 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 25 Sep 2018 09:48:04 +0300 Subject: [PATCH 026/240] MAGETWO-94260: PayPal Billing Address for Registered Customers --- app/code/Magento/Paypal/Model/Express/Checkout.php | 2 +- .../testsuite/Magento/Paypal/Model/Express/CheckoutTest.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Express/Checkout.php b/app/code/Magento/Paypal/Model/Express/Checkout.php index a69cbd9daee8a..9d8a82445aa59 100644 --- a/app/code/Magento/Paypal/Model/Express/Checkout.php +++ b/app/code/Magento/Paypal/Model/Express/Checkout.php @@ -371,7 +371,7 @@ public function __construct( * * @return string */ - public function getCheckoutShortcutImageUrl(): string + public function getCheckoutShortcutImageUrl() { // get "pal" thing from cache or lookup it via API $pal = null; diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php index 96a1d3f6e9e62..eb80da6d21b19 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php @@ -490,6 +490,7 @@ private function performQuoteAddressAssertions(Address $address, array $expected * Initialize a checkout model mock. * * @param Quote $quote + * @param string $prefix * @return void */ private function prepareCheckoutModel(Quote $quote, $prefix = '') @@ -533,7 +534,7 @@ private function getExportedData(): array 'city' => 'Denver', 'street' => '66 Pearl St', 'postcode' => '80203', - 'telephone' => '555-555-555' + 'telephone' => '555-555-555', ], 'billing' => [ 'email' => 'customer@example.com', @@ -545,7 +546,7 @@ private function getExportedData(): array 'city' => 'Austin', 'street' => '1100 Congress Ave', 'postcode' => '78701', - 'telephone' => '555-555-555' + 'telephone' => '555-555-555', ] ]; } From 85a63f86b25857eedc7b45da854169b93eb398cd Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 25 Sep 2018 16:27:55 +0300 Subject: [PATCH 027/240] MAGETWO-86344: REST API extension_attributes for configurable products is empty when using search criteria on products --- setup/performance-toolkit/benchmark.jmx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 452664611168d..c48f11f0c2e88 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -1433,7 +1433,7 @@ productList.add(productMap);</stringProp> <hashTree/> </hashTree> </hashTree> - + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Extract configurable products for edit" enabled="true"> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/extract_configurable_products_for_edit.jmx</stringProp> </TestFragmentController> @@ -1642,7 +1642,7 @@ editProductList.add(editProductMap);</stringProp> <hashTree/> </hashTree> </hashTree> - + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Extract simple products for edit" enabled="true"> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/extract_simple_products_for_edit.jmx</stringProp> </TestFragmentController> @@ -1865,7 +1865,7 @@ editProductList.add(editProductMap);</stringProp> <hashTree/> </hashTree> </hashTree> - + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Extract simple products" enabled="true"> <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/extract_simple_products.jmx</stringProp> </TestFragmentController> From e6d31b0baecc57ad8c40daf557a4430fae9450ec Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 25 Sep 2018 17:12:43 +0300 Subject: [PATCH 028/240] MAGETWO-86098: Cart Price Rule for configurable products --- ...dminCategoryProductAttributeActionGroup.xml | 4 ++-- .../Page/AdminProductAttributeGridPage.xml | 1 + .../Section/CheckoutCartSummarySection.xml | 4 ++-- .../SalesRule/Model/Rule/Condition/Product.php | 8 +++++--- .../Model/Rule/Condition/Product/Combine.php | 11 +++++++---- .../Test/Mftf/Page/CheckoutCartPage.xml | 14 ++++++++++++++ .../Section/AdminCartPriceRulesFormSection.xml | 6 +++--- .../Mftf/Section/StorefrontDiscountSection.xml | 2 +- ...CartPriceRuleForConfigurableProductTest.xml | 18 +++++++++--------- 9 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Page/CheckoutCartPage.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml index 7fc35a8dd0f10..5c71342264212 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml @@ -33,9 +33,9 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> <waitForPageLoad stepKey="waitForAttributeGridPageLoad"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> - <fillField selector="{{AdminProductAttributeGridSection.filterByAttributeCode}}" userInput="{{attributeCode}}" stepKey="fillBillToNameFilter"/> + <fillField selector="{{AdminProductAttributeGridSection.filterByAttributeCode}}" userInput="{{attributeCode}}" stepKey="fillAttributeCodeFilter"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminProductAttributeGridSection.attributeCode(attributeCode)}}" stepKey="navigateToAttributeEditPage1" /> + <click selector="{{AdminProductAttributeGridSection.attributeCode(attributeCode)}}" stepKey="navigateToAttributeEditPage" /> <waitForPageLoad stepKey="waitForAttributeEditPageLoad" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml index a5de7453d9c23..3a9bd1647a476 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeGridPage.xml @@ -9,5 +9,6 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> <page name="AdminProductAttributeGridPage" url="catalog/product_attribute" area="admin" module="Catalog"> <section name="AdminProductAttributeGridSection"/> + <section name="AdminDataGridHeaderSection"/> </page> </pages> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index e597e55aaa003..b8b5f75cc838c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -17,7 +17,7 @@ <element name="postcode" type="text" selector="#block-summary input[name='postcode']" timeout="30"/> <element name="estimateShippingAndTax" type="text" selector="#block-shipping-heading" timeout="5"/> <element name="flatRateShippingMethod" type="radio" selector="#s_method_flatrate_flatrate" timeout="30"/> - <element name="itemDiscount" type="text" selector="//tr[@class='totals']//td[@class='amount']/span"/> - <element name="subtotal" type="text" selector="//tr[@class='totals sub']//td[@class='amount']/span"/> + <element name="itemDiscount" type="text" selector="tr[class='totals'] td.amount > span"/> + <element name="subtotal" type="text" selector="tr.totals.sub td.amount > span"/> </section> </sections> diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php index e54823c78684a..c90894febd44b 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php @@ -39,7 +39,7 @@ public function getAttribute(): string { $attribute = $this->getData('attribute'); if (strpos($attribute, '::') !== false) { - list (, $attribute) = explode('::', $attribute); + list(, $attribute) = explode('::', $attribute); } return $attribute; @@ -113,7 +113,7 @@ private function getAttributeScopeElement(): \Magento\Framework\Data\Form\Elemen 'value' => $this->getAttributeScope(), 'no_span' => true, 'class' => 'hidden', - 'data-form-part' => $this->getFormName() + 'data-form-part' => $this->getFormName(), ] ); } @@ -141,7 +141,8 @@ public function setAttribute(string $value) public function loadArray($arr) { parent::loadArray($arr); - $this->setAttributeScope(isset($arr['attribute_scope']) ? $arr['attribute_scope'] : null); + $this->setAttributeScope($arr['attribute_scope'] ?? null); + return $this; } @@ -152,6 +153,7 @@ public function asArray(array $arrAttributes = []) { $out = parent::asArray($arrAttributes); $out['attribute_scope'] = $this->getAttributeScope(); + return $out; } diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php index 975f26c90b382..3c0aff51b92ef 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php @@ -112,6 +112,7 @@ protected function _isValid($entity) return true; } } + return $all ? true : false; } @@ -126,7 +127,7 @@ private function validateEntity($cond, \Magento\Framework\Model\AbstractModel $e { $true = (bool)$this->getValue(); $validated = !$true; - foreach ($this->retrieveValidateEntities($cond->getAttributeScope(), $entity) as $validateEntity) { + foreach ($this->retrieveValidateEntities($entity, $cond->getAttributeScope()) as $validateEntity) { $validated = $cond->validate($validateEntity); if ($validated === $true) { break; @@ -139,12 +140,14 @@ private function validateEntity($cond, \Magento\Framework\Model\AbstractModel $e /** * Retrieve entities for validation by attribute scope * - * @param string $attributeScope * @param \Magento\Framework\Model\AbstractModel $entity + * @param string $attributeScope * @return \Magento\Framework\Model\AbstractModel[] */ - private function retrieveValidateEntities($attributeScope, \Magento\Framework\Model\AbstractModel $entity): array - { + private function retrieveValidateEntities( + \Magento\Framework\Model\AbstractModel $entity, + string $attributeScope + ): array { if ($attributeScope === 'parent') { $validateEntities = [$entity]; } elseif ($attributeScope === 'children') { diff --git a/app/code/Magento/SalesRule/Test/Mftf/Page/CheckoutCartPage.xml b/app/code/Magento/SalesRule/Test/Mftf/Page/CheckoutCartPage.xml new file mode 100644 index 0000000000000..83ec6095d57ee --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Page/CheckoutCartPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="CheckoutCartPage" url="/checkout/cart" area="storefront" module="Magento_Checkout"> + <section name="StorefrontDiscountSection"/> + </page> +</pages> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index 95fafa37c4042..73efbe122adb6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -27,8 +27,8 @@ <element name="discountAmount" type="input" selector="input[name='discount_amount']"/> <element name="conditions" type="button" selector=".rule-param.rule-param-new-child > a"/> <element name="condition" type="text" selector="//span[@class='rule-param']/a[text()='{{arg}}']" parameterized="true"/> - <element name="operator" type="select" selector="//select[contains(@name, '[operator]')]"/> - <element name="childAttribute" type="select" selector="//select[contains(@name, 'new_child')]"/> - <element name="optionInput" type="input" selector="//ul[contains(@class, 'rule-param-children')]//input[contains(@name, '[value]')]"/> + <element name="operator" type="select" selector="select[name*='[operator]']"/> + <element name="childAttribute" type="select" selector="select[name*='new_child']"/> + <element name="optionInput" type="input" selector="ul[class*='rule-param-children'] input[name*='[value]']"/> </section> </sections> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml index d438c4d3a7f2f..2ae50489b6d12 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="DiscountSection"> + <section name="StorefrontDiscountSection"> <element name="discountTab" type="button" selector="#block-discount"/> <element name="couponInput" type="input" selector="#coupon_code"/> <element name="applyCodeBtn" type="button" selector="//span[text()='Apply Discount']"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml index 8522e2efc5d7b..ebd912a865fbb 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -16,7 +16,7 @@ <description value="Checking Cart Price Rule for configurable products"/> <severity value="BLOCKER"/> <testCaseId value="MAGETWO-95121"/> - <group value="SalesRule"/> + <group value="sales_rule"/> </annotations> <before> @@ -79,7 +79,7 @@ <argument name="attributeCode" value="sku"/> </actionGroup> <click selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> - <scrollTo selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="ScrollToUseForPromoRuleConditions"/> + <scrollTo selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="scrollToUseForPromoRuleConditions"/> <selectOption selector="{{AdminEditAttributeStorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="Yes" stepKey="changeAttributeProperty"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="saveAttribute"/> <waitForPageLoad stepKey="waitForAttributeIsSaved"/> @@ -95,8 +95,8 @@ <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{SalesRuleWithSkuInActions.coupon_code}}" stepKey="fillCouponCOde"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="{{SalesRuleWithSkuInActions.discount_amount}}" stepKey="fillDiscountAmount"/> - <scrollTo selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="ScrollToApplyRuleForConditions"/> - <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="AddConditionForRule"/> + <scrollTo selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="scrollToApplyRuleForConditions"/> + <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="addConditionForRule"/> <waitForPageLoad stepKey="waitForDropDownOpened"/> <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="SKU(Children Only)" stepKey="selectAttribute"/> <waitForPageLoad stepKey="waitForOperatorOpened"/> @@ -121,7 +121,7 @@ <argument name="attributeCode" value="sku"/> </actionGroup> <click selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab1"/> - <scrollTo selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="ScrollToUseForPromoRuleConditions"/> + <scrollTo selector="{{AdminEditAttributeStorefrontPropertiesSection.storeFrontPropertiesTab}}" stepKey="scrollToUseForPromoRuleConditions"/> <selectOption selector="{{AdminEditAttributeStorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="No" stepKey="changeAttributeProperty"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="saveAttribute"/> <waitForPageLoad stepKey="waitForAttributeIsSaved"/> @@ -153,10 +153,10 @@ <!--View and edit cart--> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="storefrontOpenCartFromMinicart"/> - <scrollTo selector="{{DiscountSection.discountTab}}" stepKey="scrollToDiscountTab"/> - <click selector="{{DiscountSection.discountTab}}" stepKey="openDiscountTab" /> - <fillField selector="{{DiscountSection.couponInput}}" userInput="{{SalesRuleWithSkuInActions.coupon_code}}" stepKey="fillCouponCode" /> - <click selector="{{DiscountSection.applyCodeBtn}}" stepKey="applyCode"/> + <scrollTo selector="{{StorefrontDiscountSection.discountTab}}" stepKey="scrollToDiscountTab"/> + <click selector="{{StorefrontDiscountSection.discountTab}}" stepKey="openDiscountTab" /> + <fillField selector="{{StorefrontDiscountSection.couponInput}}" userInput="{{SalesRuleWithSkuInActions.coupon_code}}" stepKey="fillCouponCode" /> + <click selector="{{StorefrontDiscountSection.applyCodeBtn}}" stepKey="applyCode"/> <waitForPageLoad stepKey="waitForPageLoaded1"/> <see userInput="You used coupon code" stepKey="assertText"/> <!--Verify values--> From 6ac15984817c1292da8acf477356365f9fad2eca Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 26 Sep 2018 08:42:13 +0300 Subject: [PATCH 029/240] MAGETWO-86098: Cart Price Rule for configurable products --- .../SalesRule/Model/Rule/Condition/Product/Combine.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php index 3c0aff51b92ef..c539bb6499df9 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php @@ -141,12 +141,12 @@ private function validateEntity($cond, \Magento\Framework\Model\AbstractModel $e * Retrieve entities for validation by attribute scope * * @param \Magento\Framework\Model\AbstractModel $entity - * @param string $attributeScope + * @param string|null $attributeScope * @return \Magento\Framework\Model\AbstractModel[] */ private function retrieveValidateEntities( \Magento\Framework\Model\AbstractModel $entity, - string $attributeScope + $attributeScope ): array { if ($attributeScope === 'parent') { $validateEntities = [$entity]; From a992ef372ac1d4b21b49af90752af70d76e5eb0f Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 26 Sep 2018 10:30:13 +0300 Subject: [PATCH 030/240] MAGETWO-86098: Cart Price Rule for configurable products --- .../Test/StorefrontEditBundleProductTest.xml | 8 +-- .../Test/AdminApplyTierPriceToProductTest.xml | 4 +- .../Test/Mftf/Page/CheckoutCartPage.xml | 2 +- ... StorefrontCheckoutCartSummarySection.xml} | 2 +- .../Model/Rule/Condition/Product/Combine.php | 4 +- ...artPriceRuleForConfigurableProductTest.xml | 4 +- ... StorefrontCheckoutCartSummarySection.xml} | 2 +- ...oppingCartForCustomerPhysicalQuoteTest.xml | 50 +++++++++---------- ...hoppingCartForCustomerVirtualQuoteTest.xml | 38 +++++++------- ...nShoppingCartForGuestPhysicalQuoteTest.xml | 44 ++++++++-------- ...InShoppingCartForGuestVirtualQuoteTest.xml | 32 ++++++------ ... StorefrontCheckoutCartSummarySection.xml} | 2 +- 12 files changed, 96 insertions(+), 96 deletions(-) rename app/code/Magento/Checkout/Test/Mftf/Section/{CheckoutCartSummarySection.xml => StorefrontCheckoutCartSummarySection.xml} (96%) rename app/code/Magento/Tax/Test/Mftf/Section/{CheckoutCartSummarySection.xml => StorefrontCheckoutCartSummarySection.xml} (92%) rename app/code/Magento/Weee/Test/Mftf/Section/{CheckoutCartSummarySection.xml => StorefrontCheckoutCartSummarySection.xml} (90%) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index f7d41d3d2907c..b18d857def40b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -90,9 +90,9 @@ <!-- Go to the shopping cart page and edit the first product --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart"/> <waitForPageLoad stepKey="waitForCartPageLoad"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.total}}" stepKey="waitForInfoDropdown"/> + <waitForElementVisible selector="{{StorefrontCheckoutCartSummarySection.total}}" stepKey="waitForInfoDropdown"/> <waitForPageLoad stepKey="waitForCartPageLoad3"/> - <grabTextFrom selector="{{CheckoutCartSummarySection.total}}" stepKey="grabTotalBefore"/> + <grabTextFrom selector="{{StorefrontCheckoutCartSummarySection.total}}" stepKey="grabTotalBefore"/> <click selector="{{CheckoutCartProductSection.editItemParametersButton('1')}}" stepKey="clickEdit"/> <waitForPageLoad stepKey="waitForStorefront2"/> @@ -113,9 +113,9 @@ <!-- Assert that the options are both there and the proce no longer matches --> <see selector="{{CheckoutCartProductSection.itemOptionsBlock('2')}}" userInput="$$simpleProduct1.sku$$" stepKey="assertBothOptions"/> <see selector="{{CheckoutCartProductSection.itemOptionsBlock('2')}}" userInput="$$simpleProduct2.sku$$" stepKey="assertBothOptions2"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.total}}" stepKey="waitForInfoDropdown2"/> + <waitForElementVisible selector="{{StorefrontCheckoutCartSummarySection.total}}" stepKey="waitForInfoDropdown2"/> <waitForPageLoad stepKey="waitForCartPageLoad4"/> - <grabTextFrom selector="{{CheckoutCartSummarySection.total}}" stepKey="grabTotalAfter"/> + <grabTextFrom selector="{{StorefrontCheckoutCartSummarySection.total}}" stepKey="grabTotalAfter"/> <assertNotEquals expected="{$grabTotalBefore}" expectedType="string" actual="{$grabTotalAfter}" actualType="string" stepKey="assertNotEquals"/> <!-- Delete the bundled product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml index aa3e4abacdfcc..5742f44e1fedf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml @@ -179,7 +179,7 @@ <expectedResult type="string">$1,500.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField4</actualResult> </assertEquals> - <grabTextFrom selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="grabTextFromCheckoutCartSummarySectionSubtotal1"/> + <grabTextFrom selector="{{StorefrontCheckoutCartSummarySection.subtotal}}" stepKey="grabTextFromCheckoutCartSummarySectionSubtotal1"/> <assertEquals message="Shopping cart summary section should contain subtotal $1,500" stepKey="assertSubtotalFieldFromCheckoutCartSummarySection1"> <expectedResult type="string">$1,500.00</expectedResult> <actualResult type="variable">grabTextFromCheckoutCartSummarySectionSubtotal1</actualResult> @@ -256,7 +256,7 @@ <expectedResult type="string">$4,000.00</expectedResult> <actualResult type="variable">grabTextFromSubtotalField7</actualResult> </assertEquals> - <grabTextFrom selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="grabTextFromCheckoutCartSummarySectionSubtotal2"/> + <grabTextFrom selector="{{StorefrontCheckoutCartSummarySection.subtotal}}" stepKey="grabTextFromCheckoutCartSummarySectionSubtotal2"/> <assertEquals message="Shopping cart summary section should contain subtotal $4,000" stepKey="assertSubtotalFieldFromCheckoutCartSummarySection2"> <expectedResult type="string">$4,000.00</expectedResult> <actualResult type="variable">grabTextFromCheckoutCartSummarySectionSubtotal2</actualResult> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml index 8c896f2cd09de..37079e2bcf144 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> <page name="CheckoutCartPage" url="/checkout/cart" area="storefront" module="Magento_Checkout"> <section name="CheckoutCartProductSection"/> - <section name="CheckoutCartSummarySection"/> + <section name="StorefrontCheckoutCartSummarySection"/> </page> </pages> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml similarity index 96% rename from app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml rename to app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml index b8b5f75cc838c..c68f1dcee3992 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="CheckoutCartSummarySection"> + <section name="StorefrontCheckoutCartSummarySection"> <element name="subtotal" type="text" selector="#cart-totals tr.totals.sub span.price"/> <element name="proceedToCheckout" type="button" selector=".action.primary.checkout span" timeout="30"/> <element name="total" type="text" selector=".amount[data-th='Order Total'] span"/> diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php index c539bb6499df9..b77298bd9a21d 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php @@ -102,7 +102,7 @@ protected function _isValid($entity) foreach ($this->getConditions() as $cond) { if ($entity instanceof \Magento\Framework\Model\AbstractModel) { - $validated = $this->validateEntity($cond, $entity); + $validated = $this->validateEntity($entity, $cond); } else { $validated = $cond->validateByEntityId($entity); } @@ -123,7 +123,7 @@ protected function _isValid($entity) * @param \Magento\Framework\Model\AbstractModel $entity * @return bool */ - private function validateEntity($cond, \Magento\Framework\Model\AbstractModel $entity): bool + private function validateEntity(\Magento\Framework\Model\AbstractModel $entity, $cond): bool { $true = (bool)$this->getValue(); $validated = !$true; diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml index ebd912a865fbb..f7010822f5f43 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -160,8 +160,8 @@ <waitForPageLoad stepKey="waitForPageLoaded1"/> <see userInput="You used coupon code" stepKey="assertText"/> <!--Verify values--> - <grabTextFrom selector="{{CheckoutCartSummarySection.itemDiscount}}" stepKey="getDiscount"/> - <grabTextFrom selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="getSubtotal"/> + <grabTextFrom selector="{{StorefrontCheckoutCartSummarySection.itemDiscount}}" stepKey="getDiscount"/> + <grabTextFrom selector="{{StorefrontCheckoutCartSummarySection.subtotal}}" stepKey="getSubtotal"/> <assertEquals stepKey="checkDescount"> <expectedResult type="string">-$7.00</expectedResult> <actualResult type="variable">$getDiscount</actualResult> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Tax/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml similarity index 92% rename from app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml rename to app/code/Magento/Tax/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml index e01afa52dc09a..81bdd4103e83a 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="CheckoutCartSummarySection"> + <section name="StorefrontCheckoutCartSummarySection"> <element name="taxAmount" type="text" selector="[data-th='Tax']>span"/> <element name="taxSummary" type="text" selector=".totals-tax-summary" timeout="5"/> <element name="rate" type="text" selector=" tr.totals-tax-details.shown th.mark"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml index 1a92e6feaf705..b9cef7dac654b 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml @@ -86,34 +86,34 @@ <!-- Step 3: Go to Shopping Cart --> <actionGroup ref="StorefrontViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <see selector="{{CheckoutCartSummarySection.country}}" userInput="$$createCustomer.country_id$$" stepKey="checkCustomerCountry" /> - <see selector="{{CheckoutCartSummarySection.region}}" userInput="$$createCustomer.state$$" stepKey="checkCustomerRegion" /> - <see selector="{{CheckoutCartSummarySection.postcode}}" userInput="$$createCustomer.postcode$$" stepKey="checkCustomerPostcode" /> - <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkAmountFPTCA" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> + <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <see selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="$$createCustomer.country_id$$" stepKey="checkCustomerCountry" /> + <see selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="$$createCustomer.state$$" stepKey="checkCustomerRegion" /> + <see selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="$$createCustomer.postcode$$" stepKey="checkCustomerPostcode" /> + <see selector="{{StorefrontCheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkAmountFPTCA" /> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> + <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> <!-- Step 5: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.region}}" userInput="Aargau" stepKey="selectAargauRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="Aargau" stepKey="selectAargauRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> <!-- Step 6: Select shipping rate again(it need for get new totals request - performance reason) --> - <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> - <dontSeeElement selector="{{CheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> + <click selector="{{StorefrontCheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod"/> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> + <dontSeeElement selector="{{StorefrontCheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> <!-- Step 7: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.region}}" userInput="New York" stepKey="selectNewYorkRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="New York" stepKey="selectNewYorkRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> <!-- Step 8: Select shipping rate again(it need for get new totals request - performance reason) --> - <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod2"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary3"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.84" stepKey="checkTaxAmountNY" /> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> - <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$20" stepKey="checkAmountFPTNY" /> + <click selector="{{StorefrontCheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod2"/> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> + <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary3"/> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.84" stepKey="checkTaxAmountNY" /> + <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> + <see selector="{{StorefrontCheckoutCartSummarySection.amountFPT}}" userInput="$20" stepKey="checkAmountFPTNY" /> </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml index afe775fc372bc..52ee2ed3915df 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml @@ -58,26 +58,26 @@ <!-- Step 3: Go to Shopping Cart --> <actionGroup ref="StorefrontViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <see selector="{{CheckoutCartSummarySection.country}}" userInput="$$createCustomer.country_id$$" stepKey="checkCustomerCountry" /> - <see selector="{{CheckoutCartSummarySection.region}}" userInput="$$createCustomer.state$$" stepKey="checkCustomerRegion" /> - <see selector="{{CheckoutCartSummarySection.postcode}}" userInput="$$createCustomer.postcode$$" stepKey="checkCustomerPostcode" /> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="expandTaxSummary"/> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> + <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <see selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="$$createCustomer.country_id$$" stepKey="checkCustomerCountry" /> + <see selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="$$createCustomer.state$$" stepKey="checkCustomerRegion" /> + <see selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="$$createCustomer.postcode$$" stepKey="checkCustomerPostcode" /> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="expandTaxSummary"/> + <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> <!-- Step 5: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.region}}" userInput="Aargau" stepKey="selectAargauRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="Aargau" stepKey="selectAargauRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> <!-- Step 6: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.region}}" userInput="California" stepKey="selectCaliforniaRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="90230" stepKey="inputPostCode2"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.30" stepKey="checkTaxAmount2" /> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="90230" stepKey="inputPostCode2"/> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> + <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$3.30" stepKey="checkTaxAmount2" /> + <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml index 893c740321314..fe900e0fd03ff 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml @@ -82,31 +82,31 @@ <!-- Step 3: Go to Shopping Cart --> <actionGroup ref="StorefrontViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkAmountFPTCA" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> + <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <see selector="{{StorefrontCheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkAmountFPTCA" /> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> + <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> <!-- Step 5: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.region}}" userInput="Aargau" stepKey="selectAargauRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="Aargau" stepKey="selectAargauRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> <!-- Step 6: Select shipping rate again(it need for get new totals request - performance reason) --> - <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> - <dontSeeElement selector="{{CheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> + <click selector="{{StorefrontCheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod"/> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" stepKey="scrollToTaxSummary2" /> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> + <dontSeeElement selector="{{StorefrontCheckoutCartSummarySection.amountFPT}}" stepKey="checkFPTIsNotDisplayed" /> <!-- Step 7: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.region}}" userInput="New York" stepKey="selectNewYorkRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="New York" stepKey="selectNewYorkRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> <!-- Step 8: Select shipping rate again(it need for get new totals request - performance reason) --> - <click selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod2"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.84" stepKey="checkTaxAmountNY" /> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> - <see selector="{{CheckoutCartSummarySection.amountFPT}}" userInput="$20" stepKey="checkAmountFPTNY" /> + <click selector="{{StorefrontCheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectflatRateShippingMethodShippingMethod2"/> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary3" /> + <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.84" stepKey="checkTaxAmountNY" /> + <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> + <see selector="{{StorefrontCheckoutCartSummarySection.amountFPT}}" userInput="$20" stepKey="checkAmountFPTNY" /> </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml index 37c1083722a25..cd051b2dcd457 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml @@ -54,23 +54,23 @@ <!-- Step 3: Go to Shopping Cart --> <actionGroup ref="StorefrontViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> - <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.30" stepKey="checkTaxAmountCA" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> + <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$3.30" stepKey="checkTaxAmountCA" /> + <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary"/> + <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-CA-*-Rate 1 (8.25%)" stepKey="checkRateCA" /> <!-- Step 5: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.region}}" userInput="Aargau" stepKey="selectAargauRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="Switzerland" stepKey="selectSwitzerlandCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="Aargau" stepKey="selectAargauRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="1234" stepKey="inputPostCode"/> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.00" stepKey="checkTaxAmount" /> <!-- Step 6: Change Data --> - <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> - <selectOption selector="{{CheckoutCartSummarySection.region}}" userInput="New York" stepKey="selectNewYorkRegion"/> - <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> - <scrollTo selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary2" /> - <click selector="{{CheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> - <see selector="{{CheckoutCartSummarySection.taxAmount}}" userInput="$3.35" stepKey="checkTaxAmountNY" /> - <see selector="{{CheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUnitedStatesCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="New York" stepKey="selectNewYorkRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="12345" stepKey="inputPostCode2"/> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary2" /> + <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="taxSummary2"/> + <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$3.35" stepKey="checkTaxAmountNY" /> + <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> </test> </tests> diff --git a/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Weee/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml similarity index 90% rename from app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml rename to app/code/Magento/Weee/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml index 9b6541b93541d..45277a7956f3d 100644 --- a/app/code/Magento/Weee/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Weee/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="CheckoutCartSummarySection"> + <section name="StorefrontCheckoutCartSummarySection"> <element name="amountFPT" type="text" selector=".totals td[data-th='FPT'] .price"/> </section> </sections> From 913ca5e3b226b193fc972ee26540a794d541806f Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 27 Sep 2018 09:39:52 +0300 Subject: [PATCH 031/240] MAGETWO-73862: Image Swatch size change not working [GitHub #6382] --- .../view/frontend/templates/product/listing/renderer.phtml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml index f8c55d6426982..f19da436a60c4 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml @@ -21,7 +21,8 @@ "numberToShow": <?= /* @escapeNotVerified */ $block->getNumberSwatchesPerProduct(); ?>, "jsonConfig": <?= /* @escapeNotVerified */ $block->getJsonConfig(); ?>, "jsonSwatchConfig": <?= /* @escapeNotVerified */ $block->getJsonSwatchConfig(); ?>, - "mediaCallback": "<?= /* @escapeNotVerified */ $block->getMediaCallback() ?>" + "mediaCallback": "<?= /* @escapeNotVerified */ $block->getMediaCallback() ?>", + "jsonSwatchImageSizeConfig": <?= /* @noEscape */ $block->getJsonSwatchSizeConfig() ?> } } } From 6f471e5cb28c0e3a4e9e61566e0e8eb7272fa3cd Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Thu, 27 Sep 2018 10:40:41 +0300 Subject: [PATCH 032/240] MAGETWO-86098: Cart Price Rule for configurable products --- .../Magento/SalesRule/Model/Rule/Condition/Product/Combine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php index b77298bd9a21d..2277240eb8aad 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php @@ -119,8 +119,8 @@ protected function _isValid($entity) /** * Validate entity. * - * @param mixed $cond * @param \Magento\Framework\Model\AbstractModel $entity + * @param mixed $cond * @return bool */ private function validateEntity(\Magento\Framework\Model\AbstractModel $entity, $cond): bool From 919f2292a0b1e7c99572b8f37634ff0f1f9d71b7 Mon Sep 17 00:00:00 2001 From: Stas Puga <stas.puga@transoftgroup.com> Date: Thu, 27 Sep 2018 12:54:47 +0300 Subject: [PATCH 033/240] MAGETWO-95172: Filtering Category Products using scope selector --- .../ActionGroup/AdminProductActionGroup.xml | 11 ++ .../Test/Mftf/Page/AdminCategoryPage.xml | 1 + .../AdminCategoryProductsGridSection.xml | 14 ++ ...CategoryProductsUsingScopeSelectorTest.xml | 164 ++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index ee7e3c82564b7..b3df9cb3b0378 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -90,4 +90,15 @@ <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> </actionGroup> + + <!--Select Product In Websites--> + <actionGroup name="SelectProductInWebsitesActionGroup"> + <arguments> + <argument name="website" type="string"/> + </arguments> + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="clickToOpenProductInWebsite"/> + <waitForPageLoad stepKey="waitForPageOpened"/> + <checkOption selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml index a11d05140c638..45485509f5e3e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml @@ -14,5 +14,6 @@ <section name="AdminCategoryBasicFieldSection"/> <section name="AdminCategorySEOSection"/> <section name="AdminCategoryModalSection"/> + <section name="AdminCategoryProductsGridSection"/> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.xml new file mode 100644 index 0000000000000..61a4c497d5b3a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsGridSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminCategoryProductsGridSection"> + <element name="productGridNameProduct" type="text" selector="//table[@id='catalog_category_products_table']//td[contains(., '{{productName}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml new file mode 100644 index 0000000000000..e87313bed6332 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminFilteringCategoryProductsUsingScopeSelectorTest"> + <annotations> + <features value="Catalog"/> + <title value="Filtering Category Products using scope selector"/> + <description value="Filtering Category Products using scope selector"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-78408"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create website, Sore adn Store View--> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="adminCreateWebsite"> + <argument name="newWebsiteName" value="secondWebsite"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="adminCreateStore"> + <argument name="website" value="secondWebsite"/> + <argument name="storeGroupName" value="Second Store"/> + <argument name="storeGroupCode" value="second_store"/> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="adminCreateStoreView"> + <argument name="storeGroup" value="secondStoreGroup"/> + <argument name="customStore" value="secondStore"/> + </actionGroup> + + <!--Create Simple Product and Category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct0"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct12"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Set filter to product name and product0 not assigned to any website--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="filterGroupedProductOptions"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + + <click selector="{{AdminProductGridSection.productGridNameProduct('$$createProduct0.name$$')}}" + stepKey="clickOpenProductForEdit"/> + <waitForPageLoad time="30" stepKey="waitForProductEditOpen"/> + + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsitesSection"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="clickToOpenWebsiteSection"/> + <waitForPageLoad stepKey="waitForToOpenedWebsiteSection"/> + <uncheckOption selector="{{ProductInWebsitesSection.website('Main Website')}}" stepKey="uncheckWebsite"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." + stepKey="seeSuccessMessage"/> + + <!-- Set filter to product name and product2 in website 2 only --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad time="30" stepKey="waitForProductsPageToLoad"/> + <click selector="{{AdminProductGridSection.productGridNameProduct('$$createProduct2.name$$')}}" + stepKey="clickOpenProductForEdit1"/> + <waitForPageLoad time="30" stepKey="waitForProductEditOpen1"/> + + <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectProductInWebsites"> + <argument name="website" value="secondWebsite"/> + </actionGroup> + <uncheckOption selector="{{ProductInWebsitesSection.website('Main Website')}}" stepKey="uncheckWebsite1"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct1"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." + stepKey="seeSuccessMessage1"/> + + <!-- Set filter to product name and product12 assigned to both websites 1 and 2 --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex1"/> + <waitForPageLoad time="30" stepKey="waitForProductsPageToLoad1"/> + <click selector="{{AdminProductGridSection.productGridNameProduct('$$createProduct12.name$$')}}" + stepKey="clickOpenProductForEdit2"/> + <waitForPageLoad time="30" stepKey="waitForProductEditOpen2"/> + + <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectProductInWebsites1"> + <argument name="website" value="secondWebsite"/> + </actionGroup> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct2"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." + stepKey="seeSuccessMessage2"/> + </before> + <after> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="secondWebsite"/> + </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductFilters"/> + <deleteData createDataKey="createProduct0" stepKey="deleteProduct"/> + <deleteData createDataKey="createProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="createProduct12" stepKey="deleteProduct3"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Step 1-2: Open Category page and Set scope selector to All Store Views--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" + stepKey="clickCategoryName"/> + <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection"/> + <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" + userInput="$$createProduct0.name$$" stepKey="seeProductName"/> + <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct1.name$$)}}" + userInput="$$createProduct1.name$$" stepKey="seeProductName1"/> + <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" + userInput="$$createProduct2.name$$" stepKey="seeProductName2"/> + <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" + userInput="$$createProduct12.name$$" stepKey="seeProductName3"/> + + <!-- Step 3: Set scope selector to Website1( Storeview for the Website 1) --> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewDropdownToggle}}" + stepKey="clickStoresList"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad1"/> + <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewOption('Default Store View')}}" + stepKey="clickStoreView"/> + <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewModalAccept}}" stepKey="clickActionAccept"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad2"/> + <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection1"/> + <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct1.name$$)}}" + userInput="$$createProduct1.name$$" stepKey="seeProductName4"/> + <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" + userInput="$$createProduct12.name$$" stepKey="seeProductName5"/> + <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" + userInput="$$createProduct0.name$$" stepKey="dontSeeProductName"/> + <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" + userInput="$$createProduct2.name$$" stepKey="dontSeeProductName1"/> + + <!-- Step 4: Set scope selector to Website2 ( StopreView for Website 2) --> + <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> + <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewDropdownToggle}}" + stepKey="clickStoresList1"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad3"/> + <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewOption(secondStore.name)}}" + stepKey="clickStoreView1"/> + <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewModalAccept}}" + stepKey="clickActionAccept1"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad4"/> + <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection2"/> + <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" + userInput="$$createProduct2.name$$" stepKey="seeProductName6"/> + <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" + userInput="$$createProduct12.name$$" stepKey="seeProductName7"/> + <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" + userInput="$$createProduct0.name$$" stepKey="dontSeeProductName2"/> + <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" + userInput="$$createProduct1.name$$" stepKey="dontSeeProductName3"/> + </test> +</tests> From e17e683884ac19ff464472d714206daeff5774a3 Mon Sep 17 00:00:00 2001 From: Stjepan Udovicic <stjepan.udovicic@inchoo.net> Date: Thu, 27 Sep 2018 14:47:19 +0200 Subject: [PATCH 034/240] Ensure integer values are not quoted as strings --- .../ConditionProcessor/ProductCategoryCondition.php | 2 +- .../Magento/Catalog/Model/ProductCategoryList.php | 2 +- .../Model/ResourceModel/Product/Collection.php | 11 ++++++----- lib/internal/Magento/Framework/Mview/View.php | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php index 09c3a3864bbc7..b11696ed0d129 100644 --- a/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php +++ b/app/code/Magento/Catalog/Model/Api/SearchCriteria/CollectionProcessor/ConditionProcessor/ProductCategoryCondition.php @@ -102,7 +102,7 @@ private function getCategoryIds(Filter $filter): array } } - return array_unique(array_merge($categoryIds, ...$childCategoryIds)); + return array_map('intval', array_unique(array_merge($categoryIds, ...$childCategoryIds))); } /** diff --git a/app/code/Magento/Catalog/Model/ProductCategoryList.php b/app/code/Magento/Catalog/Model/ProductCategoryList.php index 68d2c0606e567..92c78c67a341c 100644 --- a/app/code/Magento/Catalog/Model/ProductCategoryList.php +++ b/app/code/Magento/Catalog/Model/ProductCategoryList.php @@ -81,7 +81,7 @@ public function getCategoryIds($productId) Select::SQL_UNION_ALL ); - $this->categoryIdList[$productId] = $this->productResource->getConnection()->fetchCol($unionSelect); + $this->categoryIdList[$productId] = array_map('intval', $this->productResource->getConnection()->fetchCol($unionSelect)); } return $this->categoryIdList[$productId]; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index b131f78ad3b5c..83a8d36850573 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1792,7 +1792,8 @@ protected function _productLimitationJoinWebsite() } $conditions[] = $this->getConnection()->quoteInto( 'product_website.website_id IN(?)', - $filters['website_ids'] + $filters['website_ids'], + 'int' ); } elseif (isset( $filters['store_id'] @@ -1804,7 +1805,7 @@ protected function _productLimitationJoinWebsite() ) { $joinWebsite = true; $websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId(); - $conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId); + $conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId, 'int'); } $fromPart = $this->getSelect()->getPart(\Magento\Framework\DB\Select::FROM); @@ -2000,12 +2001,12 @@ protected function _applyProductLimitations() $conditions = [ 'cat_index.product_id=e.entity_id', - $this->getConnection()->quoteInto('cat_index.store_id=?', $filters['store_id']), + $this->getConnection()->quoteInto('cat_index.store_id=?', $filters['store_id'], 'int'), ]; if (isset($filters['visibility']) && !isset($filters['store_table'])) { - $conditions[] = $this->getConnection()->quoteInto('cat_index.visibility IN(?)', $filters['visibility']); + $conditions[] = $this->getConnection()->quoteInto('cat_index.visibility IN(?)', $filters['visibility'], 'int'); } - $conditions[] = $this->getConnection()->quoteInto('cat_index.category_id=?', $filters['category_id']); + $conditions[] = $this->getConnection()->quoteInto('cat_index.category_id=?', $filters['category_id'], 'int'); if (isset($filters['category_is_anchor'])) { $conditions[] = $this->getConnection()->quoteInto('cat_index.is_parent=?', $filters['category_is_anchor']); } diff --git a/lib/internal/Magento/Framework/Mview/View.php b/lib/internal/Magento/Framework/Mview/View.php index 1f1bde6bb021c..fca827ca2505f 100644 --- a/lib/internal/Magento/Framework/Mview/View.php +++ b/lib/internal/Magento/Framework/Mview/View.php @@ -285,7 +285,7 @@ public function update() for ($versionFrom = $lastVersionId; $versionFrom < $currentVersionId; $versionFrom += $versionBatchSize) { // Don't go past the current version for atomicy. $versionTo = min($currentVersionId, $versionFrom + $versionBatchSize); - $ids = $this->getChangelog()->getList($versionFrom, $versionTo); + $ids = array_map('intval', $this->getChangelog()->getList($versionFrom, $versionTo)); // We run the actual indexer in batches. Chunked AFTER loading to avoid duplicates in separate chunks. $chunks = array_chunk($ids, $batchSize); From 770b173d7df96bd976d4a38a006d0da1897feb46 Mon Sep 17 00:00:00 2001 From: Stas Puga <stas.puga@transoftgroup.com> Date: Thu, 27 Sep 2018 16:12:01 +0300 Subject: [PATCH 035/240] MAGETWO-95172: Filtering Category Products using scope selector --- .../AdminFilteringCategoryProductsUsingScopeSelectorTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index e87313bed6332..3f69b5cf70fac 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="AdminFilteringCategoryProductsUsingScopeSelectorTest"> <annotations> <features value="Catalog"/> From 43e2b0364a5eb851704be4967b6a48b81c41204e Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Thu, 27 Sep 2018 16:30:10 +0300 Subject: [PATCH 036/240] MAGETWO-95170: Add Bundle product with zero price to shopping cart --- .../StorefrontProductCartActionGroup.xml | 26 +++++++ ...ProductWithZeroPriceToShoppingCartTest.xml | 77 +++++++++++++++++++ .../Mftf/ActionGroup/CheckoutActionGroup.xml | 7 ++ ...ckoutFillingShippingSectionActionGroup.xml | 10 ++- .../ActionGroup/AdminOrderGridActionGroup.xml | 23 ++++++ .../Mftf/Section/AdminOrdersGridSection.xml | 4 + 6 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml new file mode 100644 index 0000000000000..fda5d10295676 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <!-- Add Bundle Product to Cart from the category page with specified quantity to cart --> + <actionGroup name="StorefrontAddCategoryBundleProductToCartActionGroup"> + <arguments> + <argument name="product"/> + <argument name="quantity" defaultValue="1" type="string"/> + </arguments> + <moveMouseOver selector="{{StorefrontCategoryProductSection.productTitleByName(product.name)}}" stepKey="moveMouseOverProduct"/> + <click selector="{{StorefrontCategoryProductSection.productTitleByName(product.name)}}" stepKey="openProductPage"/> + <waitForPageLoad time="30" stepKey="waitForBundleProductPageLoad"/> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomizeAndAddToCart"/> + <fillField selector="{{StorefrontProductInfoMainSection.qty}}" userInput="{{quantity}}" stepKey="fillBundleProductQuantity"/> + <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddBundleProductToCart"/> + <waitForText userInput="{{quantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <see userInput="You added {{product.name}} to your shopping cart." selector="{{StorefrontMessagesSection.success}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml new file mode 100644 index 0000000000000..112b804382ea2 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontAddBundleProductWithZeroPriceToShoppingCartTest"> + <annotations> + <features value="Bundle"/> + <stories value="Add Bundle product with zero price to shopping cart"/> + <title value="Add Bundle product with zero price to shopping cart"/> + <description value="Add Bundle product with zero price to shopping cart"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-83535"/> + <group value="bundle"/> + </annotations> + <before> + <!--Enable freeShipping--> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> + <!--Create category--> + <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> + <!--Create simple with zero price product--> + <createData entity="ApiProductWithDescription" stepKey="apiSimple"> + <field key="price">0</field> + </createData> + <!--Create Bundle product--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="apiBundleProduct"> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <!--Create Attribute--> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="apiBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <requiredEntity createDataKey="apiBundleProduct"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="apiSimple"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> + <deleteData createDataKey="apiSimple" stepKey="deleteSimple"/> + <deleteData createDataKey="apiBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="createSubCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Open category page--> + <amOnPage url="{{StorefrontCategoryPage.url($$createSubCategory.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <!--Add bundle product to cart--> + <actionGroup ref="StorefrontAddCategoryBundleProductToCartActionGroup" stepKey="addBundleProductToCart"> + <argument name="product" value="$$apiBundleProduct$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + + <!--Place order--> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShipping"> + <argument name="shippingMethod" value="Free Shipping"/> + </actionGroup> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="checkoutPlaceOrder"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + + <!--Check subtotal in created order--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderById"> + <argument name="orderId" value="$grabOrderNumber"/> + </actionGroup> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> + <see selector="{{AdminOrderTotalSection.subTotal}}" userInput="$0.00" stepKey="checkSubtotal"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index 21f3c675eba87..d1d5947b469cc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -60,4 +60,11 @@ <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{StorefrontCheckoutCartSummarySection.country}}" visible="false" stepKey="openShippingDetails"/> <see selector="{{StorefrontCheckoutCartSummarySection.countryParameterized('placeNumber')}}" userInput="{{country}}" stepKey="seeCountry"/> </actionGroup> + + <!--Click Place Order button--> + <actionGroup name="ClickPlaceOrderActionGroup"> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml index 9e66e742229bd..6b76445ab1bb4 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml @@ -11,8 +11,10 @@ <!-- Guest checkout filling shipping section --> <actionGroup name="GuestCheckoutFillingShippingSectionActionGroup"> <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> + <argument name="customerVar" defaultValue="CustomerEntityOne"/> + <argument name="customerAddressVar" defaultValue="CustomerAddressSimple"/> + <!--First available shipping method will be selected if value is not passed for shippingMethod--> + <argument name="shippingMethod" defaultValue="" type="string"/> </arguments> <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> @@ -23,11 +25,11 @@ <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask1"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml new file mode 100644 index 0000000000000..eb4c1887875e5 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <!--Filter order grid by order id field--> + <actionGroup name="filterOrderGridById"> + <arguments> + <argument name="orderId" type="string"/> + </arguments> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderGridPage"/> + <waitForPageLoad stepKey="waitForOrderGridLoad"/> + <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + <click selector="{{AdminOrdersGridSection.filters}}" stepKey="openOrderGridFilters"/> + <fillField selector="{{AdminOrdersGridSection.idFilter}}" userInput="{{orderId}}" stepKey="fillOrderIdFilter"/> + <click selector="{{AdminOrdersGridSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index 18823b2b0acc2..f7b6868a444ba 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -13,5 +13,9 @@ <element name="submitSearch" type="button" selector=".//*[@id='container']/div/div[2]/div[1]/div[2]/button"/> <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> <element name="createNewOrder" type="button" selector=".page-actions-buttons button#add" timeout="30"/> + <element name="filters" type="button" selector="button[data-action='grid-filter-expand']" timeout="30"/> + <element name="idFilter" type="input" selector=".admin__data-grid-filters input[name='increment_id']"/> + <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> + <element name="clearFilters" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" timeout="30"/> </section> </sections> From 0d80468eb711cf7f0d0ef732d4270a23dc44db2a Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Fri, 28 Sep 2018 09:41:06 +0300 Subject: [PATCH 037/240] MAGETWO-95170: Add Bundle product with zero price to shopping cart --- ...efrontAddBundleProductWithZeroPriceToShoppingCartTest.xml | 1 + .../Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml | 5 +++++ .../Sales/Test/Mftf/Section/AdminOrdersGridSection.xml | 1 + 3 files changed, 7 insertions(+) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml index 112b804382ea2..68602e5342fab 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -48,6 +48,7 @@ <deleteData createDataKey="apiBundleProduct" stepKey="deleteBundleProduct"/> <deleteData createDataKey="createSubCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> </after> <!--Open category page--> <amOnPage url="{{StorefrontCategoryPage.url($$createSubCategory.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml index eb4c1887875e5..65a4b512394d8 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -20,4 +20,9 @@ <fillField selector="{{AdminOrdersGridSection.idFilter}}" userInput="{{orderId}}" stepKey="fillOrderIdFilter"/> <click selector="{{AdminOrdersGridSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> </actionGroup> + <actionGroup name="AdminOrdersGridClearFiltersActionGroup"> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToGridOrdersPage"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index f7b6868a444ba..9ac24f828da75 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -17,5 +17,6 @@ <element name="idFilter" type="input" selector=".admin__data-grid-filters input[name='increment_id']"/> <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="clearFilters" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" timeout="30"/> + <element name="enabledFilters" type="block" selector=".admin__data-grid-header .admin__data-grid-filters-current._show"/> </section> </sections> From a8559e77662e94bcf76518350f6c9000778615cb Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 28 Sep 2018 12:47:45 +0200 Subject: [PATCH 038/240] Fixed line length to comply with coding standard --- app/code/Magento/Catalog/Model/ProductCategoryList.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ProductCategoryList.php b/app/code/Magento/Catalog/Model/ProductCategoryList.php index 92c78c67a341c..f65d9b4c0bf7a 100644 --- a/app/code/Magento/Catalog/Model/ProductCategoryList.php +++ b/app/code/Magento/Catalog/Model/ProductCategoryList.php @@ -81,7 +81,10 @@ public function getCategoryIds($productId) Select::SQL_UNION_ALL ); - $this->categoryIdList[$productId] = array_map('intval', $this->productResource->getConnection()->fetchCol($unionSelect)); + $this->categoryIdList[$productId] = array_map( + 'intval', + $this->productResource->getConnection()->fetchCol($unionSelect) + ); } return $this->categoryIdList[$productId]; From 05dfef4724c1155964fb74c7952ecd71d3204cdf Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Fri, 28 Sep 2018 15:35:19 +0300 Subject: [PATCH 039/240] MAGETWO-95170: Add Bundle product with zero price to shopping cart --- ...torefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml index 68602e5342fab..46ac1cdae93c0 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -47,8 +47,8 @@ <deleteData createDataKey="apiSimple" stepKey="deleteSimple"/> <deleteData createDataKey="apiBundleProduct" stepKey="deleteBundleProduct"/> <deleteData createDataKey="createSubCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <actionGroup ref="logout" stepKey="logout"/> </after> <!--Open category page--> <amOnPage url="{{StorefrontCategoryPage.url($$createSubCategory.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryPage"/> From 1205929dcd9aae647e930331388a38f2b0bc9245 Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Fri, 28 Sep 2018 16:26:10 +0300 Subject: [PATCH 040/240] MAGETWO-95170: Add Bundle product with zero price to shopping cart --- .../Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index 9ac24f828da75..f9e954548a4d0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -11,7 +11,7 @@ <section name="AdminOrdersGridSection"> <element name="search" type="input" selector="#fulltext"/> <element name="submitSearch" type="button" selector=".//*[@id='container']/div/div[2]/div[1]/div[2]/button"/> - <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> + <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)" timeout="30"/> <element name="createNewOrder" type="button" selector=".page-actions-buttons button#add" timeout="30"/> <element name="filters" type="button" selector="button[data-action='grid-filter-expand']" timeout="30"/> <element name="idFilter" type="input" selector=".admin__data-grid-filters input[name='increment_id']"/> From 1399f05721c3806c4df2983ad27851cb9663c300 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 1 Oct 2018 13:40:45 +0300 Subject: [PATCH 041/240] MAGETWO-73449: Changes in default scope not effect product images in other scopes --- .../Model/Product/Gallery/UpdateHandler.php | 12 +- .../ActionGroup/AdminProductActionGroup.xml | 43 ++++++ .../AdminProductGridActionGroup.xml | 9 ++ .../Section/AdminProductContentSection.xml | 15 ++ .../Mftf/Section/AdminProductFormSection.xml | 3 + .../AdminRemoveImageAffectsAllScopesTest.xml | 138 ++++++++++++++++++ app/code/Magento/ProductVideo/i18n/de_DE.csv | 1 + app/code/Magento/ProductVideo/i18n/en_US.csv | 1 + app/code/Magento/ProductVideo/i18n/es_ES.csv | 1 + app/code/Magento/ProductVideo/i18n/fr_FR.csv | 1 + app/code/Magento/ProductVideo/i18n/nl_NL.csv | 1 + app/code/Magento/ProductVideo/i18n/pt_BR.csv | 1 + .../Magento/ProductVideo/i18n/zh_Hans_CN.csv | 1 + .../adminhtml/layout/catalog_product_new.xml | 3 + .../adminhtml/templates/helper/gallery.phtml | 45 +++--- .../web/css/gallery-delete-tooltip.css | 20 +++ .../Store/Test/Mftf/Data/StoreData.xml | 9 ++ .../Store/Test/Mftf/Data/StoreGroupData.xml | 6 + .../Store/Test/Mftf/Data/WebsiteData.xml | 24 +++ 19 files changed, 312 insertions(+), 22 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml create mode 100644 app/code/Magento/ProductVideo/view/adminhtml/web/css/gallery-delete-tooltip.css create mode 100644 app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php index f26e55099b054..9a4f3892fe115 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php @@ -16,7 +16,8 @@ class UpdateHandler extends \Magento\Catalog\Model\Product\Gallery\CreateHandler { /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ protected function processDeletedImages($product, array &$images) @@ -31,7 +32,7 @@ protected function processDeletedImages($product, array &$images) foreach ($images as &$image) { if (!empty($image['removed'])) { - if (!empty($image['value_id']) && !isset($picturesInOtherStores[$image['file']])) { + if (!empty($image['value_id'])) { if (preg_match('/\.\.(\\\|\/)/', $image['file'])) { continue; } @@ -52,7 +53,8 @@ protected function processDeletedImages($product, array &$images) } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ protected function processNewImage($product, array &$image) @@ -79,6 +81,8 @@ protected function processNewImage($product, array &$image) } /** + * Retrieve store ids from product. + * * @param \Magento\Catalog\Model\Product $product * @return array * @since 101.0.0 @@ -97,6 +101,8 @@ protected function extractStoreIds($product) } /** + * Remove deleted images. + * * @param array $files * @return null * @since 101.0.0 diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index ee7e3c82564b7..581941bd29c09 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -90,4 +90,47 @@ <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> </actionGroup> + + <!--Switch to New Store view--> + <actionGroup name="SwitchToTheNewStoreView"> + <arguments> + <argument name="storeViewName" defaultValue="_defaultStore"/> + </arguments> + <scrollTo selector="{{AdminProductContentSection.pageHeader}}" stepKey="scrollToUp"/> + <waitForElementVisible selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="waitForElementBecomeVisible"/> + <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher"/> + <click selector="{{AdminProductFormActionSection.selectStoreView(storeViewName.name)}}" stepKey="chooseStoreView"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> + + <!--Set product to website--> + <actionGroup name="ProductSetWebsite"> + <arguments> + <argument name="website" defaultValue="_defaultWebsite"/> + </arguments> + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ScrollToWebsites"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ClickTpOpenProductInWebsite"/> + <waitForPageLoad stepKey="waitForPageOpened"/> + <click selector="{{ProductInWebsitesSection.website(website.name)}}" stepKey="SelectWebsite"/> + <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> + <waitForPageLoad time='60' stepKey="waitForPageOpened1"/> + </actionGroup> + + <!--Remove product image--> + <actionGroup name="RemoveProductImage"> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> + <waitForPageLoad time="30" stepKey="waitForPageRefresh"/> + <click selector="{{AdminProductImagesSection.removeImageButton}}" stepKey="clickRemoveImage"/> + </actionGroup> + + <!-- Assert no product image in Admin Product page --> + <actionGroup name="AssertProductImageNotInAdminProductPage"> + <arguments> + <argument name="image" defaultValue="MagentoLogo" /> + </arguments> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <dontSeeElement selector="{{AdminProductImagesSection.imageFile(image.filename)}}" stepKey="seeImage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 408586d603835..5c175a55eebe7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -51,4 +51,13 @@ <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMessage"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> </actionGroup> + + <!--Reset the product grid to the default view--> + <actionGroup name="resetProductGridToDefaultView"> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.viewDropdown}}" stepKey="openViewBookmarksTab"/> + <click selector="{{AdminProductGridFilterSection.viewBookmark('Default View')}}" stepKey="resetToDefaultGridView"/> + <waitForPageLoad stepKey="waitForProductGridLoad"/> + <see selector="{{AdminProductGridFilterSection.viewDropdown}}" userInput="Default View" stepKey="seeDefaultViewSelected"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml new file mode 100644 index 0000000000000..ab9fe9616f38a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminProductContentSection"> + <element name="sectionHeaderIfNotShowing" type="button" selector="div[data-index='content'] div[class*='_hide']]"/> + <element name="pageHeader" type="text" selector=".page-header.row"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 5f33eb6f52edf..c5b48b1eb1342 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -181,4 +181,7 @@ <section name="AdminChooseAffectedAttributeSetPopup"> <element name="confirm" type="button" selector="button[data-index='confirm_button']" timeout="30"/> </section> + <section name="AdminAddRelatedProductsModalSection"> + <element name="addSelectedProductsButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_related_related_modal')]//button/span[contains(text(), 'Add Selected Products')]" timeout="30"/> + </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml new file mode 100644 index 0000000000000..b36f67e463a84 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminRemoveImageAffectsAllScopesTest"> + <annotations> + <features value="Catalog"/> + <stories value="MAGETWO-73449: Changes in default scope not effect product images in other scopes"/> + <title value="Effect of product images changes in default scope to other scopes"/> + <description value="Product image should be deleted from all scopes"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-95344"/> + <group value="Catalog"/> + </annotations> + <before> + <!--Create 2 websites (with stores, store views)--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="_defaultProduct" stepKey="product"> + <requiredEntity createDataKey="category"/> + </createData> + + <!-- Create first custom website, store, store view --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> + <argument name="newWebsiteName" value="{{CustomWebSite.name}}"/> + <argument name="websiteCode" value="{{CustomWebSite.code}}"/> + </actionGroup> + + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createStore"> + <argument name="website" value="{{CustomWebSite.name}}"/> + <argument name="storeGroupName" value="{{customStoreGroup.name}}"/> + <argument name="storeGroupCode" value="{{customStoreGroup.code}}"/> + </actionGroup> + + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> + <argument name="storeGroup" value="customStoreGroup"/> + <argument name="customStore" value="customStore"/> + </actionGroup> + + <!-- Create second custom website, store, store view --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> + <argument name="newWebsiteName" value="{{SecondCustomWebSite.name}}"/> + <argument name="websiteCode" value="{{SecondCustomWebSite.code}}"/> + </actionGroup> + + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createSecondStore"> + <argument name="website" value="{{SecondCustomWebSite.name}}"/> + <argument name="storeGroupName" value="{{SecondCustomStoreGroup.name}}"/> + <argument name="storeGroupCode" value="{{SecondCustomStoreGroup.code}}"/> + </actionGroup> + + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView"> + <argument name="storeGroup" value="SecondCustomStoreGroup"/> + <argument name="customStore" value="SecondCustomStore"/> + </actionGroup> + </before> + + <after> + <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{CustomWebSite.name}}"/> + </actionGroup> + + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> + <argument name="websiteName" value="{{SecondCustomWebSite.name}}"/> + </actionGroup> + + <deleteData createDataKey="category" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="product" stepKey="deleteFirstProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + + <!--Open created product--> + <click selector="{{AdminProductGridSection.productGridNameProduct($$product.name$$)}}" stepKey="createdProduct"/> + <waitForPageLoad stepKey="waitForOpenedCreatedProduct"/> + + <!-- Add image to product --> + <actionGroup ref="addProductImage" stepKey="addFirstImageForProduct"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + + <!-- Add second image to product --> + <actionGroup ref="addProductImage" stepKey="addSecondImageForProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + + <!--"Product in Websites": select both Websites--> + <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite1"> + <argument name="website" value="CustomWebSite"/> + </actionGroup> + <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite2"> + <argument name="website" value="SecondCustomWebSite"/> + </actionGroup> + + <!--Go to "Catalog" -> "Products". Open created product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoaded"/> + <click selector="{{AdminProductGridSection.productGridNameProduct($$product.name$$)}}" stepKey="openCreatedProduct"/> + <waitForPageLoad stepKey="waitForCreatedProductOpened"/> + + <!--Delete Image 1--> + <actionGroup ref="RemoveProductImage" stepKey="removeProductImage"/> + + <!--Click "Save" in the upper right corner--> + <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + + <!--Switch to "Store view 1"--> + <actionGroup ref="SwitchToTheNewStoreView" stepKey="selectStoreView"> + <argument name="storeViewName" value="customStore"/> + </actionGroup> + + <!-- Assert product first image not in admin product form --> + <actionGroup ref="AssertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + + <!--Switch to "Store view 2"--> + <actionGroup ref="SwitchToTheNewStoreView" stepKey="selectSecondStoreView"> + <argument name="storeViewName" value="SecondCustomStore"/> + </actionGroup> + + <!-- Verify that Image 1 is deleted from the Second Store View list --> + <actionGroup ref="AssertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInSecondStoreViewPage"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ProductVideo/i18n/de_DE.csv b/app/code/Magento/ProductVideo/i18n/de_DE.csv index 7047317396999..ca24668bb8d16 100644 --- a/app/code/Magento/ProductVideo/i18n/de_DE.csv +++ b/app/code/Magento/ProductVideo/i18n/de_DE.csv @@ -7,3 +7,4 @@ "Preview Image","Preview Image" "Get Video Information","Get Video Information" "Youtube or Vimeo supported","Youtube or Vimeo supported" +"Delete image in all store views","Delete image in all store views" diff --git a/app/code/Magento/ProductVideo/i18n/en_US.csv b/app/code/Magento/ProductVideo/i18n/en_US.csv index 2d226c6daefa3..debcab151cc91 100644 --- a/app/code/Magento/ProductVideo/i18n/en_US.csv +++ b/app/code/Magento/ProductVideo/i18n/en_US.csv @@ -40,3 +40,4 @@ Delete,Delete "Autostart base video","Autostart base video" "Show related video","Show related video" "Auto restart video","Auto restart video" +"Delete image in all store views","Delete image in all store views" diff --git a/app/code/Magento/ProductVideo/i18n/es_ES.csv b/app/code/Magento/ProductVideo/i18n/es_ES.csv index 7047317396999..ca24668bb8d16 100644 --- a/app/code/Magento/ProductVideo/i18n/es_ES.csv +++ b/app/code/Magento/ProductVideo/i18n/es_ES.csv @@ -7,3 +7,4 @@ "Preview Image","Preview Image" "Get Video Information","Get Video Information" "Youtube or Vimeo supported","Youtube or Vimeo supported" +"Delete image in all store views","Delete image in all store views" diff --git a/app/code/Magento/ProductVideo/i18n/fr_FR.csv b/app/code/Magento/ProductVideo/i18n/fr_FR.csv index 7047317396999..ca24668bb8d16 100644 --- a/app/code/Magento/ProductVideo/i18n/fr_FR.csv +++ b/app/code/Magento/ProductVideo/i18n/fr_FR.csv @@ -7,3 +7,4 @@ "Preview Image","Preview Image" "Get Video Information","Get Video Information" "Youtube or Vimeo supported","Youtube or Vimeo supported" +"Delete image in all store views","Delete image in all store views" diff --git a/app/code/Magento/ProductVideo/i18n/nl_NL.csv b/app/code/Magento/ProductVideo/i18n/nl_NL.csv index 7047317396999..ca24668bb8d16 100644 --- a/app/code/Magento/ProductVideo/i18n/nl_NL.csv +++ b/app/code/Magento/ProductVideo/i18n/nl_NL.csv @@ -7,3 +7,4 @@ "Preview Image","Preview Image" "Get Video Information","Get Video Information" "Youtube or Vimeo supported","Youtube or Vimeo supported" +"Delete image in all store views","Delete image in all store views" diff --git a/app/code/Magento/ProductVideo/i18n/pt_BR.csv b/app/code/Magento/ProductVideo/i18n/pt_BR.csv index 7047317396999..ca24668bb8d16 100644 --- a/app/code/Magento/ProductVideo/i18n/pt_BR.csv +++ b/app/code/Magento/ProductVideo/i18n/pt_BR.csv @@ -7,3 +7,4 @@ "Preview Image","Preview Image" "Get Video Information","Get Video Information" "Youtube or Vimeo supported","Youtube or Vimeo supported" +"Delete image in all store views","Delete image in all store views" diff --git a/app/code/Magento/ProductVideo/i18n/zh_Hans_CN.csv b/app/code/Magento/ProductVideo/i18n/zh_Hans_CN.csv index 7047317396999..ca24668bb8d16 100644 --- a/app/code/Magento/ProductVideo/i18n/zh_Hans_CN.csv +++ b/app/code/Magento/ProductVideo/i18n/zh_Hans_CN.csv @@ -7,3 +7,4 @@ "Preview Image","Preview Image" "Get Video Information","Get Video Information" "Youtube or Vimeo supported","Youtube or Vimeo supported" +"Delete image in all store views","Delete image in all store views" diff --git a/app/code/Magento/ProductVideo/view/adminhtml/layout/catalog_product_new.xml b/app/code/Magento/ProductVideo/view/adminhtml/layout/catalog_product_new.xml index f5a22c50e6d0d..63bd5321ad30b 100644 --- a/app/code/Magento/ProductVideo/view/adminhtml/layout/catalog_product_new.xml +++ b/app/code/Magento/ProductVideo/view/adminhtml/layout/catalog_product_new.xml @@ -6,6 +6,9 @@ */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <head> + <css src="Magento_ProductVideo::css/gallery-delete-tooltip.css"/> + </head> <body> <referenceContainer name="content"> diff --git a/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml b/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml index f0ae057bc724d..2ceb9e9c9d4f2 100755 --- a/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml +++ b/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml @@ -34,7 +34,7 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to class="gallery" data-mage-init='{"openVideoModal":{}}' data-parent-component="<?= $block->escapeHtml($block->getData('config/parentComponent')) ?>" - data-images="<?= $block->escapeHtml($block->getImagesJson()) ?>" + data-images="<?= $block->escapeHtmlAttr($block->getImagesJson()) ?>" data-types="<?= $block->escapeHtml( $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getImageTypes()) ) ?>" @@ -140,30 +140,37 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to alt="<%- data.label %>"/> <div class="actions"> - <button type="button" - class="action-remove" - data-role="delete-button" - title="<% if (data.media_type == 'external-video') {%> + <div class="tooltip"> + <span class="delete-tooltiptext"> + <?= $block->escapeHtml( + __('Delete image in all store views') + ); ?> + </span> + <button type="button" + class="action-remove" + data-role="delete-button" + title="<% if (data.media_type == 'external-video') {%> + <?= $block->escapeHtml( + __('Delete video') + ); ?> + <%} else {%> + <?= $block->escapeHtml( + __('Delete image') + ); ?> + <%}%>"> + <span> + <% if (data.media_type == 'external-video') { %> <?= $block->escapeHtml( __('Delete video') ); ?> - <%} else {%> + <% } else {%> <?= $block->escapeHtml( __('Delete image') ); ?> - <%}%>"> - <span> - <% if (data.media_type == 'external-video') { %> - <?= $block->escapeHtml( - __('Delete video') - ); ?> - <% } else {%> - <?= $block->escapeHtml( - __('Delete image') - ); ?> - <%} %> - </span> - </button> + <%} %> + </span> + </button> + </div> <div class="draggable-handle"></div> </div> <div class="image-fade"><span><?= $block->escapeHtml( diff --git a/app/code/Magento/ProductVideo/view/adminhtml/web/css/gallery-delete-tooltip.css b/app/code/Magento/ProductVideo/view/adminhtml/web/css/gallery-delete-tooltip.css new file mode 100644 index 0000000000000..835a22683b157 --- /dev/null +++ b/app/code/Magento/ProductVideo/view/adminhtml/web/css/gallery-delete-tooltip.css @@ -0,0 +1,20 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +.gallery .tooltip .delete-tooltiptext { + visibility: hidden; + width: 112px; + background-color: #373330; + color: #F7F3EB; + text-align: center; + padding: 5px 0; + position: absolute; + z-index: 1; + left: 30px; + top: 91px; +} + +.gallery .tooltip:hover .delete-tooltiptext { + visibility: visible; +} diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 3e38d9aa53c9f..69dd2b20356ff 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -57,4 +57,13 @@ <data key="store_type">group</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> + <entity name="SecondCustomStore" type="store"> + <data key="name" unique="suffix">store</data> + <data key="code" unique="suffix">store</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_action">add</data> + <data key="store_type">store</data> + <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml index 30dfbd9dbedf2..0137a24a187d4 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml @@ -35,4 +35,10 @@ <data key="root_category_id">2</data> <data key="website_id">1</data> </entity> + <entity name="SecondCustomStoreGroup" type="group"> + <data key="name" unique="suffix">store</data> + <data key="code" unique="suffix">store</data> + <data key="root_category_id">2</data> + <data key="website_id">1</data> + </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml new file mode 100644 index 0000000000000..8726bac186f02 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="_defaultWebsite" type="group"> + <data key="name">Main Website</data> + <data key="code">base</data> + <data key="sort_order">1</data> + </entity> + <entity name="CustomWebSite" type="website"> + <data key="name" unique="suffix">website</data> + <data key="code" unique="suffix">website</data> + <data key="sort_order">2</data> + </entity> + <entity name="SecondCustomWebSite" type="website"> + <data key="name" unique="suffix">website</data> + <data key="code" unique="suffix">website</data> + <data key="sort_order">3</data> + </entity> +</entities> From 25569626180d1eaef2bbfb3640e24116e029fd75 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <bkorablov@magento.com> Date: Tue, 2 Oct 2018 09:49:35 +0300 Subject: [PATCH 042/240] MAGETWO-95411: [Backport 2.2.x] Add the ability to install Magento without creating an administrator --- .../Command/AdminUserCreateCommand.php | 43 ++++- .../Setup/Console/Command/InstallCommand.php | 27 ++- setup/src/Magento/Setup/Model/Installer.php | 27 ++- .../Command/AdminUserCreateCommandTest.php | 36 +++- .../Console/Command/InstallCommandTest.php | 37 ++-- .../Setup/Test/Unit/Model/InstallerTest.php | 168 +++++++++++++----- 6 files changed, 261 insertions(+), 77 deletions(-) diff --git a/setup/src/Magento/Setup/Console/Command/AdminUserCreateCommand.php b/setup/src/Magento/Setup/Console/Command/AdminUserCreateCommand.php index 00fa272e74962..173ea9e49a8a4 100644 --- a/setup/src/Magento/Setup/Console/Command/AdminUserCreateCommand.php +++ b/setup/src/Magento/Setup/Console/Command/AdminUserCreateCommand.php @@ -15,6 +15,9 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\Question; +/** + * Command to create an admin user. + */ class AdminUserCreateCommand extends AbstractSetupCommand { /** @@ -52,6 +55,8 @@ protected function configure() } /** + * Creation admin user in interaction mode. + * * @param \Symfony\Component\Console\Input\InputInterface $input * @param \Symfony\Component\Console\Output\OutputInterface $output * @@ -129,6 +134,8 @@ protected function interact(InputInterface $input, OutputInterface $output) } /** + * Add not empty validator. + * * @param \Symfony\Component\Console\Question\Question $question * @return void */ @@ -144,7 +151,7 @@ private function addNotEmptyValidator(Question $question) } /** - * {@inheritdoc} + * @inheritdoc */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -165,25 +172,43 @@ protected function execute(InputInterface $input, OutputInterface $output) /** * Get list of arguments for the command * + * @param int $mode The mode of options. * @return InputOption[] */ - public function getOptionsList() + public function getOptionsList($mode = InputOption::VALUE_REQUIRED) { + $requiredStr = ($mode === InputOption::VALUE_REQUIRED ? '(Required) ' : ''); + return [ - new InputOption(AdminAccount::KEY_USER, null, InputOption::VALUE_REQUIRED, '(Required) Admin user'), - new InputOption(AdminAccount::KEY_PASSWORD, null, InputOption::VALUE_REQUIRED, '(Required) Admin password'), - new InputOption(AdminAccount::KEY_EMAIL, null, InputOption::VALUE_REQUIRED, '(Required) Admin email'), + new InputOption( + AdminAccount::KEY_USER, + null, + $mode, + $requiredStr . 'Admin user' + ), + new InputOption( + AdminAccount::KEY_PASSWORD, + null, + $mode, + $requiredStr . 'Admin password' + ), + new InputOption( + AdminAccount::KEY_EMAIL, + null, + $mode, + $requiredStr . 'Admin email' + ), new InputOption( AdminAccount::KEY_FIRST_NAME, null, - InputOption::VALUE_REQUIRED, - '(Required) Admin first name' + $mode, + $requiredStr . 'Admin first name' ), new InputOption( AdminAccount::KEY_LAST_NAME, null, - InputOption::VALUE_REQUIRED, - '(Required) Admin last name' + $mode, + $requiredStr . 'Admin last name' ), ]; } diff --git a/setup/src/Magento/Setup/Console/Command/InstallCommand.php b/setup/src/Magento/Setup/Console/Command/InstallCommand.php index a2e8715b02233..dd5509d61865a 100644 --- a/setup/src/Magento/Setup/Console/Command/InstallCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InstallCommand.php @@ -11,12 +11,14 @@ use Symfony\Component\Console\Output\OutputInterface; use Magento\Setup\Model\InstallerFactory; use Magento\Framework\Setup\ConsoleLogger; +use Magento\Setup\Model\AdminAccount; use Symfony\Component\Console\Input\InputOption; use Magento\Setup\Model\ConfigModel; use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Helper\QuestionHelper; + /** * Command to install Magento application * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -103,7 +105,7 @@ protected function configure() { $inputOptions = $this->configModel->getAvailableOptions(); $inputOptions = array_merge($inputOptions, $this->userConfig->getOptionsList()); - $inputOptions = array_merge($inputOptions, $this->adminUser->getOptionsList()); + $inputOptions = array_merge($inputOptions, $this->adminUser->getOptionsList(InputOption::VALUE_OPTIONAL)); $inputOptions = array_merge($inputOptions, [ new InputOption( self::INPUT_KEY_CLEANUP_DB, @@ -178,7 +180,7 @@ protected function initialize(InputInterface $input, OutputInterface $output) } $errors = $this->configModel->validate($configOptionsToValidate); - $errors = array_merge($errors, $this->adminUser->validate($input)); + $errors = array_merge($errors, $this->validateAdmin($input)); $errors = array_merge($errors, $this->validate($input)); $errors = array_merge($errors, $this->userConfig->validate($input)); @@ -247,7 +249,7 @@ private function interactiveQuestions(InputInterface $input, OutputInterface $ou $output->writeln(""); - foreach ($this->adminUser->getOptionsList() as $option) { + foreach ($this->adminUser->getOptionsList(InputOption::VALUE_OPTIONAL) as $option) { $configOptionsToValidate[$option->getName()] = $this->askQuestion( $input, $output, @@ -338,4 +340,23 @@ private function askQuestion( return $value; } + + /** + * Performs validation of admin options if at least one of them was set. + * + * @param InputInterface $input + * @return array + */ + private function validateAdmin(InputInterface $input): array + { + if ($input->getOption(AdminAccount::KEY_FIRST_NAME) + || $input->getOption(AdminAccount::KEY_LAST_NAME) + || $input->getOption(AdminAccount::KEY_EMAIL) + || $input->getOption(AdminAccount::KEY_USER) + || $input->getOption(AdminAccount::KEY_PASSWORD) + ) { + return $this->adminUser->validate($input); + } + return []; + } } diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php index 438df0e0ae14b..60c98c01f34db 100644 --- a/setup/src/Magento/Setup/Model/Installer.php +++ b/setup/src/Magento/Setup/Model/Installer.php @@ -316,7 +316,9 @@ public function install($request) [$request[InstallCommand::INPUT_KEY_SALES_ORDER_INCREMENT_PREFIX]], ]; } - $script[] = ['Installing admin user...', 'installAdminUser', [$request]]; + if ($this->isAdminDataSet($request)) { + $script[] = ['Installing admin user...', 'installAdminUser', [$request]]; + } $script[] = ['Caches clearing:', 'cleanCaches', []]; $script[] = ['Disabling Maintenance Mode:', 'setMaintenanceMode', [0]]; $script[] = ['Post installation file permissions check...', 'checkApplicationFilePermissions', []]; @@ -1318,4 +1320,27 @@ private function cleanupGeneratedFiles() $this->log->log($message); } } + + /** + * Checks that admin data is not empty in request array + * + * @param \ArrayObject|array $request + * @return bool + */ + private function isAdminDataSet($request) + { + $adminData = array_filter($request, function ($value, $key) { + return in_array( + $key, + [ + AdminAccount::KEY_EMAIL, + AdminAccount::KEY_FIRST_NAME, + AdminAccount::KEY_LAST_NAME, + AdminAccount::KEY_USER, + AdminAccount::KEY_PASSWORD, + ] + ) && $value !== null; + }, ARRAY_FILTER_USE_BOTH); + return !empty($adminData); + } } diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/AdminUserCreateCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/AdminUserCreateCommandTest.php index d244f48d4e1ea..eda8c819e052a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/AdminUserCreateCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/AdminUserCreateCommandTest.php @@ -11,8 +11,12 @@ use Magento\User\Model\UserValidationRules; use Symfony\Component\Console\Application; use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Tester\CommandTester; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class AdminUserCreateCommandTest extends \PHPUnit\Framework\TestCase { /** @@ -125,11 +129,34 @@ public function testInteraction() ); } - public function testGetOptionsList() + /** + * @param int $mode + * @param string $description + * @dataProvider getOptionListDataProvider + */ + public function testGetOptionsList($mode, $description) { /* @var $argsList \Symfony\Component\Console\Input\InputArgument[] */ - $argsList = $this->command->getOptionsList(); + $argsList = $this->command->getOptionsList($mode); $this->assertEquals(AdminAccount::KEY_EMAIL, $argsList[2]->getName()); + $this->assertEquals($description, $argsList[2]->getDescription()); + } + + /** + * @return array + */ + public function getOptionListDataProvider() + { + return [ + [ + 'mode' => InputOption::VALUE_REQUIRED, + 'description' => '(Required) Admin email', + ], + [ + 'mode' => InputOption::VALUE_OPTIONAL, + 'description' => 'Admin email', + ], + ]; } /** @@ -158,7 +185,10 @@ public function testValidate(array $options, array $errors) public function validateDataProvider() { return [ - [[null, 'Doe', 'admin', 'test@test.com', '123123q', '123123q'], ['First Name is a required field.']], + [ + [null, 'Doe', 'admin', 'test@test.com', '123123q', '123123q'], + ['First Name is a required field.'] + ], [ ['John', null, null, 'test@test.com', '123123q', '123123q'], ['User Name is a required field.', 'Last Name is a required field.'], diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/InstallCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/InstallCommandTest.php index 5b7b6c1626911..3c3a875a278e8 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/InstallCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/InstallCommandTest.php @@ -16,6 +16,7 @@ use Magento\Backend\Setup\ConfigOptionsList as BackendConfigOptionsList; use Magento\Framework\Config\ConfigOptionsListConstants as SetupConfigOptionsList; use Magento\Setup\Model\StoreConfigurationDataMapper; +use Magento\Setup\Console\Command\AdminUserCreateCommand; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -62,6 +63,11 @@ class InstallCommandTest extends \PHPUnit\Framework\TestCase */ private $configImportMock; + /** + * @var AdminUserCreateCommand|\PHPUnit_Framework_MockObject_MockObject + */ + private $adminUserMock; + public function setUp() { $this->input = [ @@ -73,11 +79,6 @@ public function setUp() '--' . StoreConfigurationDataMapper::KEY_LANGUAGE => 'en_US', '--' . StoreConfigurationDataMapper::KEY_TIMEZONE => 'America/Chicago', '--' . StoreConfigurationDataMapper::KEY_CURRENCY => 'USD', - '--' . AdminAccount::KEY_USER => 'user', - '--' . AdminAccount::KEY_PASSWORD => '123123q', - '--' . AdminAccount::KEY_EMAIL => 'test@test.com', - '--' . AdminAccount::KEY_FIRST_NAME => 'John', - '--' . AdminAccount::KEY_LAST_NAME => 'Doe', ]; $configModel = $this->createMock(\Magento\Setup\Model\ConfigModel::class); @@ -100,15 +101,11 @@ public function setUp() ->method('validate') ->will($this->returnValue([])); - $adminUser = $this->createMock(\Magento\Setup\Console\Command\AdminUserCreateCommand::class); - $adminUser + $this->adminUserMock = $this->createMock(AdminUserCreateCommand::class); + $this->adminUserMock ->expects($this->once()) ->method('getOptionsList') ->will($this->returnValue($this->getOptionsListAdminUser())); - $adminUser - ->expects($this->once()) - ->method('validate') - ->will($this->returnValue([])); $this->installerFactory = $this->createMock(\Magento\Setup\Model\InstallerFactory::class); $this->installer = $this->createMock(\Magento\Setup\Model\Installer::class); @@ -143,7 +140,7 @@ public function setUp() $this->installerFactory, $configModel, $userConfig, - $adminUser + $this->adminUserMock ); $this->command->setApplication( $this->applicationMock @@ -152,6 +149,16 @@ public function setUp() public function testExecute() { + $this->input['--' . AdminAccount::KEY_USER] = 'user'; + $this->input['--' . AdminAccount::KEY_PASSWORD] = '123123q'; + $this->input['--' . AdminAccount::KEY_EMAIL] = 'test@test.com'; + $this->input['--' . AdminAccount::KEY_FIRST_NAME] = 'John'; + $this->input['--' . AdminAccount::KEY_LAST_NAME] = 'Doe'; + + $this->adminUserMock + ->expects($this->once()) + ->method('validate') + ->willReturn([]); $this->installerFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->installer)); @@ -269,6 +276,9 @@ private function getOptionsListAdminUser() */ public function testValidate($prefixValue) { + $this->adminUserMock + ->expects($this->never()) + ->method('validate'); $this->installerFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->installer)); @@ -288,6 +298,9 @@ public function testValidate($prefixValue) */ public function testValidateWithException($prefixValue) { + $this->adminUserMock + ->expects($this->never()) + ->method('validate'); $this->installerFactory->expects($this->never()) ->method('create') ->will($this->returnValue($this->installer)); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php index ca8799393320f..6e57f452a997d 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php @@ -14,6 +14,7 @@ use Magento\Framework\Config\File\ConfigFilePool; use Magento\Framework\App\State\CleanupFiles; use Magento\Setup\Validator\DbValidator; +use Magento\Setup\Model\AdminAccount; /** * @SuppressWarnings(PHPMD.TooManyFields) @@ -227,15 +228,15 @@ private function createObject($connectionFactory = false, $objectManagerProvider ); } - public function testInstall() + /** + * @param array $request + * @param array $logMessages + * @dataProvider installDataProvider + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testInstall(array $request, array $logMessages) { - $request = [ - ConfigOptionsListConstants::INPUT_KEY_DB_HOST => '127.0.0.1', - ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'magento', - ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'magento', - ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => 'encryption_key', - ConfigOptionsList::INPUT_KEY_BACKEND_FRONTNAME => 'backend', - ]; $this->config->expects($this->atLeastOnce()) ->method('get') ->willReturnMap( @@ -285,7 +286,7 @@ public function testInstall() [\Magento\Framework\Module\ModuleResource::class, [], $moduleResource], [\Magento\Framework\Registry::class, $registry] ])); - $this->adminFactory->expects($this->once())->method('create')->willReturn( + $this->adminFactory->expects($this->any())->method('create')->willReturn( $this->createMock(\Magento\Setup\Model\AdminAccount::class) ); $this->sampleDataState->expects($this->once())->method('hasError')->willReturn(true); @@ -298,11 +299,119 @@ public function testInstall() $this->filePermissions->expects($this->once()) ->method('getMissingWritableDirectoriesForDbUpgrade') ->willReturn([]); - $this->setupLoggerExpectsForInstall(); + call_user_func_array( + [ + $this->logger->expects($this->exactly(count($logMessages)))->method('log'), + 'withConsecutive' + ], + $logMessages + ); + $this->logger->expects($this->exactly(2)) + ->method('logSuccess') + ->withConsecutive( + ['Magento installation complete.'], + ['Magento Admin URI: /'] + ); $this->object->install($request); } + /** + * @return array + */ + public function installDataProvider() + { + return [ + [ + 'request' => [ + ConfigOptionsListConstants::INPUT_KEY_DB_HOST => '127.0.0.1', + ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'magento', + ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'magento', + ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => 'encryption_key', + ConfigOptionsList::INPUT_KEY_BACKEND_FRONTNAME => 'backend', + ], + 'logMessages' => [ + ['Starting Magento installation:'], + ['File permissions check...'], + ['Required extensions check...'], + ['Enabling Maintenance Mode...'], + ['Installing deployment configuration...'], + ['Installing database schema:'], + ['Schema creation/updates:'], + ['Module \'Foo_One\':'], + ['Module \'Bar_Two\':'], + ['Schema post-updates:'], + ['Module \'Foo_One\':'], + ['Module \'Bar_Two\':'], + ['Installing user configuration...'], + ['Enabling caches:'], + ['Current status:'], + [''], + ['Installing data...'], + ['Data install/update:'], + ['Module \'Foo_One\':'], + ['Module \'Bar_Two\':'], + ['Data post-updates:'], + ['Module \'Foo_One\':'], + ['Module \'Bar_Two\':'], + //['Installing admin user...'], + ['Caches clearing:'], + ['Cache cleared successfully'], + ['Disabling Maintenance Mode:'], + ['Post installation file permissions check...'], + ['Write installation date...'], + ['Sample Data is installed with errors. See log file for details'] + ], + ], + [ + 'request' => [ + ConfigOptionsListConstants::INPUT_KEY_DB_HOST => '127.0.0.1', + ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'magento', + ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'magento', + ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => 'encryption_key', + ConfigOptionsList::INPUT_KEY_BACKEND_FRONTNAME => 'backend', + AdminAccount::KEY_USER => 'admin', + AdminAccount::KEY_PASSWORD => '123', + AdminAccount::KEY_EMAIL => 'admin@example.com', + AdminAccount::KEY_FIRST_NAME => 'John', + AdminAccount::KEY_LAST_NAME => 'Doe', + ], + 'logMessages' => [ + ['Starting Magento installation:'], + ['File permissions check...'], + ['Required extensions check...'], + ['Enabling Maintenance Mode...'], + ['Installing deployment configuration...'], + ['Installing database schema:'], + ['Schema creation/updates:'], + ['Module \'Foo_One\':'], + ['Module \'Bar_Two\':'], + ['Schema post-updates:'], + ['Module \'Foo_One\':'], + ['Module \'Bar_Two\':'], + ['Installing user configuration...'], + ['Enabling caches:'], + ['Current status:'], + [''], + ['Installing data...'], + ['Data install/update:'], + ['Module \'Foo_One\':'], + ['Module \'Bar_Two\':'], + ['Data post-updates:'], + ['Module \'Foo_One\':'], + ['Module \'Bar_Two\':'], + ['Installing admin user...'], + ['Caches clearing:'], + ['Cache cleared successfully'], + ['Disabling Maintenance Mode:'], + ['Post installation file permissions check...'], + ['Write installation date...'], + ['Sample Data is installed with errors. See log file for details'] + ], + ], + ]; + } + public function testCheckInstallationFilePermissions() { $this->filePermissions @@ -512,45 +621,6 @@ private function prepareForUpdateModulesTests() return $newObject; } - - /** - * Set up logger expectations for install method - * - * @return void - */ - private function setupLoggerExpectsForInstall() - { - $this->logger->expects($this->at(0))->method('log')->with('Starting Magento installation:'); - $this->logger->expects($this->at(1))->method('log')->with('File permissions check...'); - $this->logger->expects($this->at(3))->method('log')->with('Required extensions check...'); - // at(2) invokes logMeta() - $this->logger->expects($this->at(5))->method('log')->with('Enabling Maintenance Mode...'); - // at(4) - logMeta and so on... - $this->logger->expects($this->at(7))->method('log')->with('Installing deployment configuration...'); - $this->logger->expects($this->at(9))->method('log')->with('Installing database schema:'); - $this->logger->expects($this->at(11))->method('log')->with("Module 'Foo_One':"); - $this->logger->expects($this->at(13))->method('log')->with("Module 'Bar_Two':"); - $this->logger->expects($this->at(15))->method('log')->with('Schema post-updates:'); - $this->logger->expects($this->at(16))->method('log')->with("Module 'Foo_One':"); - $this->logger->expects($this->at(18))->method('log')->with("Module 'Bar_Two':"); - $this->logger->expects($this->at(21))->method('log')->with('Installing user configuration...'); - $this->logger->expects($this->at(23))->method('log')->with('Enabling caches:'); - $this->logger->expects($this->at(27))->method('log')->with('Installing data...'); - $this->logger->expects($this->at(28))->method('log')->with('Data install/update:'); - $this->logger->expects($this->at(29))->method('log')->with("Module 'Foo_One':"); - $this->logger->expects($this->at(31))->method('log')->with("Module 'Bar_Two':"); - $this->logger->expects($this->at(33))->method('log')->with('Data post-updates:'); - $this->logger->expects($this->at(34))->method('log')->with("Module 'Foo_One':"); - $this->logger->expects($this->at(36))->method('log')->with("Module 'Bar_Two':"); - $this->logger->expects($this->at(39))->method('log')->with('Installing admin user...'); - $this->logger->expects($this->at(41))->method('log')->with('Caches clearing:'); - $this->logger->expects($this->at(44))->method('log')->with('Disabling Maintenance Mode:'); - $this->logger->expects($this->at(46))->method('log')->with('Post installation file permissions check...'); - $this->logger->expects($this->at(48))->method('log')->with('Write installation date...'); - $this->logger->expects($this->at(50))->method('logSuccess')->with('Magento installation complete.'); - $this->logger->expects($this->at(52))->method('log') - ->with('Sample Data is installed with errors. See log file for details'); - } } namespace Magento\Setup\Model; From 000b45967dfb70fc921363b102709ec49cb91f12 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <bkorablov@magento.com> Date: Tue, 2 Oct 2018 11:50:09 +0300 Subject: [PATCH 043/240] MAGETWO-95411: [Backport 2.2.x] Add the ability to install Magento without creating an administrator --- setup/src/Magento/Setup/Console/Command/InstallCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/setup/src/Magento/Setup/Console/Command/InstallCommand.php b/setup/src/Magento/Setup/Console/Command/InstallCommand.php index dd5509d61865a..1276a443697c8 100644 --- a/setup/src/Magento/Setup/Console/Command/InstallCommand.php +++ b/setup/src/Magento/Setup/Console/Command/InstallCommand.php @@ -18,7 +18,6 @@ use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Helper\QuestionHelper; - /** * Command to install Magento application * @SuppressWarnings(PHPMD.CouplingBetweenObjects) From 6a701a94118122f617ae92ebf1818228ef0b0147 Mon Sep 17 00:00:00 2001 From: Vitalii Zabaznov <vzabaznov@magento.com> Date: Tue, 2 Oct 2018 18:25:45 +0300 Subject: [PATCH 044/240] MAGETWO-94670: Product Export fails --- .../Magento/CatalogImportExport/Model/Export/Product.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 86196cbe652a2..4ece425e53ce5 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -517,10 +517,13 @@ protected function getMediaGallery(array $productIds) if (empty($productIds)) { return []; } + + $productEntityJoinField = $this->getProductEntityLinkField(); + $select = $this->_connection->select()->from( ['mgvte' => $this->_resourceModel->getTableName('catalog_product_entity_media_gallery_value_to_entity')], [ - "mgvte.{$this->getProductEntityLinkField()}", + "mgvte.$productEntityJoinField", 'mgvte.value_id' ] )->joinLeft( @@ -532,7 +535,7 @@ protected function getMediaGallery(array $productIds) ] )->joinLeft( ['mgv' => $this->_resourceModel->getTableName('catalog_product_entity_media_gallery_value')], - '(mg.value_id = mgv.value_id)', + "(mg.value_id = mgv.value_id) and (mgvte.$productEntityJoinField = mgv.$productEntityJoinField)", [ 'mgv.label', 'mgv.position', From a885eacd7b84e780539bd60076934c1d6050846b Mon Sep 17 00:00:00 2001 From: Vitalii Zabaznov <vzabaznov@magento.com> Date: Tue, 2 Oct 2018 18:40:42 +0300 Subject: [PATCH 045/240] MAGETWO-94670: Product Export fails --- app/code/Magento/CatalogImportExport/Model/Export/Product.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 4ece425e53ce5..e77c01b658eea 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -543,14 +543,14 @@ protected function getMediaGallery(array $productIds) 'mgv.store_id' ] )->where( - "mgvte.{$this->getProductEntityLinkField()} IN (?)", + "mgvte.$productEntityJoinField IN (?)", $productIds ); $rowMediaGallery = []; $stmt = $this->_connection->query($select); while ($mediaRow = $stmt->fetch()) { - $rowMediaGallery[$mediaRow[$this->getProductEntityLinkField()]][] = [ + $rowMediaGallery[$mediaRow[$productEntityJoinField]][] = [ '_media_attribute_id' => $mediaRow['attribute_id'], '_media_image' => $mediaRow['filename'], '_media_label' => $mediaRow['label'], From d90954923e5e314e9fa7759d8d54d28e5af2b353 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Wed, 3 Oct 2018 12:12:10 +0300 Subject: [PATCH 046/240] MAGETWO-94458: Unable to create credit memo for order placed via online payment with taxes and discounts --- .../Model/Order/Creditmemo/Total/Shipping.php | 11 ++- .../Model/Order/Creditmemo/Total/Tax.php | 13 ++-- .../Sales/Model/Order/Invoice/Total/Tax.php | 28 +++++--- .../Sales/Model/Service/CreditmemoService.php | 16 +++-- .../Model/Service/CreditmemoServiceTest.php | 70 +++++++++++-------- 5 files changed, 79 insertions(+), 59 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Shipping.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Shipping.php index f00334f496b2a..f644d0c3a5a63 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Shipping.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Shipping.php @@ -37,6 +37,8 @@ public function __construct( } /** + * Collects credit memo shipping totals. + * * @param \Magento\Sales\Model\Order\Creditmemo $creditmemo * @return $this * @throws \Magento\Framework\Exception\LocalizedException @@ -55,12 +57,10 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) $orderShippingInclTax = $order->getShippingInclTax(); $orderBaseShippingInclTax = $order->getBaseShippingInclTax(); $allowedTaxAmount = $order->getShippingTaxAmount() - $order->getShippingTaxRefunded(); - $baseAllowedTaxAmount = $order->getBaseShippingTaxAmount() - $order->getBaseShippingTaxRefunded(); $allowedAmountInclTax = $allowedAmount + $allowedTaxAmount; - $baseAllowedAmountInclTax = $baseAllowedAmount + $baseAllowedTaxAmount; - - // for the credit memo - $shippingAmount = $baseShippingAmount = $shippingInclTax = $baseShippingInclTax = 0; + $baseAllowedAmountInclTax = $orderBaseShippingInclTax + - $order->getBaseShippingRefunded() + - $order->getBaseShippingTaxRefunded(); // Check if the desired shipping amount to refund was specified (from invoice or another source). if ($creditmemo->hasBaseShippingAmount()) { @@ -128,7 +128,6 @@ private function isSuppliedShippingAmountInclTax($order) /** * Get the Tax Config. - * In a future release, will become a constructor parameter. * * @return \Magento\Tax\Model\Config * diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php index a842c0470ad85..5ab9469441bef 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -5,9 +5,14 @@ */ namespace Magento\Sales\Model\Order\Creditmemo\Total; +/** + * Collects credit memo taxes. + */ class Tax extends AbstractTotal { /** + * Collects credit memo taxes. + * * @param \Magento\Sales\Model\Order\Creditmemo $creditmemo * @return $this * @@ -79,18 +84,10 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) $totalDiscountTaxCompensation += $invoice->getShippingDiscountTaxCompensationAmount() * $taxFactor; $baseTotalDiscountTaxCompensation += $invoice->getBaseShippingDiscountTaxCompensationAmnt() * $taxFactor; - $shippingDiscountTaxCompensationAmount = - $invoice->getShippingDiscountTaxCompensationAmount() * $taxFactor; - $baseShippingDiscountTaxCompensationAmount = - $invoice->getBaseShippingDiscountTaxCompensationAmnt() * $taxFactor; $shippingTaxAmount = $creditmemo->roundPrice($shippingTaxAmount); $baseShippingTaxAmount = $creditmemo->roundPrice($baseShippingTaxAmount, 'base'); $totalDiscountTaxCompensation = $creditmemo->roundPrice($totalDiscountTaxCompensation); $baseTotalDiscountTaxCompensation = $creditmemo->roundPrice($baseTotalDiscountTaxCompensation, 'base'); - $shippingDiscountTaxCompensationAmount = - $creditmemo->roundPrice($shippingDiscountTaxCompensationAmount); - $baseShippingDiscountTaxCompensationAmount = - $creditmemo->roundPrice($baseShippingDiscountTaxCompensationAmount, 'base'); if ($taxFactor < 1 && $invoice->getShippingTaxAmount() > 0) { $isPartialShippingRefunded = true; } diff --git a/app/code/Magento/Sales/Model/Order/Invoice/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Invoice/Total/Tax.php index fd5c015d9db4f..6e12f10f0c679 100644 --- a/app/code/Magento/Sales/Model/Order/Invoice/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Invoice/Total/Tax.php @@ -5,6 +5,9 @@ */ namespace Magento\Sales\Model\Order\Invoice\Total; +/** + * Collects invoice taxes. + */ class Tax extends AbstractTotal { /** @@ -69,11 +72,24 @@ public function collect(\Magento\Sales\Model\Order\Invoice $invoice) } } + $taxDiscountCompensationAmt = $totalDiscountTaxCompensation; + $baseTaxDiscountCompensationAmt = $baseTotalDiscountTaxCompensation; + $allowedDiscountTaxCompensation = $order->getDiscountTaxCompensationAmount() - + $order->getDiscountTaxCompensationInvoiced(); + $allowedBaseDiscountTaxCompensation = $order->getBaseDiscountTaxCompensationAmount() - + $order->getBaseDiscountTaxCompensationInvoiced(); + if ($this->_canIncludeShipping($invoice)) { $totalTax += $order->getShippingTaxAmount(); $baseTotalTax += $order->getBaseShippingTaxAmount(); $totalDiscountTaxCompensation += $order->getShippingDiscountTaxCompensationAmount(); $baseTotalDiscountTaxCompensation += $order->getBaseShippingDiscountTaxCompensationAmnt(); + + $allowedDiscountTaxCompensation += $order->getShippingDiscountTaxCompensationAmount() - + $order->getShippingDiscountTaxCompensationInvoiced(); + $allowedBaseDiscountTaxCompensation += $order->getBaseShippingDiscountTaxCompensationAmnt() - + $order->getBaseShippingDiscountTaxCompensationInvoiced(); + $invoice->setShippingTaxAmount($order->getShippingTaxAmount()); $invoice->setBaseShippingTaxAmount($order->getBaseShippingTaxAmount()); $invoice->setShippingDiscountTaxCompensationAmount($order->getShippingDiscountTaxCompensationAmount()); @@ -81,14 +97,6 @@ public function collect(\Magento\Sales\Model\Order\Invoice $invoice) } $allowedTax = $order->getTaxAmount() - $order->getTaxInvoiced(); $allowedBaseTax = $order->getBaseTaxAmount() - $order->getBaseTaxInvoiced(); - $allowedDiscountTaxCompensation = $order->getDiscountTaxCompensationAmount() + - $order->getShippingDiscountTaxCompensationAmount() - - $order->getDiscountTaxCompensationInvoiced() - - $order->getShippingDiscountTaxCompensationInvoiced(); - $allowedBaseDiscountTaxCompensation = $order->getBaseDiscountTaxCompensationAmount() + - $order->getBaseShippingDiscountTaxCompensationAmnt() - - $order->getBaseDiscountTaxCompensationInvoiced() - - $order->getBaseShippingDiscountTaxCompensationInvoiced(); if ($invoice->isLast()) { $totalTax = $allowedTax; @@ -107,8 +115,8 @@ public function collect(\Magento\Sales\Model\Order\Invoice $invoice) $invoice->setTaxAmount($totalTax); $invoice->setBaseTaxAmount($baseTotalTax); - $invoice->setDiscountTaxCompensationAmount($totalDiscountTaxCompensation); - $invoice->setBaseDiscountTaxCompensationAmount($baseTotalDiscountTaxCompensation); + $invoice->setDiscountTaxCompensationAmount($taxDiscountCompensationAmt); + $invoice->setBaseDiscountTaxCompensationAmount($baseTaxDiscountCompensationAmt); $invoice->setGrandTotal($invoice->getGrandTotal() + $totalTax + $totalDiscountTaxCompensation); $invoice->setBaseGrandTotal($invoice->getBaseGrandTotal() + $baseTotalTax + $baseTotalDiscountTaxCompensation); diff --git a/app/code/Magento/Sales/Model/Service/CreditmemoService.php b/app/code/Magento/Sales/Model/Service/CreditmemoService.php index 905e9cc4eae1c..1173fa4b7eb5a 100644 --- a/app/code/Magento/Sales/Model/Service/CreditmemoService.php +++ b/app/code/Magento/Sales/Model/Service/CreditmemoService.php @@ -189,6 +189,8 @@ public function refund( } /** + * Checks if credit memo is available for refund. + * * @param \Magento\Sales\Api\Data\CreditmemoInterface $creditmemo * @return bool * @throws \Magento\Framework\Exception\LocalizedException @@ -211,7 +213,7 @@ protected function validateForRefund(\Magento\Sales\Api\Data\CreditmemoInterface throw new \Magento\Framework\Exception\LocalizedException( __( 'The most money available to refund is %1.', - $creditmemo->getOrder()->formatBasePrice($baseAvailableRefund) + $creditmemo->getOrder()->formatPriceTxt($baseAvailableRefund) ) ); } @@ -219,8 +221,9 @@ protected function validateForRefund(\Magento\Sales\Api\Data\CreditmemoInterface } /** - * @return \Magento\Sales\Model\Order\RefundAdapterInterface + * Initializes RefundAdapterInterface dependency. * + * @return \Magento\Sales\Model\Order\RefundAdapterInterface * @deprecated 100.1.3 */ private function getRefundAdapter() @@ -233,8 +236,9 @@ private function getRefundAdapter() } /** - * @return \Magento\Framework\App\ResourceConnection|mixed + * Initializes ResourceConnection dependency. * + * @return \Magento\Framework\App\ResourceConnection|mixed * @deprecated 100.1.3 */ private function getResource() @@ -247,8 +251,9 @@ private function getResource() } /** - * @return \Magento\Sales\Api\OrderRepositoryInterface + * Initializes OrderRepositoryInterface dependency. * + * @return \Magento\Sales\Api\OrderRepositoryInterface * @deprecated 100.1.3 */ private function getOrderRepository() @@ -261,8 +266,9 @@ private function getOrderRepository() } /** - * @return \Magento\Sales\Api\InvoiceRepositoryInterface + * Initializes InvoiceRepositoryInterface dependency. * + * @return \Magento\Sales\Api\InvoiceRepositoryInterface * @deprecated 100.1.3 */ private function getInvoiceRepository() diff --git a/app/code/Magento/Sales/Test/Unit/Model/Service/CreditmemoServiceTest.php b/app/code/Magento/Sales/Test/Unit/Model/Service/CreditmemoServiceTest.php index 2e668f0b0d6f1..68681c6c5a66b 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Service/CreditmemoServiceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Service/CreditmemoServiceTest.php @@ -3,10 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Test\Unit\Model\Service; use Magento\Sales\Model\Order; +use Magento\Sales\Api\Data\CreditmemoInterface; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + /** * Class CreditmemoServiceTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -14,34 +19,34 @@ class CreditmemoServiceTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Sales\Api\CreditmemoRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Api\CreditmemoRepositoryInterface|MockObject */ protected $creditmemoRepositoryMock; /** - * @var \Magento\Sales\Api\CreditmemoCommentRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Api\CreditmemoCommentRepositoryInterface|MockObject */ protected $creditmemoCommentRepositoryMock; /** - * @var \Magento\Framework\Api\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Api\SearchCriteriaBuilder|MockObject */ protected $searchCriteriaBuilderMock; /** - * @var \Magento\Framework\Api\FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Api\FilterBuilder|MockObject */ protected $filterBuilderMock; /** - * @var \Magento\Sales\Model\Order\CreditmemoNotifier|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\Order\CreditmemoNotifier|MockObject */ protected $creditmemoNotifierMock; /** - * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\PriceCurrencyInterface|MockObject */ - private $priceCurrencyMock; + private $priceCurrency; /** * @var \Magento\Sales\Model\Service\CreditmemoService @@ -79,7 +84,7 @@ protected function setUp() ['setField', 'setValue', 'setConditionType', 'create'] ); $this->creditmemoNotifierMock = $this->createMock(\Magento\Sales\Model\Order\CreditmemoNotifier::class); - $this->priceCurrencyMock = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class) + $this->priceCurrency = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class) ->getMockForAbstractClass(); $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -91,7 +96,7 @@ protected function setUp() 'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock, 'filterBuilder' => $this->filterBuilderMock, 'creditmemoNotifier' => $this->creditmemoNotifierMock, - 'priceCurrency' => $this->priceCurrencyMock, + 'priceCurrency' => $this->priceCurrency, ] ); } @@ -187,7 +192,7 @@ public function testRefund() $orderMock->expects($this->once())->method('getBaseTotalPaid')->willReturn(10); $creditMemoMock->expects($this->once())->method('getBaseGrandTotal')->willReturn(10); - $this->priceCurrencyMock->expects($this->any()) + $this->priceCurrency->expects($this->any()) ->method('round') ->willReturnArgument(0); @@ -259,7 +264,7 @@ public function testRefundPendingCreditMemo() $orderMock->expects($this->once())->method('getBaseTotalPaid')->willReturn(10); $creditMemoMock->expects($this->once())->method('getBaseGrandTotal')->willReturn(10); - $this->priceCurrencyMock->expects($this->any()) + $this->priceCurrency->expects($this->any()) ->method('round') ->willReturnArgument(0); @@ -324,27 +329,32 @@ public function testRefundExpectsMoneyAvailableToReturn() $baseGrandTotal = 10; $baseTotalRefunded = 9; $baseTotalPaid = 10; - $creditMemoMock = $this->getMockBuilder(\Magento\Sales\Api\Data\CreditmemoInterface::class) - ->setMethods(['getId', 'getOrder', 'formatBasePrice']) + /** @var CreditmemoInterface|MockObject $creditMemo */ + $creditMemo = $this->getMockBuilder(CreditmemoInterface::class) + ->setMethods(['getId', 'getOrder']) ->getMockForAbstractClass(); - $creditMemoMock->expects($this->once())->method('getId')->willReturn(null); - $orderMock = $this->getMockBuilder(Order::class)->disableOriginalConstructor()->getMock(); - $creditMemoMock->expects($this->atLeastOnce())->method('getOrder')->willReturn($orderMock); - $creditMemoMock->expects($this->once())->method('getBaseGrandTotal')->willReturn($baseGrandTotal); - $orderMock->expects($this->atLeastOnce())->method('getBaseTotalRefunded')->willReturn($baseTotalRefunded); - $this->priceCurrencyMock->expects($this->exactly(2))->method('round')->withConsecutive( - [$baseTotalRefunded + $baseGrandTotal], - [$baseTotalPaid] - )->willReturnOnConsecutiveCalls( - $baseTotalRefunded + $baseGrandTotal, - $baseTotalPaid - ); - $orderMock->expects($this->atLeastOnce())->method('getBaseTotalPaid')->willReturn($baseTotalPaid); + $creditMemo->method('getId') + ->willReturn(null); + /** @var Order|MockObject $order */ + $order = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + $creditMemo->method('getOrder') + ->willReturn($order); + $creditMemo->method('getBaseGrandTotal') + ->willReturn($baseGrandTotal); + $order->method('getBaseTotalRefunded') + ->willReturn($baseTotalRefunded); + $this->priceCurrency->method('round') + ->withConsecutive([$baseTotalRefunded + $baseGrandTotal], [$baseTotalPaid]) + ->willReturnOnConsecutiveCalls($baseTotalRefunded + $baseGrandTotal, $baseTotalPaid); + $order->method('getBaseTotalPaid') + ->willReturn($baseTotalPaid); $baseAvailableRefund = $baseTotalPaid - $baseTotalRefunded; - $orderMock->expects($this->once())->method('formatBasePrice')->with( - $baseAvailableRefund - )->willReturn($baseAvailableRefund); - $this->creditmemoService->refund($creditMemoMock, true); + $order->method('formatPriceTxt') + ->with($baseAvailableRefund) + ->willReturn($baseAvailableRefund); + $this->creditmemoService->refund($creditMemo, true); } /** From 5937495fca6903d999896657bc36f698225cb454 Mon Sep 17 00:00:00 2001 From: Neeta Kangiya <neeta@wagento.com> Date: Thu, 4 Oct 2018 12:28:16 +0530 Subject: [PATCH 047/240] resolve typo in dispatchevent --- .../Model/Indexer/Fulltext/Action/DataProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 381fb573b21a1..80034eb9f8308 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -317,7 +317,7 @@ public function getSearchableAttributes($backendType = null) $attributes = $productAttributes->getItems(); $this->eventManager->dispatch( - 'catelogsearch_searchable_attributes_load_after', + 'catalogsearch_searchable_attributes_load_after', ['engine' => $this->engine, 'attributes' => $attributes] ); From 51d8e20b6fcb3e6135884d864e27c0ee5b276b24 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 4 Oct 2018 14:48:06 +0300 Subject: [PATCH 048/240] MAGETWO-73449: Changes in default scope not effect product images in other scopes --- .../ActionGroup/AdminProductActionGroup.xml | 6 +- .../AdminProductGridActionGroup.xml | 9 --- .../Mftf/Section/AdminProductFormSection.xml | 3 - .../AdminRemoveImageAffectsAllScopesTest.xml | 63 ++++++++++--------- .../Store/Test/Mftf/Data/StoreData.xml | 18 +++--- .../Store/Test/Mftf/Data/StoreGroupData.xml | 14 +++-- .../Store/Test/Mftf/Data/WebsiteData.xml | 8 +-- 7 files changed, 59 insertions(+), 62 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 581941bd29c09..ea762ca0280a3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -96,7 +96,7 @@ <arguments> <argument name="storeViewName" defaultValue="_defaultStore"/> </arguments> - <scrollTo selector="{{AdminProductContentSection.pageHeader}}" stepKey="scrollToUp"/> + <scrollTo selector="{{AdminHeaderSection.pageTitle}}" stepKey="scrollToUp"/> <waitForElementVisible selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="waitForElementBecomeVisible"/> <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher"/> <click selector="{{AdminProductFormActionSection.selectStoreView(storeViewName.name)}}" stepKey="chooseStoreView"/> @@ -107,13 +107,13 @@ <!--Set product to website--> <actionGroup name="ProductSetWebsite"> <arguments> - <argument name="website" defaultValue="_defaultWebsite"/> + <argument name="website" defaultValue="DefaultWebsite"/> </arguments> <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ScrollToWebsites"/> <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ClickTpOpenProductInWebsite"/> <waitForPageLoad stepKey="waitForPageOpened"/> <click selector="{{ProductInWebsitesSection.website(website.name)}}" stepKey="SelectWebsite"/> - <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> <waitForPageLoad time='60' stepKey="waitForPageOpened1"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 5c175a55eebe7..408586d603835 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -51,13 +51,4 @@ <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMessage"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> </actionGroup> - - <!--Reset the product grid to the default view--> - <actionGroup name="resetProductGridToDefaultView"> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.viewDropdown}}" stepKey="openViewBookmarksTab"/> - <click selector="{{AdminProductGridFilterSection.viewBookmark('Default View')}}" stepKey="resetToDefaultGridView"/> - <waitForPageLoad stepKey="waitForProductGridLoad"/> - <see selector="{{AdminProductGridFilterSection.viewDropdown}}" userInput="Default View" stepKey="seeDefaultViewSelected"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index c5b48b1eb1342..5f33eb6f52edf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -181,7 +181,4 @@ <section name="AdminChooseAffectedAttributeSetPopup"> <element name="confirm" type="button" selector="button[data-index='confirm_button']" timeout="30"/> </section> - <section name="AdminAddRelatedProductsModalSection"> - <element name="addSelectedProductsButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_related_related_modal')]//button/span[contains(text(), 'Add Selected Products')]" timeout="30"/> - </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml index b36f67e463a84..8e67610c71358 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml @@ -16,14 +16,14 @@ <description value="Product image should be deleted from all scopes"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-95344"/> - <group value="Catalog"/> + <group value="catalog"/> </annotations> <before> - <!--Create 2 websites (with stores, store views)--> + <!-- login to admin, create default category and product --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <createData entity="_defaultCategory" stepKey="category"/> - <createData entity="_defaultProduct" stepKey="product"> - <requiredEntity createDataKey="category"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> </createData> <!-- Create first custom website, store, store view --> @@ -45,19 +45,19 @@ <!-- Create second custom website, store, store view --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> - <argument name="newWebsiteName" value="{{SecondCustomWebSite.name}}"/> - <argument name="websiteCode" value="{{SecondCustomWebSite.code}}"/> + <argument name="newWebsiteName" value="{{SecondWebsite.name}}"/> + <argument name="websiteCode" value="{{SecondWebsite.code}}"/> </actionGroup> <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createSecondStore"> - <argument name="website" value="{{SecondCustomWebSite.name}}"/> - <argument name="storeGroupName" value="{{SecondCustomStoreGroup.name}}"/> - <argument name="storeGroupCode" value="{{SecondCustomStoreGroup.code}}"/> + <argument name="website" value="{{SecondWebsite.name}}"/> + <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> + <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> </actionGroup> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView"> - <argument name="storeGroup" value="SecondCustomStoreGroup"/> - <argument name="customStore" value="SecondCustomStore"/> + <argument name="storeGroup" value="SecondStoreGroupUnique"/> + <argument name="customStore" value="SecondStoreUnique"/> </actionGroup> </before> @@ -68,22 +68,27 @@ </actionGroup> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> - <argument name="websiteName" value="{{SecondCustomWebSite.name}}"/> + <argument name="websiteName" value="{{SecondWebsite.name}}"/> </actionGroup> - <deleteData createDataKey="category" stepKey="deletePreReqCategory"/> - <deleteData createDataKey="product" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteFirstProduct"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <!--Create product--> + <!-- Open product index page, clear filters and change gridview to default view --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="resetAdminDataGridToDefaultView" stepKey="resetAdminDataGridToDefaultView"/> <!--Open created product--> - <click selector="{{AdminProductGridSection.productGridNameProduct($$product.name$$)}}" stepKey="createdProduct"/> - <waitForPageLoad stepKey="waitForOpenedCreatedProduct"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchProduct"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProductPage"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> <!-- Add image to product --> <actionGroup ref="addProductImage" stepKey="addFirstImageForProduct"> @@ -96,18 +101,20 @@ </actionGroup> <!--"Product in Websites": select both Websites--> - <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite1"> + <actionGroup ref="ProductSetWebsite" stepKey="productSetWebsite1"> <argument name="website" value="CustomWebSite"/> </actionGroup> - <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite2"> - <argument name="website" value="SecondCustomWebSite"/> + <actionGroup ref="ProductSetWebsite" stepKey="productSetWebsite2"> + <argument name="website" value="SecondWebsite"/> </actionGroup> - <!--Go to "Catalog" -> "Products". Open created product--> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoaded"/> - <click selector="{{AdminProductGridSection.productGridNameProduct($$product.name$$)}}" stepKey="openCreatedProduct"/> - <waitForPageLoad stepKey="waitForCreatedProductOpened"/> + <!--Open created product--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchProduct1"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProductPage1"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> <!--Delete Image 1--> <actionGroup ref="RemoveProductImage" stepKey="removeProductImage"/> @@ -127,7 +134,7 @@ <!--Switch to "Store view 2"--> <actionGroup ref="SwitchToTheNewStoreView" stepKey="selectSecondStoreView"> - <argument name="storeViewName" value="SecondCustomStore"/> + <argument name="storeViewName" value="SecondStoreUnique"/> </actionGroup> <!-- Verify that Image 1 is deleted from the Second Store View list --> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 69dd2b20356ff..f85125bcf3291 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -48,22 +48,22 @@ <data key="store_type">store</data> <requiredEntity type="storeGroup">secondStoreGroup</requiredEntity> </entity> - <entity name="staticStore" type="store"> - <data key="name">Second Store View</data> - <data key="code">store123</data> + <entity name="SecondStoreUnique" type="store"> + <data key="name" unique="suffix">Second Store View </data> + <data key="code" unique="suffix">second_store_view_</data> <data key="is_active">1</data> <data key="store_id">null</data> <data key="store_action">add</data> - <data key="store_type">group</data> - <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> + <data key="store_type">store</data> + <requiredEntity type="storeGroup">secondStoreGroup</requiredEntity> </entity> - <entity name="SecondCustomStore" type="store"> - <data key="name" unique="suffix">store</data> - <data key="code" unique="suffix">store</data> + <entity name="staticStore" type="store"> + <data key="name">Second Store View</data> + <data key="code">store123</data> <data key="is_active">1</data> <data key="store_id">null</data> <data key="store_action">add</data> - <data key="store_type">store</data> + <data key="store_type">group</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml index 0137a24a187d4..e45653ca3a796 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml @@ -29,16 +29,18 @@ <data key="store_action">add</data> <data key="store_type">group</data> </entity> + <entity name="SecondStoreGroupUnique" type="group"> + <data key="group_id">null</data> + <data key="name" unique="suffix">Second Store </data> + <data key="code" unique="suffix">second_store_</data> + <var key="root_category_id" entityKey="id" entityType="category"/> + <data key="store_action">add</data> + <data key="store_type">group</data> + </entity> <entity name="staticStoreGroup" type="group"> <data key="name">NewStore</data> <data key="code">Base12</data> <data key="root_category_id">2</data> <data key="website_id">1</data> </entity> - <entity name="SecondCustomStoreGroup" type="group"> - <data key="name" unique="suffix">store</data> - <data key="code" unique="suffix">store</data> - <data key="root_category_id">2</data> - <data key="website_id">1</data> - </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml index 8726bac186f02..32f7305a15e3e 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml @@ -16,9 +16,9 @@ <data key="code" unique="suffix">website</data> <data key="sort_order">2</data> </entity> - <entity name="SecondCustomWebSite" type="website"> - <data key="name" unique="suffix">website</data> - <data key="code" unique="suffix">website</data> - <data key="sort_order">3</data> + <entity name="SecondWebsite" type="website"> + <data key="name" unique="suffix">Second Website </data> + <data key="code" unique="suffix">second_website_</data> + <data key="sort_order">10</data> </entity> </entities> From e8636ef98ec3b747c0773f30bd7adac6153d6bf7 Mon Sep 17 00:00:00 2001 From: prakashpatel07 <prakash.patel@krishtechnolabs.com> Date: Thu, 4 Oct 2018 18:02:24 +0000 Subject: [PATCH 049/240] Add backward compatible into code --- app/code/Magento/Integration/Model/CustomerTokenService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Integration/Model/CustomerTokenService.php b/app/code/Magento/Integration/Model/CustomerTokenService.php index 1433361e3a9b9..296605a9315a5 100644 --- a/app/code/Magento/Integration/Model/CustomerTokenService.php +++ b/app/code/Magento/Integration/Model/CustomerTokenService.php @@ -68,13 +68,13 @@ public function __construct( AccountManagementInterface $accountManagement, TokenCollectionFactory $tokenModelCollectionFactory, CredentialsValidator $validatorHelper, - ManagerInterface $eventManager + ManagerInterface $eventManager = null ) { $this->tokenModelFactory = $tokenModelFactory; $this->accountManagement = $accountManagement; $this->tokenModelCollectionFactory = $tokenModelCollectionFactory; $this->validatorHelper = $validatorHelper; - $this->eventManager = $eventManager; + $this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ManagerInterface::class); } /** From c106a62a03e7e7538da09f2054cbaf124816f018 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Fri, 5 Oct 2018 09:56:25 +0300 Subject: [PATCH 050/240] magento/magento2#14510: Creating custom customer attribute with default value 0 will cause not saving value for customer entity. Remove curly braces. --- app/code/Magento/Downloadable/Setup/UpgradeData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Setup/UpgradeData.php b/app/code/Magento/Downloadable/Setup/UpgradeData.php index 0f509ed32cbf2..aec15ce7f9d90 100644 --- a/app/code/Magento/Downloadable/Setup/UpgradeData.php +++ b/app/code/Magento/Downloadable/Setup/UpgradeData.php @@ -35,7 +35,7 @@ public function __construct(EavSetupFactory $eavSetupFactory) } /** - * {@inheritdoc} + * @inheritdoc */ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) { From a4e59fc137edd5c922126efd8c59914e5b630109 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 5 Oct 2018 10:30:28 +0300 Subject: [PATCH 051/240] MAGETWO-93393: Table rate shipment is taken from wrong website ID when creating an order through the admin --- .../Magento/Sales/Model/AdminOrder/Create.php | 17 ++- .../_files/tablerates_second_website.php | 40 +++++ .../tablerates_second_website_rollback.php | 13 ++ .../Controller/Adminhtml/Order/CreateTest.php | 140 +++++++++++++++++- .../_files/websites_different_countries.php | 14 +- 5 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website.php create mode 100644 dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website_rollback.php diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index c30bb5e328e8b..12d2a5396ddc4 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -16,6 +16,7 @@ use Magento\Quote\Model\Quote\Item; use Magento\Sales\Api\Data\OrderAddressInterface; use Magento\Sales\Model\Order; +use Magento\Store\Model\StoreManagerInterface; /** * Order create model @@ -243,6 +244,11 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ private $dataObjectConverter; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -274,6 +280,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ * @param array $data * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @param ExtensibleDataObjectConverter|null $dataObjectConverter + * @param StoreManagerInterface $storeManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -306,7 +313,8 @@ public function __construct( \Magento\Quote\Model\QuoteFactory $quoteFactory, array $data = [], \Magento\Framework\Serialize\Serializer\Json $serializer = null, - ExtensibleDataObjectConverter $dataObjectConverter = null + ExtensibleDataObjectConverter $dataObjectConverter = null, + StoreManagerInterface $storeManager = null ) { $this->_objectManager = $objectManager; $this->_eventManager = $eventManager; @@ -340,6 +348,7 @@ public function __construct( parent::__construct($data); $this->dataObjectConverter = $dataObjectConverter ?: ObjectManager::getInstance() ->get(ExtensibleDataObjectConverter::class); + $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); } /** @@ -417,7 +426,8 @@ public function setRecollect($flag) /** * Recollect totals for customer cart. - * Set recollect totals flag for quote + * + * Set recollect totals flag for quote. * * @return $this */ @@ -1327,6 +1337,7 @@ protected function _createCustomerForm(\Magento\Customer\Api\Data\CustomerInterf /** * Set and validate Quote address + * * All errors added to _errors * * @param \Magento\Quote\Model\Quote\Address $address @@ -1530,6 +1541,8 @@ public function resetShippingMethod() */ public function collectShippingRates() { + $store = $this->getQuote()->getStore(); + $this->storeManager->setCurrentStore($store); $this->getQuote()->getShippingAddress()->setCollectShippingRates(true); $this->collectRates(); diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website.php new file mode 100644 index 0000000000000..52551e6dc96d8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\ResourceConnection; +use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$website = $websiteRepository->get('test'); + +/** @var ResourceConnection $resource */ +$resource = $objectManager->get(ResourceConnection::class); +$connection = $resource->getConnection(); +$resourceModel = $objectManager->create(Tablerate::class); +$entityTable = $resourceModel->getTable('shipping_tablerate'); +$data = + [ + 'website_id' => $website->getId(), + 'dest_country_id' => 'US', + 'dest_region_id' => 0, + 'dest_zip' => '*', + 'condition_name' => 'package_qty', + 'condition_value' => 1, + 'price' => 20, + 'cost' => 20 + ]; +$connection->query( + "INSERT INTO {$entityTable} (`website_id`, `dest_country_id`, `dest_region_id`, `dest_zip`, `condition_name`," + . "`condition_value`, `price`, `cost`) VALUES (:website_id, :dest_country_id, :dest_region_id, :dest_zip," + . " :condition_name, :condition_value, :price, :cost);", + $data +); diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website_rollback.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website_rollback.php new file mode 100644 index 0000000000000..9606b0eb605fd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/tablerates_second_website_rollback.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$resource = $objectManager->get(\Magento\Framework\App\ResourceConnection::class); +$connection = $resource->getConnection(); +$resourceModel = $objectManager->create(\Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate::class); +$entityTable = $resourceModel->getTable('shipping_tablerate'); +$connection->query("DELETE FROM {$entityTable};"); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php index e071dde26a263..01eb917135730 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php @@ -6,12 +6,26 @@ namespace Magento\Sales\Controller\Adminhtml\Order; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Backend\Model\Session\Quote; +use Magento\Backend\Model\Session\Quote as SessionQuote; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Quote\Model\Quote; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Api\Data\WebsiteInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\ScopeInterface; /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class CreateTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -57,6 +71,57 @@ public function testLoadBlockActionData() } /** + * Tests that shipping method 'Table rates' shows rates according to selected website. + * + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Quote/Fixtures/quote_sec_website.php + * @magentoDataFixture Magento/OfflineShipping/_files/tablerates_second_website.php + * @magentoDbIsolation disabled + */ + public function testLoadBlockShippingMethod() + { + $store = $this->getStore('fixture_second_store'); + + /** @var MutableScopeConfigInterface $mutableScopeConfig */ + $mutableScopeConfig = $this->_objectManager->get(MutableScopeConfigInterface::class); + $mutableScopeConfig->setValue( + 'carriers/tablerate/active', + 1, + ScopeInterface::SCOPE_STORE, + $store->getCode() + ); + $mutableScopeConfig->setValue( + 'carriers/tablerate/condition_name', + 'package_qty', + ScopeInterface::SCOPE_STORE, + $store->getCode() + ); + + $website = $this->getWebsite('test'); + $customer = $this->getCustomer('customer.web@example.com', (int)$website->getId()); + $quote = $this->getQuoteById('0000032134'); + $session = $this->_objectManager->get(SessionQuote::class); + $session->setQuoteId($quote->getId()); + + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue( + [ + 'customer_id' => $customer->getId(), + 'collect_shipping_rates' => 1, + 'store_id' => $store->getId(), + 'json' => true + ] + ); + $this->dispatch('backend/sales/order_create/loadBlock/block/shipping_method'); + $body = $this->getResponse()->getBody(); + $expectedTableRatePrice = '<span class=\"price\">$20.00<\/span>'; + + $this->assertContains($expectedTableRatePrice, $body, ''); + } + + /** + * Tests LoadBlock actions. + * * @dataProvider loadBlockActionsDataProvider */ public function testLoadBlockActions($block, $expected) @@ -80,6 +145,8 @@ public function loadBlockActionsDataProvider() } /** + * Tests action items. + * * @magentoDataFixture Magento/Catalog/_files/product_simple.php */ public function testLoadBlockActionItems() @@ -153,6 +220,8 @@ public function testIndexAction() } /** + * Tests ACL. + * * @param string $actionName * @param boolean $reordered * @param string $expectedResult @@ -162,7 +231,7 @@ public function testIndexAction() */ public function testGetAclResource($actionName, $reordered, $expectedResult) { - $this->_objectManager->get(Quote::class)->setReordered($reordered); + $this->_objectManager->get(SessionQuote::class)->setReordered($reordered); $orderController = $this->_objectManager->get( \Magento\Sales\Controller\Adminhtml\Order\Stub\OrderCreateStub::class ); @@ -251,7 +320,7 @@ public function testSyncBetweenQuoteAddresses() $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); $quote = $quoteRepository->getActiveForCustomer($customer->getId()); - $session = $this->_objectManager->get(Quote::class); + $session = $this->_objectManager->get(SessionQuote::class); $session->setQuoteId($quote->getId()); $data = [ @@ -286,4 +355,69 @@ public function testSyncBetweenQuoteAddresses() self::assertEquals($data['city'], $shippingAddress->getCity()); self::assertEquals($data['street'], $shippingAddress->getStreet()); } + + /** + * Gets quote entity by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuoteById(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $repository */ + $repository = $this->_objectManager->get(CartRepositoryInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Gets website entity. + * + * @param string $code + * @return WebsiteInterface + * @throws NoSuchEntityException + */ + private function getWebsite(string $code): WebsiteInterface + { + /** @var WebsiteRepositoryInterface $repository */ + $repository = $this->_objectManager->get(WebsiteRepositoryInterface::class); + return $repository->get($code); + } + + /** + * Gets customer entity. + * + * @param string $email + * @param int $websiteId + * @return CustomerInterface + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getCustomer(string $email, int $websiteId): CustomerInterface + { + /** @var CustomerRepositoryInterface $repository */ + $repository = $this->_objectManager->get(CustomerRepositoryInterface::class); + return $repository->get($email, $websiteId); + } + + /** + * Gets store by code. + * + * @param string $code + * @return StoreInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getStore(string $code): StoreInterface + { + /** @var StoreRepositoryInterface $repository */ + $repository = $this->_objectManager->get(StoreRepositoryInterface::class); + return $repository->get($code); + } } diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/websites_different_countries.php b/dev/tests/integration/testsuite/Magento/Store/_files/websites_different_countries.php index 6a00031434b3d..dd58f4eb7a984 100644 --- a/dev/tests/integration/testsuite/Magento/Store/_files/websites_different_countries.php +++ b/dev/tests/integration/testsuite/Magento/Store/_files/websites_different_countries.php @@ -10,6 +10,7 @@ use Magento\Store\Model\Store; use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndex; use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Store\Model\Group; $objectManager = Bootstrap::getObjectManager(); //Creating second website with a store. @@ -21,12 +22,23 @@ $website->setData([ 'code' => 'test', 'name' => 'Test Website', - 'default_group_id' => '1', 'is_default' => '0', ]); $website->save(); } +/** + * @var Group $storeGroup + */ +$storeGroup = $objectManager->create(Group::class); +$storeGroup->setCode('some_group') + ->setName('custom store group') + ->setWebsite($website); +$storeGroup->save($storeGroup); + +$website->setDefaultGroupId($storeGroup->getId()); +$website->save($website); + $websiteId = $website->getId(); $store = $objectManager->create(Store::class); $store->load('fixture_second_store', 'code'); From 141b0e4be95ab843a7659759e79b36654930a004 Mon Sep 17 00:00:00 2001 From: prakashpatel07 <prakash.patel@krishtechnolabs.com> Date: Fri, 5 Oct 2018 07:44:30 +0000 Subject: [PATCH 052/240] Fixed Code Sniffer issue. --- app/code/Magento/Integration/Model/CustomerTokenService.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Integration/Model/CustomerTokenService.php b/app/code/Magento/Integration/Model/CustomerTokenService.php index 296605a9315a5..adacf5ebacf71 100644 --- a/app/code/Magento/Integration/Model/CustomerTokenService.php +++ b/app/code/Magento/Integration/Model/CustomerTokenService.php @@ -74,7 +74,8 @@ public function __construct( $this->accountManagement = $accountManagement; $this->tokenModelCollectionFactory = $tokenModelCollectionFactory; $this->validatorHelper = $validatorHelper; - $this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ManagerInterface::class); + $this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(ManagerInterface::class); } /** From d173ee28ca4e1852dec7b9e4e8b95d67c665cfb3 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 5 Oct 2018 11:04:17 +0300 Subject: [PATCH 053/240] MAGETWO-73449: Changes in default scope not effect product images in other scopes --- .../Mftf/ActionGroup/AdminProductActionGroup.xml | 6 +++--- .../Mftf/Section/AdminProductContentSection.xml | 15 --------------- .../Test/AdminRemoveImageAffectsAllScopesTest.xml | 13 +++++++------ 3 files changed, 10 insertions(+), 24 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index ea762ca0280a3..f3fca85c291d6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -109,10 +109,10 @@ <arguments> <argument name="website" defaultValue="DefaultWebsite"/> </arguments> - <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ScrollToWebsites"/> - <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ClickTpOpenProductInWebsite"/> + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="clickToOpenProductInWebsite"/> <waitForPageLoad stepKey="waitForPageOpened"/> - <click selector="{{ProductInWebsitesSection.website(website.name)}}" stepKey="SelectWebsite"/> + <click selector="{{ProductInWebsitesSection.website(website.name)}}" stepKey="selectWebsite"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> <waitForPageLoad time='60' stepKey="waitForPageOpened1"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml deleted file mode 100644 index ab9fe9616f38a..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductContentSection.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="AdminProductContentSection"> - <element name="sectionHeaderIfNotShowing" type="button" selector="div[data-index='content'] div[class*='_hide']]"/> - <element name="pageHeader" type="text" selector=".page-header.row"/> - </section> -</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml index 8e67610c71358..197de39ead756 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml @@ -73,15 +73,16 @@ <deleteData createDataKey="createCategory" stepKey="deletePreReqCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteFirstProduct"/> + + <!-- Open product index page, clear filters and change gridview to default view --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="resetAdminDataGridToDefaultView" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="logout" stepKey="logout"/> </after> - <!-- Open product index page, clear filters and change gridview to default view --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> - <actionGroup ref="resetAdminDataGridToDefaultView" stepKey="resetAdminDataGridToDefaultView"/> - <!--Open created product--> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchProduct"> <argument name="product" value="$$createProduct$$"/> From 69368225c8c67b94930163ef621a72b17f081150 Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Fri, 5 Oct 2018 11:22:51 +0300 Subject: [PATCH 054/240] MAGETWO-95306: Url-rewrites for product in anchor categories --- .../Mftf/Page/AdminUrlRewriteIndexPage.xml | 14 ++++ .../Section/AdminUrlRewriteIndexSection.xml | 15 ++++ ...writesForProductInAnchorCategoriesTest.xml | 80 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteIndexPage.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteIndexPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteIndexPage.xml new file mode 100644 index 0000000000000..9524e41dc7c9e --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteIndexPage.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminUrlRewriteIndexPage" url="admin/url_rewrite/index/" area="admin" module="Magento_UrlRewrite"> + <section name="AdminUrlRewriteIndexSection"/> + </page> +</pages> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml new file mode 100644 index 0000000000000..455748a0da534 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminUrlRewriteIndexSection"> + <element name="requestPathFilter" type="input" selector="#urlrewriteGrid_filter_request_path"/> + <element name="requestPathColumnValue" type="text" selector="//*[@id='urlrewriteGrid']//tbody//td[@data-column='request_path' and normalize-space(.)='{{columnValue}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml new file mode 100644 index 0000000000000..ec1aef7da4f16 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="AdminUrlRewritesForProductInAnchorCategoriesTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories"/> + <title value="Url-rewrites for product in anchor categories"/> + <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-76098"/> + <group value="urlRewrite"/> + </annotations> + + <!-- Preconditions--> + <!-- Create 3 categories --> + <before> + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory2"/> + </createData> + <!-- Create Simple product 1 and assign it to Category 3 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Steps --> + <!-- 1. Log in to Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeValue2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$/$$simpleSubCategory2.custom_attributes[url_key]$$/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeValue3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$/$$simpleSubCategory2.custom_attributes[url_key]$$/$$simpleSubCategory3.custom_attributes[url_key]$$/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeValue4"/> + + <!-- 3. Edit Category 1 for DEFAULT Store View: --> + <actionGroup ref="switchCategoryStoreView" stepKey="switchStoreView"> + <argument name="store" value="_defaultStore.name"/> + <argument name="catName" value="$$simpleSubCategory1.name$$"/> + </actionGroup> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> + <uncheckOption selector="{{AdminCategorySEOSection.urlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$$simpleSubCategory1.custom_attributes[url_key]$$-new" stepKey="changeURLKey"/> + <checkOption selector="{{AdminCategorySEOSection.urlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> + <!-- 4. Save Category 1 --> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> + + <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="inputProductName2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$/$$simpleSubCategory2.custom_attributes[url_key]$$/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$/$$simpleSubCategory2.custom_attributes[url_key]$$/$$simpleSubCategory3.custom_attributes[url_key]$$/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$-new/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$-new/$$simpleSubCategory2.custom_attributes[url_key]$$/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue6"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$simpleSubCategory1.custom_attributes[url_key]$$-new/$$simpleSubCategory2.custom_attributes[url_key]$$/$$simpleSubCategory3.custom_attributes[url_key]$$/$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue7"/> + </test> +</tests> From ee004f432ae187b911d76e10ac041154fe01b67e Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Wed, 26 Sep 2018 16:02:54 +0300 Subject: [PATCH 055/240] MAGETWO-93985: Magnifier does not work with Windows Chrome/FF --- lib/web/magnifier/magnify.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/web/magnifier/magnify.js b/lib/web/magnifier/magnify.js index 1fb73ea28bff1..9d673092b806c 100644 --- a/lib/web/magnifier/magnify.js +++ b/lib/web/magnifier/magnify.js @@ -35,13 +35,6 @@ define([ allowZoomOut = false, allowZoomIn = true; - if (isTouchEnabled) { - $(element).on('fotorama:showend fotorama:load', function () { - $(magnifierSelector).remove(); - $(magnifierZoomSelector).remove(); - }); - } - (function () { var style = document.documentElement.style, transitionEnabled = style.transition !== undefined || From 7af045a4fb06e3366897c4aa23c7fe0357a1fb6e Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Fri, 5 Oct 2018 15:43:11 +0300 Subject: [PATCH 056/240] MAGETWO-95306: Url-rewrites for product in anchor categories --- .../Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml index ec1aef7da4f16..7143c6ac52572 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -61,6 +61,7 @@ <uncheckOption selector="{{AdminCategorySEOSection.urlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$$simpleSubCategory1.custom_attributes[url_key]$$-new" stepKey="changeURLKey"/> <checkOption selector="{{AdminCategorySEOSection.urlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> + <!-- 4. Save Category 1 --> <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> From 8de5d772ee80dee5b34cd97aa1231371416eebdd Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Fri, 5 Oct 2018 16:59:38 +0300 Subject: [PATCH 057/240] MAGETWO-95306: Url-rewrites for product in anchor categories --- .../Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml index 7143c6ac52572..6d693893bdd5f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -68,6 +68,7 @@ <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> + <waitForPageLoad stepKey="waitForPageLoad"/> <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="inputProductName2"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue1"/> From 3fc107ad5426ccf73566d562cf643c9e94e27d5b Mon Sep 17 00:00:00 2001 From: Lewis Voncken <lewis@experius.nl> Date: Sat, 6 Oct 2018 18:12:27 +0200 Subject: [PATCH 058/240] [BACKPORT] type casted $qty to float in \Magento\Catalog\Model\Product::setQty() --- app/code/Magento/Catalog/Model/Product.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index e4dacf275fc9b..b5e0a2c0f48b6 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -972,7 +972,7 @@ public function afterSave() public function setQty($qty) { if ($this->getData('qty') != $qty) { - $this->setData('qty', $qty); + $this->setData('qty', (float)$qty); $this->reloadPriceInfo(); } return $this; @@ -985,7 +985,7 @@ public function setQty($qty) */ public function getQty() { - return $this->getData('qty'); + return (float)$this->getData('qty'); } /** From c909461833a86321ab2cfa921217fccc5cd6f0b8 Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Mon, 8 Oct 2018 14:55:08 +0300 Subject: [PATCH 059/240] MAGETWO-95306: Url-rewrites for product in anchor categories --- .../Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml index 6d693893bdd5f..363e6491a25f0 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -68,7 +68,7 @@ <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="inputProductName2"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('$$createSimpleProduct.custom_attributes[url_key]$$.html')}}" stepKey="seeInListValue1"/> From 03698da2ebc0cdb02ec1a0c4e65eb00097c61d42 Mon Sep 17 00:00:00 2001 From: Thiago Lima <thiagolimaufrj@gmail.com> Date: Mon, 8 Oct 2018 14:29:53 +0200 Subject: [PATCH 060/240] fix Fatal Error when save configurable product in Magento 2.2.5 #18082 --- .../Product/Initialization/Helper/Plugin/Configurable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php index 5cd8b6a7d0b95..556939ec112f1 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php @@ -158,7 +158,7 @@ protected function getVariationMatrix() $configurableMatrix = json_decode($configurableMatrix, true); foreach ($configurableMatrix as $item) { - if ($item['newProduct']) { + if (isset($item['newProduct'])) { $result[$item['variationKey']] = $this->mapData($item); if (isset($item['qty'])) { From 1ac0fa657ef1ba46b3738892874a25396bf2f30f Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Mon, 8 Oct 2018 16:29:26 +0300 Subject: [PATCH 061/240] MAGETWO-95333: Update price in shopping cart after product save --- .../Mftf/Section/AdminMainActionsSection.xml | 4 +- ...ckoutFillingShippingSectionActionGroup.xml | 8 +-- .../Mftf/Section/CheckoutPaymentSection.xml | 9 +-- ...riceInShoppingCartAfterProductSaveTest.xml | 66 +++++++++++++++++++ .../AdminConfigCustomerActionGroup.xml | 19 ++++++ .../Mftf/Page/AdminCustomerConfigPage.xml | 13 ++++ .../Section/AdminCustomerConfigSection.xml | 13 ++++ .../Mftf/Test/AdminCreateCustomerTest.xml | 5 +- 8 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminConfigCustomerActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerConfigPage.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml index c56a8768b0adf..bba375c2d6bfd 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml @@ -7,9 +7,9 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMainActionsSection"> - <element name="save" type="button" selector="#save"/> + <element name="save" type="button" selector="#save" timeout="30"/> <element name="delete" type="button" selector="#delete"/> <element name="add" type="button" selector="#add" timeout="30"/> </section> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml index 9e66e742229bd..7ac23613f34b2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml @@ -7,12 +7,12 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Guest checkout filling shipping section --> <actionGroup name="GuestCheckoutFillingShippingSectionActionGroup"> <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> + <argument name="customerVar" defaultValue="CustomerEntityOne"/> + <argument name="customerAddressVar" defaultValue="CustomerAddressSimple"/> </arguments> <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> @@ -30,4 +30,4 @@ <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask1"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index f9e27ef36c715..c73001cfe6832 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutPaymentSection"> <element name="isPaymentSection" type="text" selector="//*[@class='opc-progress-bar']/li[contains(@class, '_active') and span[contains(.,'Review & Payments')]]"/> <element name="availablePaymentSolutions" type="text" selector="#checkout-payment-method-load>div>div>div:nth-child(2)>div.payment-method-title.field.choice"/> @@ -25,12 +25,13 @@ <element name="cartItems" type="text" selector=".minicart-items"/> <element name="billingAddress" type="text" selector="div.billing-address-details"/> <element name="placeOrder" type="button" selector="button.action.primary.checkout" timeout="30"/> - <element name="productOptionsByProductItemPrice" type="text" selector="//div[@class='product-item-inner']//div[@class='subtotal']//span[@class='price'][contains(.,'{{var1}}')]//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true"/> - <element name="productOptionsActiveByProductItemPrice" type="text" selector="//div[@class='subtotal']//span[@class='price'][contains(.,'{{var1}}')]//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true"/> + <element name="productOptionsByProductItemPrice" type="text" selector="//div[@class='product-item-inner']//div[@class='subtotal']//span[@class='price'][contains(.,'{{price}}')]//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true"/> + <element name="productOptionsActiveByProductItemPrice" type="text" selector="//div[@class='subtotal']//span[@class='price'][contains(.,'{{price}}')]//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true"/> + <element name="productItemPriceByName" type="text" selector="//div[@class='product-item-details'][contains(., '{{ProductName}}')]//span[@class='price']" parameterized="true"/> <element name="productOptionsActiveByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true" /> <element name="productOptionsByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true" /> <element name="cartItemsArea" type="button" selector=".items-in-cart"/> - <element name="cartItemsAreaActive" type="textarea" selector=".items-in-cart.active"/> + <element name="cartItemsAreaActive" type="textarea" selector=".items-in-cart.active" timeout="30"/> <element name="paymentSectionTitle" type="text" selector="#checkout-payment-method-load .step-title" /> <element name="paymentMethodTitle" type="text" selector=".payment-method-title span" /> <element name="checkMoneyOrderPayment" type="radio" selector="#checkmo.radio" timeout="30"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml new file mode 100644 index 0000000000000..cda8039e505a8 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdatePriceInShoppingCartAfterProductSaveTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via the Storefront"/> + <title value="Update price in shopping cart after product save"/> + <description value="Price in shopping cart should be updated after product save with changed price"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-77832"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="SimpleProduct3" stepKey="createSimpleProduct"> + <field key="price">100</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SetCustomerDataLifetimeActionGroup" stepKey="setCustomerDataLifetime"> + <argument name="minutes" value="1"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <actionGroup ref="SetCustomerDataLifetimeActionGroup" stepKey="setDefaultCustomerDataLifetime"/> + <magentoCLI command="indexer:reindex customer_grid" stepKey="reindex"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Go to product page--> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> + + <!--Add Product to Shopping Cart--> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Go to Checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShipping"/> + + <!--Check price--> + <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsAreaActive}}" visible="false" stepKey="openItemProductBlock"/> + <see userInput="$100.00" selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" stepKey="checkSummarySubtotal"/> + <see userInput="$100.00" selector="{{CheckoutPaymentSection.productItemPriceByName($$createSimpleProduct.name$$)}}" stepKey="checkItemPrice"/> + + <!--Edit product price via admin panel--> + <openNewTab stepKey="openNewTab"/> + <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <fillField userInput="120" selector="{{AdminProductFormSection.productPrice}}" stepKey="setNewPrice"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <closeTab stepKey="closeTab"/> + + <!--Check price--> + <reloadPage stepKey="reloadPage"/> + <waitForPageLoad stepKey="waitForCheckoutPageReload"/> + <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsAreaActive}}" visible="false" stepKey="openItemProductBlock1"/> + <see userInput="$120.00" selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" stepKey="checkSummarySubtotal1"/> + <see userInput="$120.00" selector="{{CheckoutPaymentSection.productItemPriceByName($$createSimpleProduct.name$$)}}" stepKey="checkItemPrice1"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminConfigCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminConfigCustomerActionGroup.xml new file mode 100644 index 0000000000000..3fc25ecf57faa --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminConfigCustomerActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetCustomerDataLifetimeActionGroup"> + <arguments> + <argument name="minutes" defaultValue="60" type="string"/> + </arguments> + <amOnPage url="{{AdminCustomerConfigPage.url('#customer_online_customers-link')}}" stepKey="openCustomerConfigPage"/> + <fillField userInput="{{minutes}}" selector="{{AdminCustomerConfigSection.customerDataLifetime}}" stepKey="fillCustomerDataLifetime"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerConfigPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerConfigPage.xml new file mode 100644 index 0000000000000..3cf8490ec4af1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerConfigPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCustomerConfigPage" url="admin/system_config/edit/section/customer/{{tabLink}}" area="admin" parameterized="true" module="Magento_Customer"> + <section name="AdminCustomerConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml new file mode 100644 index 0000000000000..33696fbd616e3 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCustomerConfigSection"> + <element name="customerDataLifetime" type="input" selector="#customer_online_customers_section_data_lifetime"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml index e5dc187e8ab7a..8e4d69e6a270d 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateCustomerTest"> <annotations> <features value="Customer Creation"/> @@ -19,6 +19,9 @@ <group value="customer"/> <group value="create"/> </annotations> + <before> + <magentoCLI command="indexer:reindex customer_grid" stepKey="reindexCustomerGrid"/> + </before> <after> <actionGroup ref="logout" stepKey="logout"/> </after> From f59bc17a78570e4c62015b558b24b42094a6e17c Mon Sep 17 00:00:00 2001 From: shubham sharma <shubhamsharma@shubhams-MacBook-Air.local> Date: Tue, 9 Oct 2018 01:29:02 +0530 Subject: [PATCH 062/240] fixed issue #18082 --- .../adminhtml/web/js/variations/variations.js | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js index be290e49a43c3..4e49ac4649ac8 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js @@ -406,17 +406,20 @@ define([ * - configurable-matrix-serialized; * - associated_product_ids_serialized. */ - serializeData: function () { - this.source.data['configurable-matrix-serialized'] = - JSON.stringify(this.source.data['configurable-matrix']); - - delete this.source.data['configurable-matrix']; - - this.source.data['associated_product_ids_serialized'] = - JSON.stringify(this.source.data['associated_product_ids']); - - delete this.source.data['associated_product_ids']; - }, + serializeData: function () { + this.source.data['configurable-matrix-serialized'] = + JSON.stringify(this.source.data['configurable-matrix']); + if ($('.admin__field-error').length===0) { + delete this.source.data['configurable-matrix']; + } + + + this.source.data['associated_product_ids_serialized'] = + JSON.stringify(this.source.data['associated_product_ids']); + if ($('.admin__field-error').length===0) { + delete this.source.data['associated_product_ids']; + } + }, /** * Check for newly added attributes From 448c58d0679b1c717e3c0c1523dcd49c0698d9be Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 9 Oct 2018 11:46:08 +0300 Subject: [PATCH 063/240] MAGETWO-93035: Removed products are still exist in wishlist_item_option table in database --- .../Magento/Wishlist/Setup/UpgradeSchema.php | 44 +++++++++++++++++++ app/code/Magento/Wishlist/etc/module.xml | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Wishlist/Setup/UpgradeSchema.php diff --git a/app/code/Magento/Wishlist/Setup/UpgradeSchema.php b/app/code/Magento/Wishlist/Setup/UpgradeSchema.php new file mode 100644 index 0000000000000..76c4000c9cec8 --- /dev/null +++ b/app/code/Magento/Wishlist/Setup/UpgradeSchema.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Wishlist\Setup; + +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\SchemaSetupInterface; +use Magento\Framework\Setup\UpgradeSchemaInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; + +/** + * Upgrade Schema + */ +class UpgradeSchema implements UpgradeSchemaInterface +{ + /** + * @inheritdoc + */ + public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context) + { + $setup->startSetup(); + $connection = $setup->getConnection(); + if (version_compare($context->getVersion(), '2.0.2', '<')) { + $connection->addForeignKey( + $connection->getForeignKeyName( + $setup->getTable('wishlist_item_option'), + 'product_id', + $setup->getTable('catalog_product_entity'), + 'entity_id' + ), + $setup->getTable('wishlist_item_option'), + 'product_id', + $setup->getTable('catalog_product_entity'), + 'entity_id', + AdapterInterface::FK_ACTION_CASCADE, + true + ); + } + $setup->endSetup(); + } +} diff --git a/app/code/Magento/Wishlist/etc/module.xml b/app/code/Magento/Wishlist/etc/module.xml index e7626f504e1f1..ade606be9e086 100644 --- a/app/code/Magento/Wishlist/etc/module.xml +++ b/app/code/Magento/Wishlist/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Wishlist" setup_version="2.0.1"> + <module name="Magento_Wishlist" setup_version="2.0.2"> <sequence> <module name="Magento_Customer"/> <module name="Magento_Catalog"/> From 838023b065e3c3c3e4ee490756ec6a725107245f Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 9 Oct 2018 13:15:30 +0300 Subject: [PATCH 064/240] MAGETWO-93035: Removed products are still exist in wishlist_item_option table in database --- app/code/Magento/Wishlist/Setup/UpgradeSchema.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Wishlist/Setup/UpgradeSchema.php b/app/code/Magento/Wishlist/Setup/UpgradeSchema.php index 76c4000c9cec8..20297da771a8e 100644 --- a/app/code/Magento/Wishlist/Setup/UpgradeSchema.php +++ b/app/code/Magento/Wishlist/Setup/UpgradeSchema.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Wishlist\Setup; From a57d8285b2777a7078317156d3ed7b63973214a9 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Tue, 9 Oct 2018 06:47:42 -0700 Subject: [PATCH 065/240] MAGETWO-95173: Sorting by price for Configurable with Catalog Rule applied --- .../AdminProductAttributeActionGroup.xml | 33 ++++ .../StorefrontCategoryActionGroup.xml | 13 ++ .../Test/Mftf/Data/FrontendLabelData.xml | 4 + .../AdminCreateProductAttributeSection.xml | 4 + .../Section/StorefrontCategoryMainSection.xml | 1 + .../CatalogPriceRuleActionGroup.xml | 14 ++ .../Test/Mftf/Data/CatalogRuleData.xml | 13 ++ ...AdminCatalogPriceRuleConditionsSection.xml | 18 ++ .../AdminCatalogPriceRuleGridSection.xml | 1 + .../Section/AdminCatalogPriceRuleSection.xml | 1 + .../Data/ConfigurableProductOptionData.xml | 7 + .../Test/Mftf/Data/ValueIndexData.xml | 3 + ...ConfigurableWithCatalogRuleAppliedTest.xml | 159 ++++++++++++++++++ 13 files changed, 271 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml create mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..22d3f656e0e0c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="navigateToEditProductAttribute"> + <arguments> + <argument name="ProductAttribute" type="string"/> + </arguments> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.gridFilterFrontEndLabel}}" userInput="{{ProductAttribute}}" stepKey="navigateToAttributeEditPage1" /> + <click selector="{{AdminProductAttributeGridSection.search}}" stepKey="navigateToAttributeEditPage2" /> + <waitForPageLoad stepKey="waitForPageLoad" /> + <click selector="{{AdminProductAttributeGridSection.firstRow}}" stepKey="navigateToAttributeEditPage3" /> + <waitForPageLoad stepKey="waitForPageLoad2" /> + </actionGroup> + <actionGroup name="changeUseForPromoRuleConditionsProductAttribute"> + <arguments> + <argument name="option" type="string"/> + </arguments> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStoreFrontPropertiesTab"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="{{option}}" stepKey="changeOption"/> + <click selector="{{AttributePropertiesSection.save}}" stepKey="saveAttribute"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the product attribute." stepKey="successMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index 1f9bd218b1111..17b569c909d18 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -35,4 +35,17 @@ <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="switchCategoryViewToListMode"/> <seeElement selector="{{StorefrontCategoryMainSection.categoryTitle}}" stepKey="assertCategoryTitle"/> </actionGroup> + + <!-- Go to storefront category product page by given parameters --> + <actionGroup name="GoToStorefrontCategoryPageByParameters"> + <arguments> + <argument name="category" type="string"/> + <argument name="mode" type="string"/> + <argument name="sortBy" type="string" defaultValue="position"/> + <argument name="sort" type="string" defaultValue="asc"/> + </arguments> + <!-- Go to storefront category page --> + <amOnPage url="{{StorefrontCategoryPage.url(category)}}?product_list_mode={{mode}}&product_list_order={{sortBy}}&product_list_dir={{sort}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml index 2423383bc19f7..6c3d04970fd32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml @@ -12,4 +12,8 @@ <data key="store_id">0</data> <data key="label" unique="suffix">attribute</data> </entity> + <entity name="ProductAttributeFrontendLabelThree" type="FrontendLabel"> + <data key="store_id">0</data> + <data key="label" unique="suffix">attributeThree</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index e5e37b59af359..607dabe6757f2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -12,4 +12,8 @@ <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> <element name="Save" type="button" selector="#save"/> </section> + <section name="StorefrontPropertiesSection"> + <element name="StoreFrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> + <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> + </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index f793cd14441f7..00e819b5e9cf7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -21,5 +21,6 @@ <element name="productsList" type="text" selector="//ol[@class='products list items product-items']"/> <element name="categoryPageProductImagePlaceholderSmall" type="text" selector=".products-grid img[src*='placeholder/small_image.jpg']"/> <element name="categoryPageProductImage" type="text" selector=".products-grid img[src*='/{{var1}}']" parameterized="true"/> + <element name="lineProductName" type="text" selector=".products.list.items.product-items li:nth-of-type({{line}}) .product-item-link" timeout="30" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index 13569632ab5d8..cd6b18adf0536 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -56,4 +56,18 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear2"/> <see userInput="You deleted the rule." stepKey="verifyRuleIsDeleted"/> </actionGroup> + <!--Add Catalog Rule Condition With product SKU--> + <actionGroup name="newCatalogPriceRuleByUIWithConditionIsSKU" extends="CreateCatalogPriceRule"> + <arguments> + <argument name="productSku"/> + </arguments> + <click selector="{{AdminCatalogPriceRuleSection.conditionsTab}}" after="discardSubsequentRules" stepKey="openConditionsTab"/> + <waitForPageLoad after="openConditionsTab" stepKey="waitForConditionTabOpened"/> + <click selector="{{AdminCatalogPriceRuleConditionsSection.newCondition}}" after="waitForConditionTabOpened" stepKey="addNewCondition"/> + <selectOption selector="{{AdminCatalogPriceRuleConditionsSection.conditionSelect('1')}}" userInput="Magento\CatalogRule\Model\Rule\Condition\Product|sku" after="addNewCondition" stepKey="selectTypeCondition"/> + <waitForPageLoad after="selectTypeCondition" stepKey="waitForConditionChosed"/> + <click selector="{{AdminCatalogPriceRuleConditionsSection.targetEllipsis('1')}}" after="waitForConditionChosed" stepKey="clickEllipsis"/> + <fillField selector="{{AdminCatalogPriceRuleConditionsSection.targetInput('1', '1')}}" userInput="{{productSku}}" after="clickEllipsis" stepKey="fillProductSku"/> + <click selector="{{AdminCatalogPriceRuleConditionsSection.applyButton('1', '1')}}" after="fillProductSku" stepKey="clickApply"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml index 1cfd8446dbe50..5b394ba4a7bf5 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml @@ -21,4 +21,17 @@ <data key="simple_action">by_percent</data> <data key="discount_amount">10</data> </entity> + <entity name="CatalogRuleByPercentWith96Amount" type="catalogRule"> + <data key="name" unique="suffix">CatalogPriceRule</data> + <data key="description">Catalog Price Rule Description</data> + <data key="is_active">1</data> + <array key="groups"> + <item>NOT LOGGED IN</item> + </array> + <array key="websites"> + <item>Main Website</item> + </array> + <data key="simple_action">by_percent</data> + <data key="discount_amount">96</data> + </entity> </entities> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml new file mode 100644 index 0000000000000..b1224ea8f4d6b --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminCatalogPriceRuleConditionsSection"> + <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child"/> + <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true"/> + <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> + <element name="targetInput" type="input" selector="input#conditions__{{var1}}--{{var2}}__value" parameterized="true"/> + <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml index fc502caf2d8e6..f7e8fda942c23 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml @@ -10,5 +10,6 @@ <section name="AdminCatalogPriceRuleGridSection"> <element name="filterByRuleName" type="input" selector="#promo_catalog_grid_filter_name"/> <element name="attribute" type="text" selector="//td[contains(text(), '{{arg}}')]" parameterized="true"/> + <element name="applyRulesButton" type="button" selector="#apply_rules"/> </section> </sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml index ca1f499db64fe..2b6662afaa058 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml @@ -11,6 +11,7 @@ <section name="AdminCatalogPriceRuleSection"> <element name="saveAndApply" type="button" selector="#save_and_apply" timeout="30"/> <element name="saveAndContinue" type="button" selector="#save_and_continue" timeout="30"/> + <element name="save" type="button" selector="#save" timeout="30"/> <element name="ruleName" type="input" selector="[name='name']"/> <element name="description" type="textarea" selector="[name='description']"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml index 21dcf998a6399..969bb3d45258d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml @@ -14,4 +14,11 @@ <requiredEntity type="ValueIndex">ValueIndex1</requiredEntity> <requiredEntity type="ValueIndex">ValueIndex2</requiredEntity> </entity> + <entity name="ConfigurableProductThreeOptions" type="ConfigurableProductOption"> + <var key="attribute_id" entityKey="attribute_id" entityType="ProductAttribute" /> + <data key="label">option</data> + <requiredEntity type="ValueIndex">ValueIndex1</requiredEntity> + <requiredEntity type="ValueIndex">ValueIndex2</requiredEntity> + <requiredEntity type="ValueIndex">ValueIndex3</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml index 54d489a446fd7..9ada1fc97471a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml @@ -14,4 +14,7 @@ <entity name="ValueIndex2" type="ValueIndex"> <var key="value_index" entityKey="value" entityType="ProductAttributeOption"/> </entity> + <entity name="ValueIndex3" type="ValueIndex"> + <var key="value_index" entityKey="value" entityType="ProductAttributeOption"/> + </entity> </entities> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml new file mode 100644 index 0000000000000..a769172933cdb --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml @@ -0,0 +1,159 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontSortingByPriceForConfigurableProductWithCatalogRuleAppliedTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View soting by price in storefront"/> + <title value="Sorting by price for Configurable with Catalog Rule applied"/> + <description value="Sort by price should be correct if the apply Catalog Rule to child product of configurable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-76081"/> + <group value="configurable_product"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">5.00</field> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">10.00</field> + </createData> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="ProductAttributeOption3" stepKey="createConfigProductAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <field key="price">15.00</field> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <field key="price">20.00</field> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + <field key="price">25.00</field> + </createData> + <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--SKU Product Attribute is enabled for Promo Rule Conditions--> + <actionGroup ref="navigateToEditProductAttribute" stepKey="navigateToSkuProductAttribute"> + <argument name="ProductAttribute" value="sku"/> + </actionGroup> + <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToYes"> + <argument name="option" value="Yes"/> + </actionGroup> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!--SKU Product Attribute is disable for Promo Rule Conditions--> + <actionGroup ref="navigateToEditProductAttribute" stepKey="navigateToSkuProductAttribute"> + <argument name="ProductAttribute" value="sku"/> + </actionGroup> + <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToNo"> + <argument name="option" value="No"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + <!--Open category with products and Sort by price desc--> + <actionGroup ref="GoToStorefrontCategoryPageByParameters" stepKey="goToStorefrontCategoryPage"> + <argument name="category" value="$$createCategory.custom_attributes[url_key]$$"/> + <argument name="mode" value="grid"/> + <argument name="sortBy" value="price"/> + <argument name="sort" value="desc"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.lineProductName('1')}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProduct"/> + <see selector="{{StorefrontCategoryMainSection.lineProductName('2')}}" userInput="$$createSimpleProduct2.name$$" stepKey="seeSimpleProductTwo"/> + <see selector="{{StorefrontCategoryMainSection.lineProductName('3')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct"/> + + <!--Create and apply catalog price rule--> + <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsSKU" stepKey="createCatalogPriceRule"> + <argument name="catalogRule" value="CatalogRuleByPercentWith96Amount" /> + <argument name="productSku" value="$$createConfigChildProduct3.sku$$" /> + </actionGroup> + <click selector="{{AdminCatalogPriceRuleGridSection.applyRulesButton}}" stepKey="clickApplyRules"/> + + <magentoCLI command="indexer:reindex" stepKey="reindex1"/> + <magentoCLI command="cache:flush" stepKey="flushCache1"/> + + <!--Reopen category with products and Sort by price desc--> + <actionGroup ref="GoToStorefrontCategoryPageByParameters" stepKey="goToStorefrontCategoryPage2"> + <argument name="category" value="$$createCategory.custom_attributes[url_key]$$"/> + <argument name="mode" value="grid"/> + <argument name="sortBy" value="price"/> + <argument name="sort" value="desc"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.lineProductName('1')}}" userInput="$$createSimpleProduct2.name$$" stepKey="seeSimpleProductTwo2"/> + <see selector="{{StorefrontCategoryMainSection.lineProductName('2')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct2"/> + <see selector="{{StorefrontCategoryMainSection.lineProductName('3')}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProduct2"/> + + <!-- Delete the rule --> + <actionGroup ref="RemoveCatalogPriceRule" stepKey="deletePriceRule"> + <argument name="ruleName" value="CatalogRuleByPercentWith96Amount.name" /> + </actionGroup> + </test> +</tests> From b56f1f34b9401c43daf92821099f2c26cb1605b4 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 9 Oct 2018 16:55:48 +0300 Subject: [PATCH 066/240] MAGETWO-93183: Quans: How to bulk updated Set Product as New to No --- .../Magento/Backend/Block/Widget/Form.php | 47 +++--- .../Widget/Form/Element/ElementCreator.php | 135 ++++++++++++++++++ .../Edit/Action/Attribute/Tab/Attributes.php | 46 ++++-- app/code/Magento/Catalog/etc/adminhtml/di.xml | 12 ++ 4 files changed, 202 insertions(+), 38 deletions(-) create mode 100644 app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php diff --git a/app/code/Magento/Backend/Block/Widget/Form.php b/app/code/Magento/Backend/Block/Widget/Form.php index 30221618edbed..59b5cc060cc05 100644 --- a/app/code/Magento/Backend/Block/Widget/Form.php +++ b/app/code/Magento/Backend/Block/Widget/Form.php @@ -3,8 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Block\Widget; +use Magento\Framework\App\ObjectManager; + /** * Backend form widget * @@ -27,13 +30,23 @@ class Form extends \Magento\Backend\Block\Widget */ protected $_template = 'Magento_Backend::widget/form.phtml'; + /** @var Form\Element\ElementCreator */ + private $creator; + /** + * Constructs form + * * @param \Magento\Backend\Block\Template\Context $context * @param array $data + * @param Form\Element\ElementCreator|null $creator */ - public function __construct(\Magento\Backend\Block\Template\Context $context, array $data = []) - { + public function __construct( + \Magento\Backend\Block\Template\Context $context, + array $data = [], + Form\Element\ElementCreator $creator = null + ) { parent::__construct($context, $data); + $this->creator = $creator ?: ObjectManager::getInstance()->get(Form\Element\ElementCreator::class); } /** @@ -148,6 +161,7 @@ protected function _beforeToHtml() /** * Initialize form fields values + * * Method will be called after prepareForm and can be used for field values initialization * * @return $this @@ -173,32 +187,11 @@ protected function _setFieldset($attributes, $fieldset, $exclude = []) if (!$this->_isAttributeVisible($attribute)) { continue; } - if (($inputType = $attribute->getFrontend()->getInputType()) && !in_array( - $attribute->getAttributeCode(), - $exclude - ) && ('media_image' != $inputType || $attribute->getAttributeCode() == 'image') + if (($inputType = $attribute->getFrontend()->getInputType()) + && !in_array($attribute->getAttributeCode(), $exclude) + && ('media_image' !== $inputType || $attribute->getAttributeCode() == 'image') ) { - $fieldType = $inputType; - $rendererClass = $attribute->getFrontend()->getInputRendererClass(); - if (!empty($rendererClass)) { - $fieldType = $inputType . '_' . $attribute->getAttributeCode(); - $fieldset->addType($fieldType, $rendererClass); - } - - $element = $fieldset->addField( - $attribute->getAttributeCode(), - $fieldType, - [ - 'name' => $attribute->getAttributeCode(), - 'label' => $attribute->getFrontend()->getLocalizedLabel(), - 'class' => $attribute->getFrontend()->getClass(), - 'required' => $attribute->getIsRequired(), - 'note' => $attribute->getNote() - ] - )->setEntityAttribute( - $attribute - ); - + $element = $this->creator->create($fieldset, $attribute); $element->setAfterElementHtml($this->_getAdditionalElementHtml($element)); $this->_applyTypeSpecificConfig($inputType, $element, $attribute); diff --git a/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php b/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php new file mode 100644 index 0000000000000..997e3061cf6ae --- /dev/null +++ b/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Backend\Block\Widget\Form\Element; + +use Magento\Eav\Model\Entity\Attribute; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\Element\Fieldset; + +/** + * Class ElementCreator + * + * @deprecated 100.3.0 in favour of UI component implementation + * @package Magento\Backend\Block\Widget\Form\Element + */ +class ElementCreator +{ + /** + * @var array + */ + private $modifiers; + + /** + * ElementCreator constructor. + * + * @param array $modifiers + */ + public function __construct(array $modifiers = []) + { + $this->modifiers = $modifiers; + } + + /** + * Creates element + * + * @param Fieldset $fieldset + * @param Attribute $attribute + * + * @return AbstractElement + */ + public function create(Fieldset $fieldset, Attribute $attribute): AbstractElement + { + $config = $this->getElementConfig($attribute); + + if (!empty($config['rendererClass'])) { + $fieldType = $config['inputType'] . '_' . $attribute->getAttributeCode(); + $fieldset->addType($fieldType, $config['rendererClass']); + } + + return $fieldset + ->addField($config['attribute_code'], $config['inputType'], $config) + ->setEntityAttribute($attribute); + } + + /** + * Returns element config + * + * @param Attribute $attribute + * @return array + */ + private function getElementConfig(Attribute $attribute): array + { + $defaultConfig = $this->createDefaultConfig($attribute); + $config = $this->modifyConfig($defaultConfig); + + $config['label'] = __($config['label']); + + return $config; + } + + /** + * Returns default config + * + * @param Attribute $attribute + * @return array + */ + private function createDefaultConfig(Attribute $attribute): array + { + return [ + 'inputType' => $attribute->getFrontend()->getInputType(), + 'rendererClass' => $attribute->getFrontend()->getInputRendererClass(), + 'attribute_code' => $attribute->getAttributeCode(), + 'name' => $attribute->getAttributeCode(), + 'label' => $attribute->getFrontend()->getLabel(), + 'class' => $attribute->getFrontend()->getClass(), + 'required' => $attribute->getIsRequired(), + 'note' => $attribute->getNote(), + ]; + } + + /** + * Modify config + * + * @param array $config + * @return array + */ + private function modifyConfig(array $config): array + { + if ($this->isModified($config['attribute_code'])) { + return $this->applyModifier($config); + } + return $config; + } + + /** + * Returns bool if attribute need to modify + * + * @param string $attribute_code + * @return bool + */ + private function isModified($attribute_code): bool + { + return isset($this->modifiers[$attribute_code]); + } + + /** + * Apply modifier to config + * + * @param array $config + * @return array + */ + private function applyModifier(array $config): array + { + $modifiedConfig = $this->modifiers[$config['attribute_code']]; + foreach (array_keys($config) as $key) { + if (isset($modifiedConfig[$key])) { + $config[$key] = $modifiedConfig[$key]; + } + } + return $config; + } +} diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php index 9c7604d27eae0..ccea42ad5575f 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php @@ -16,8 +16,11 @@ use Magento\Framework\Data\Form\Element\AbstractElement; /** + * Attributes tab block + * * @api * @SuppressWarnings(PHPMD.DepthOfInheritance) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ class Attributes extends \Magento\Catalog\Block\Adminhtml\Form implements @@ -33,6 +36,9 @@ class Attributes extends \Magento\Catalog\Block\Adminhtml\Form implements */ protected $_attributeAction; + /** @var array */ + private $excludeFields; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry @@ -40,6 +46,7 @@ class Attributes extends \Magento\Catalog\Block\Adminhtml\Form implements * @param \Magento\Catalog\Model\ProductFactory $productFactory * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeAction * @param array $data + * @param array|null $excludeFields */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -47,14 +54,19 @@ public function __construct( \Magento\Framework\Data\FormFactory $formFactory, \Magento\Catalog\Model\ProductFactory $productFactory, \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeAction, - array $data = [] + array $data = [], + array $excludeFields = null ) { $this->_attributeAction = $attributeAction; $this->_productFactory = $productFactory; + $this->excludeFields = $excludeFields ?: []; + parent::__construct($context, $registry, $formFactory, $data); } /** + * Construct block + * * @return void */ protected function _construct() @@ -64,20 +76,14 @@ protected function _construct() } /** + * Prepares form + * * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _prepareForm() { - $this->setFormExcludedFieldList( - [ - 'category_ids', - 'gallery', - 'image', - 'media_gallery', - 'quantity_and_stock_status', - 'tier_price', - ] - ); + $this->setFormExcludedFieldList($this->getExcludedFields()); $this->_eventManager->dispatch( 'adminhtml_catalog_product_form_prepare_excluded_field_list', ['object' => $this] @@ -155,6 +161,8 @@ protected function _getAdditionalElementHtml($element) } /** + * Returns tab label + * * @return \Magento\Framework\Phrase */ public function getTabLabel() @@ -163,6 +171,8 @@ public function getTabLabel() } /** + * Return Tab title + * * @return \Magento\Framework\Phrase */ public function getTabTitle() @@ -171,6 +181,8 @@ public function getTabTitle() } /** + * Can show tab in tabs + * * @return bool */ public function canShowTab() @@ -179,10 +191,22 @@ public function canShowTab() } /** + * Tab not hidden + * * @return bool */ public function isHidden() { return false; } + + /** + * Returns excluded fields + * + * @return array + */ + private function getExcludedFields(): array + { + return $this->excludeFields; + } } diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index 9739ee28a6dae..dd3008a4a98a4 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -193,4 +193,16 @@ <type name="Magento\Eav\Api\AttributeSetRepositoryInterface"> <plugin name="remove_products" type="Magento\Catalog\Plugin\Model\AttributeSetRepository\RemoveProducts"/> </type> + <type name="Magento\Catalog\Block\Adminhtml\Product\Edit\Action\Attribute\Tab\Attributes"> + <arguments> + <argument name="excludeFields" xsi:type="array"> + <item name="0" xsi:type="string">category_ids</item> + <item name="1" xsi:type="string">gallery</item> + <item name="2" xsi:type="string">image</item> + <item name="3" xsi:type="string">media_gallery</item> + <item name="4" xsi:type="string">quantity_and_stock_status</item> + <item name="5" xsi:type="string">tier_price</item> + </argument> + </arguments> + </type> </config> From a5bbc68af9b6c8d61595bd0043f22297ea6ed6b7 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Tue, 9 Oct 2018 07:05:41 -0700 Subject: [PATCH 067/240] MAGETWO-95173: Sorting by price for Configurable with Catalog Rule applied --- .../Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml | 2 +- .../Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml | 2 +- ...SortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index 17b569c909d18..806f8122cf687 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Check the category page --> <actionGroup name="StorefrontCheckCategoryActionGroup"> <arguments> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml index b1224ea8f4d6b..e7450663464c2 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogPriceRuleConditionsSection"> <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child"/> <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml index a769172933cdb..8e868300efbf2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml @@ -6,8 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontSortingByPriceForConfigurableProductWithCatalogRuleAppliedTest"> <annotations> <features value="ConfigurableProduct"/> From d265ee312fd5bcc8f9b8e33e8d9dfe4605dcb60a Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Tue, 9 Oct 2018 20:19:21 +0300 Subject: [PATCH 068/240] MAGETWO-93222: Missing URL Keys upon product import --- .../Model/Import/Product.php | 52 ++++++++++++++----- .../Model/Import/ProductTest.php | 50 +++++++++++++++--- .../products_to_import_without_url_keys.csv | 1 - ...ts_to_import_without_url_keys_and_name.csv | 3 ++ 4 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_without_url_keys_and_name.csv diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 19e33ee267da7..ce7239abb7fd5 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1574,8 +1574,14 @@ protected function _saveProducts() $rowScope = $this->getRowScope($rowData); $urlKey = $this->getUrlKey($rowData); - if (!empty($urlKey)) { + if (!empty($rowData[self::URL_KEY])) { + // If url_key column and its value were in the CSV file $rowData[self::URL_KEY] = $urlKey; + } else if($this->isNeedToChangeUrlKey($rowData)) { + // If url_key column was empty or even not declared in the CSV file but by the rules it is need to + // be setteed. In case when url_key is generating from name column we have to ensure that the bunch + // of products will pass for the event with url_key column. + $bunch[$rowNum][self::URL_KEY] = $rowData[self::URL_KEY] = $urlKey; } $rowSku = $rowData[self::COL_SKU]; @@ -2475,17 +2481,17 @@ public function validateRow(array $rowData, $rowNum) } /** + * Check if need to validate url key. + * * @param array $rowData * @return bool */ private function isNeedToValidateUrlKey($rowData) { - $urlKey = $this->getUrlKey($rowData); - - return (!empty($urlKey)) + return (!empty($rowData[self::URL_KEY]) || !empty($rowData[self::COL_NAME])) && (empty($rowData[self::COL_VISIBILITY]) - || $rowData[self::COL_VISIBILITY] - !== (string)Visibility::getOptionArray()[Visibility::VISIBILITY_NOT_VISIBLE]); + || $rowData[self::COL_VISIBILITY] + !== (string)Visibility::getOptionArray()[Visibility::VISIBILITY_NOT_VISIBLE]); } /** @@ -2785,8 +2791,11 @@ protected function getProductUrlSuffix($storeId = null) } /** + * Retrieve url key from provided row data. + * * @param array $rowData * @return string + * * @since 100.0.3 */ protected function getUrlKey($rowData) @@ -2794,14 +2803,8 @@ protected function getUrlKey($rowData) if (!empty($rowData[self::URL_KEY])) { return $this->productUrl->formatUrlKey($rowData[self::URL_KEY]); } - - /** - * If the product exists, assume it already has a URL Key and even - * if a name is provided in the import data, it should not be used - * to overwrite that existing URL Key the product already has. - */ - $isSkuExist = $this->isSkuExist($rowData[self::COL_SKU]); - if (!$isSkuExist && !empty($rowData[self::COL_NAME])) { + + if (!empty($rowData[self::COL_NAME])) { return $this->productUrl->formatUrlKey($rowData[self::COL_NAME]); } @@ -2820,6 +2823,27 @@ protected function getResource() return $this->_resource; } + /** + * Whether a url key is needed to be change. + * Returns false if + * + * @param array $rowData + * @return bool + */ + private function isNeedToChangeUrlKey(array $rowData): bool + { + $urlKey = $this->getUrlKey($rowData); + $productExists = $this->isSkuExist($rowData[self::COL_SKU]); + $markedToEraseUrlKey = isset($rowData[self::URL_KEY]); + // The product isn't new and the url key index wasn't marked for change. + if (!$urlKey && $productExists && !$markedToEraseUrlKey) { + // Seems there is no need to change the url key + return false; + } + + return true; + } + /** * Get product entity link field * 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 019fffc8c0898..a9c24829f9ad8 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -1573,15 +1573,10 @@ public function testExistingProductWithUrlKeys() */ public function testImportWithoutUrlKeys() { - /** - * Products `simple1` and `simple2` are created by fixture so already - * have a URL Key, whereas `new-simple` is a new product so the import - * will generate it's URL Key from the name provided in the CSV. - */ $products = [ - 'simple1' => 'url-key', - 'simple2' => 'url-key2', - 'new-simple' => 'new-simple', + 'simple1' => 'simple-1', + 'simple2' => 'simple-2', + 'simple3' => 'simple-3' ]; $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); @@ -1645,6 +1640,45 @@ public function testImportWithUrlKeysWithSpaces() } } + /** + * Make sure the absence of a url_key column in the csv file won't erase the url key of the existing products. + * To reach the goal we need to not send the name column, as the url key is generated from it. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_url_key.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testImportWithoutUrlKeysAndName() + { + $products = [ + 'simple1' => 'url-key', + 'simple2' => 'url-key2', + ]; + $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/products_to_import_without_url_keys_and_name.csv', + 'directory' => $directory + ] + ); + + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + ) + ->setSource($source) + ->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + foreach ($products as $productSku => $productUrlKey) { + $this->assertEquals($productUrlKey, $productRepository->get($productSku)->getUrlKey()); + } + } + /** * @magentoAppIsolation enabled */ diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_without_url_keys.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_without_url_keys.csv index 80898c5440df7..ff1b9f4b02afb 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_without_url_keys.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_without_url_keys.csv @@ -2,4 +2,3 @@ sku,product_type,store_view_code,name,price,attribute_set_code,url_key simple1,simple,,"simple 1",25,Default,"" simple2,simple,,"simple 2",34,Default,"" simple3,simple,,"simple 3",58,Default,"" -new-simple,simple,,"new simple",25,Default,"" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_without_url_keys_and_name.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_without_url_keys_and_name.csv new file mode 100644 index 0000000000000..8ea6ab92a0295 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_without_url_keys_and_name.csv @@ -0,0 +1,3 @@ +sku,price +simple1,25 +simple2,34 From da204ffcbfbccfc7cb01c12b4d3bd4666988b87b Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 10 Oct 2018 12:14:10 +0300 Subject: [PATCH 069/240] MAGETWO-95057: Return path e-mail variable system/smtp/return_path_email doesn't work --- .../Magento/Email/Model/AbstractTemplate.php | 11 +- app/code/Magento/Email/Model/Transport.php | 105 +++---- .../Email/Test/Unit/Model/TransportTest.php | 192 ------------- composer.json | 1 + composer.lock | 265 ++++++++++++++---- .../Magento/Framework/Mail/MessageTest.php | 33 --- .../Newsletter/Model/SubscriberTest.php | 2 +- .../Magento/ProductAlert/Model/EmailTest.php | 2 +- .../ProductAlert/Model/ObserverTest.php | 2 +- .../Magento/Wishlist/Controller/IndexTest.php | 4 +- .../Framework/Mail/MailMessageInterface.php | 35 +++ .../Magento/Framework/Mail/Message.php | 160 +++++++++-- .../Mail/Template/TransportBuilderByStore.php | 1 - .../Framework/Mail/Test/Unit/MessageTest.php | 75 +---- .../Template/TransportBuilderByStoreTest.php | 3 - .../Mail/Test/Unit/TransportTest.php | 41 +-- .../Magento/Framework/Mail/Transport.php | 42 +-- 17 files changed, 495 insertions(+), 479 deletions(-) delete mode 100644 app/code/Magento/Email/Test/Unit/Model/TransportTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Framework/Mail/MessageTest.php create mode 100644 lib/internal/Magento/Framework/Mail/MailMessageInterface.php diff --git a/app/code/Magento/Email/Model/AbstractTemplate.php b/app/code/Magento/Email/Model/AbstractTemplate.php index 81e993fad76df..a6ecdaf24ebbb 100644 --- a/app/code/Magento/Email/Model/AbstractTemplate.php +++ b/app/code/Magento/Email/Model/AbstractTemplate.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Email\Model; use Magento\Framework\App\Filesystem\DirectoryList; @@ -289,7 +290,7 @@ public function loadDefault($templateId) /** * trim copyright message */ - if (preg_match('/^<!--[\w\W]+?-->/m', $templateText, $matches) && strpos($matches[0], 'Copyright') > 0) { + if (preg_match('/^<!--[\w\W]+?-->/m', $templateText, $matches) && strpos($matches[0], 'Copyright') !== false) { $templateText = str_replace($matches[0], '', $templateText); } @@ -530,13 +531,13 @@ protected function cancelDesignConfig() * * @param string $templateId * @return $this - * @throws \Magento\Framework\Exception\MailException */ public function setForcedArea($templateId) { - if (!isset($this->area)) { + if ($this->area === null) { $this->area = $this->emailConfig->getTemplateArea($templateId); } + return $this; } @@ -604,7 +605,9 @@ public function getDesignConfig() public function setDesignConfig(array $config) { if (!isset($config['area']) || !isset($config['store'])) { - throw new LocalizedException(__('Design config must have area and store.')); + throw new LocalizedException( + __('The design config needs an area and a store. Verify that both are set and try again.') + ); } $this->getDesignConfig()->setData($config); return $this; diff --git a/app/code/Magento/Email/Model/Transport.php b/app/code/Magento/Email/Model/Transport.php index 7e966a9a5a28d..dc7824a228e34 100644 --- a/app/code/Magento/Email/Model/Transport.php +++ b/app/code/Magento/Email/Model/Transport.php @@ -7,13 +7,16 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\MailException; -use Magento\Framework\Mail\MessageInterface; +use Magento\Framework\Mail\Message; use Magento\Framework\Mail\TransportInterface; +use Magento\Framework\Phrase; use Magento\Store\Model\ScopeInterface; +use Zend\Mail\Message as ZendMessage; +use Zend\Mail\Transport\Sendmail; /** * Class that responsible for filling some message data before transporting it. - * @see Zend_Mail_Transport_Sendmail is used for transport + * @see \Zend\Mail\Transport\Sendmail is used for transport */ class Transport implements TransportInterface { @@ -29,79 +32,81 @@ class Transport implements TransportInterface const XML_PATH_SENDING_RETURN_PATH_EMAIL = 'system/smtp/return_path_email'; /** - * Object for sending eMails + * Whether return path should be set or no. * - * @var \Zend_Mail_Transport_Sendmail + * Possible values are: + * 0 - no + * 1 - yes (set value as FROM address) + * 2 - use custom value + * + * @var int */ - private $transport; + private $isSetReturnPath; /** - * Email message object that should be instance of \Zend_Mail - * - * @var MessageInterface + * @var string|null + */ + private $returnPathValue; + + /** + * @var Sendmail + */ + private $zendTransport; + + /** + * @var Message */ private $message; /** - * Core store config - * - * @var ScopeConfigInterface + * @var ZendMessage */ - private $scopeConfig; + private $zendMessage; /** - * @param \Zend_Mail_Transport_Sendmail $transport - * @param MessageInterface $message Email message object + * @param Message $message Email message object * @param ScopeConfigInterface $scopeConfig Core store config - * @param string|array|\Zend_Config|null $parameters Config options for sendmail parameters - * - * @throws \InvalidArgumentException when $message is not an instance of \Zend_Mail + * @param ZendMessage $zendMessage + * @param null|string|array|\Traversable $parameters Config options for sendmail parameters */ public function __construct( - \Zend_Mail_Transport_Sendmail $transport, - MessageInterface $message, - ScopeConfigInterface $scopeConfig + Message $message, + ScopeConfigInterface $scopeConfig, + ZendMessage $zendMessage, + $parameters = null ) { - if (!$message instanceof \Zend_Mail) { - throw new \InvalidArgumentException('The message should be an instance of \Zend_Mail'); - } - $this->transport = $transport; + $this->isSetReturnPath = (int) $scopeConfig->getValue( + self::XML_PATH_SENDING_SET_RETURN_PATH, + ScopeInterface::SCOPE_STORE + ); + $this->returnPathValue = $scopeConfig->getValue( + self::XML_PATH_SENDING_RETURN_PATH_EMAIL, + ScopeInterface::SCOPE_STORE + ); + + $this->zendTransport = new Sendmail($parameters); $this->message = $message; - $this->scopeConfig = $scopeConfig; + $this->zendMessage = $zendMessage; } /** - * Sets Return-Path to email if necessary, and sends email if it is allowed by System Configurations - * - * @return void - * @throws MailException + * @inheritdoc */ public function sendMessage() { try { - /* configuration of whether return path should be set or no. Possible values are: - * 0 - no - * 1 - yes (set value as FROM address) - * 2 - use custom value - * @see Magento\Config\Model\Config\Source\Yesnocustom - */ - $isSetReturnPath = $this->scopeConfig->getValue( - self::XML_PATH_SENDING_SET_RETURN_PATH, - ScopeInterface::SCOPE_STORE - ); - $returnPathValue = $this->scopeConfig->getValue( - self::XML_PATH_SENDING_RETURN_PATH_EMAIL, - ScopeInterface::SCOPE_STORE - ); - - if ($isSetReturnPath == '1') { - $this->message->setReturnPath($this->message->getFrom()); - } elseif ($isSetReturnPath == '2' && $returnPathValue !== null) { - $this->message->setReturnPath($returnPathValue); + $message = $this->zendMessage->fromString($this->message->getRawMessage()); + if (2 === $this->isSetReturnPath && $this->returnPathValue) { + $message->setSender($this->returnPathValue); + } elseif (1 === $this->isSetReturnPath && $message->getFrom()->count()) { + $fromAddressList = $message->getFrom(); + $fromAddressList->rewind(); + $message->setSender($fromAddressList->current()->getEmail()); } - $this->transport->send($this->message); + + $this->zendTransport->send($message); } catch (\Exception $e) { - throw new MailException(__($e->getMessage()), $e); + throw new MailException(new Phrase($e->getMessage()), $e); } } diff --git a/app/code/Magento/Email/Test/Unit/Model/TransportTest.php b/app/code/Magento/Email/Test/Unit/Model/TransportTest.php deleted file mode 100644 index 3589e43996936..0000000000000 --- a/app/code/Magento/Email/Test/Unit/Model/TransportTest.php +++ /dev/null @@ -1,192 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Email\Test\Unit\Model; - -use Magento\Email\Model\Transport; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Mail\Message; -use Magento\Store\Model\ScopeInterface; - -/** - * Covers \Magento\Email\Model\Transport - */ -class TransportTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Zend_Mail_Transport_Sendmail|\PHPUnit_Framework_MockObject_MockObject - */ - private $transportMock; - - /** - * @var Message|\PHPUnit_Framework_MockObject_MockObject - */ - private $messageMock; - - /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $scopeConfigMock; - - /** - * @var Transport - */ - private $model; - - protected function setUp() - { - $this->transportMock = $this->createMock(\Zend_Mail_Transport_Sendmail::class); - - $this->messageMock = $this->createMock(Message::class); - - $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); - - $this->model = new Transport($this->transportMock, $this->messageMock, $this->scopeConfigMock); - } - - /** - * Tests that if any exception was caught, \Magento\Framework\Exception\MailException will thrown - * - * @expectedException \Magento\Framework\Exception\MailException - */ - public function testSendMessageException() - { - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->willThrowException(new \Exception('some exception')); - $this->model->sendMessage(); - } - - /** - * Tests that if sending Return-Path was disabled or email was not provided, - this header won't be set - * - * @param string|int|null $returnPathSet - * @param string|null $returnPathEmail - * - * @dataProvider sendMessageWithoutReturnPathDataProvider - */ - public function testSendMessageWithoutReturnPath($returnPathSet, $returnPathEmail = null) - { - $this->prepareSendingMessage($returnPathSet, $returnPathEmail); - - $this->messageMock->expects($this->never()) - ->method('setReturnPath'); - $this->transportMock->expects($this->once()) - ->method('send'); - $this->model->sendMessage(); - } - - /** - * Tests that if sending Return-Path was disabled, this header won't be set - * - * @param string|int|null $returnPathSet - * @param string|null $emailFrom - * - * @dataProvider sendMessageWithDefaultReturnPathDataProvider - */ - public function testSendMessageWithDefaultReturnPath($returnPathSet, $emailFrom) - { - $this->prepareSendingMessage($returnPathSet, null); - - $this->messageMock->expects($this->once()) - ->method('setReturnPath') - ->with($emailFrom); - $this->messageMock->expects($this->once()) - ->method('getFrom') - ->willReturn($emailFrom); - $this->transportMock->expects($this->once()) - ->method('send'); - $this->model->sendMessage(); - } - - /** - * Tests that if sending Return-Path was disabled, this header won't be set - * - * @param string|int|null $returnPathSet - * @param string|null $emailFrom - * - * @dataProvider sendMessageWithCustomReturnPathDataProvider - */ - public function testSendMessageWithCustomReturnPath($returnPathSet, $emailFrom) - { - $this->prepareSendingMessage($returnPathSet, $emailFrom); - - $this->messageMock->expects($this->once()) - ->method('setReturnPath') - ->with($emailFrom); - $this->messageMock->expects($this->never()) - ->method('getFrom') - ->willReturn($emailFrom); - $this->transportMock->expects($this->once()) - ->method('send'); - $this->model->sendMessage(); - } - - /** - * Tests retrieving message object - */ - public function testGetMessage() - { - $this->assertEquals($this->messageMock, $this->model->getMessage()); - } - - /** - * Executes all main sets for sending message - * - * @param string|int|null $returnPathSet - * @param string|null $returnPathEmail - */ - private function prepareSendingMessage($returnPathSet, $returnPathEmail) - { - $map = [ - [Transport::XML_PATH_SENDING_SET_RETURN_PATH, ScopeInterface::SCOPE_STORE, null, $returnPathSet], - [Transport::XML_PATH_SENDING_RETURN_PATH_EMAIL, ScopeInterface::SCOPE_STORE, null, $returnPathEmail] - ]; - $this->scopeConfigMock->expects($this->exactly(2)) - ->method('getValue') - ->willReturnMap($map); - } - - /** - * Data provider for testSendMessageWithoutReturnPath - * @return array - */ - public function sendMessageWithoutReturnPathDataProvider() - { - return [ - [0], - ['0'], - [3], - ['2', null], - [2, null], - ]; - } - - /** - * Data provider for testSendMessageWithDefaultReturnPath - * @return array - */ - public function sendMessageWithDefaultReturnPathDataProvider() - { - return [ - [1, 'test@exemple.com'], - ['1', 'test@exemple.com'], - ['1', ''] - ]; - } - - /** - * Data provider for testSendMessageWithCustomReturnPath - * @return array - */ - public function sendMessageWithCustomReturnPathDataProvider() - { - return [ - [2, 'test@exemple.com'], - ['2', 'test@exemple.com'], - ['2', ''] - ]; - } -} diff --git a/composer.json b/composer.json index 9f38adf3eac9f..5a35f7c94a209 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,7 @@ "zendframework/zend-db": "^2.8.2", "zendframework/zend-captcha": "^2.7.1", "zendframework/zend-session": "^2.7.3", + "zendframework/zend-mail": "^2.9.0", "magento/zendframework1": "~1.13.0", "colinmollenhour/credis": "1.8.2", "colinmollenhour/php-redis-session-abstract": "1.3.4", diff --git a/composer.lock b/composer.lock index 64a11d27b5295..c765d467dfd0c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "78c402f8214c96270f62f23eee80a6f1", + "content-hash": "c9603f6f68dc6d6cc1ca6b6be242da74", "packages": [ { "name": "braintree/braintree_php", @@ -1436,16 +1436,16 @@ }, { "name": "symfony/console", - "version": "v2.8.45", + "version": "v2.8.46", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0c1fcbb9afb5cff992c982ff99c0434f0146dcfc" + "reference": "aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0c1fcbb9afb5cff992c982ff99c0434f0146dcfc", - "reference": "0c1fcbb9afb5cff992c982ff99c0434f0146dcfc", + "url": "https://api.github.com/repos/symfony/console/zipball/aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789", + "reference": "aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789", "shasum": "" }, "require": { @@ -1493,7 +1493,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:13:39+00:00" + "time": "2018-09-30T03:33:07+00:00" }, { "name": "symfony/debug", @@ -1554,7 +1554,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.45", + "version": "v2.8.46", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -1614,16 +1614,16 @@ }, { "name": "symfony/filesystem", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "285ce5005cb01a0aeaa5b0cf590bd0cc40bb631c" + "reference": "d69930fc337d767607267d57c20a7403d0a822a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/285ce5005cb01a0aeaa5b0cf590bd0cc40bb631c", - "reference": "285ce5005cb01a0aeaa5b0cf590bd0cc40bb631c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d69930fc337d767607267d57c20a7403d0a822a4", + "reference": "d69930fc337d767607267d57c20a7403d0a822a4", "shasum": "" }, "require": { @@ -1660,20 +1660,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-08-10T07:29:05+00:00" + "time": "2018-10-02T12:28:39+00:00" }, { "name": "symfony/finder", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "8a84fcb207451df0013b2c74cbbf1b62d47b999a" + "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8a84fcb207451df0013b2c74cbbf1b62d47b999a", - "reference": "8a84fcb207451df0013b2c74cbbf1b62d47b999a", + "url": "https://api.github.com/repos/symfony/finder/zipball/54ba444dddc5bd5708a34bd095ea67c6eb54644d", + "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d", "shasum": "" }, "require": { @@ -1709,7 +1709,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:19:56+00:00" + "time": "2018-10-03T08:46:40+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1830,16 +1830,16 @@ }, { "name": "symfony/process", - "version": "v2.8.45", + "version": "v2.8.46", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4be278e19064c3492095de50c9e375caae569ae1" + "reference": "f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4be278e19064c3492095de50c9e375caae569ae1", - "reference": "4be278e19064c3492095de50c9e375caae569ae1", + "url": "https://api.github.com/repos/symfony/process/zipball/f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6", + "reference": "f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6", "shasum": "" }, "require": { @@ -1875,7 +1875,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-08-03T09:45:57+00:00" + "time": "2018-09-06T17:11:15+00:00" }, { "name": "tedivm/jshrink", @@ -1923,6 +1923,52 @@ ], "time": "2018-09-16T00:02:51+00:00" }, + { + "name": "true/punycode", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/true/php-punycode.git", + "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", + "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.7", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "TrueBV\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Renan Gonçalves", + "email": "renan.saddam@gmail.com" + } + ], + "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)", + "homepage": "https://github.com/true/php-punycode", + "keywords": [ + "idna", + "punycode" + ], + "time": "2016-11-16T10:37:54+00:00" + }, { "name": "tubalmartin/cssmin", "version": "v4.1.1", @@ -3053,6 +3099,68 @@ ], "time": "2018-04-09T21:59:51+00:00" }, + { + "name": "zendframework/zend-mail", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-mail.git", + "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/d7beb63d5f7144a21ac100072c453e63860cdab8", + "reference": "d7beb63d5f7144a21ac100072c453e63860cdab8", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": "^5.6 || ^7.0", + "true/punycode": "^2.1", + "zendframework/zend-loader": "^2.5", + "zendframework/zend-mime": "^2.5", + "zendframework/zend-stdlib": "^2.7 || ^3.0", + "zendframework/zend-validator": "^2.10.2" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1.4", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-config": "^2.6", + "zendframework/zend-crypt": "^2.6 || ^3.0", + "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1" + }, + "suggest": { + "zendframework/zend-crypt": "Crammd5 support in SMTP Auth", + "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1 when using SMTP to deliver messages" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" + }, + "zf": { + "component": "Zend\\Mail", + "config-provider": "Zend\\Mail\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Mail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Provides generalized functionality to compose and send both text and MIME-compliant multipart e-mail messages", + "keywords": [ + "ZendFramework", + "mail", + "zf" + ], + "time": "2018-06-07T13:37:07+00:00" + }, { "name": "zendframework/zend-math", "version": "2.7.0", @@ -3103,6 +3211,57 @@ ], "time": "2016-04-07T16:29:53+00:00" }, + { + "name": "zendframework/zend-mime", + "version": "2.7.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-mime.git", + "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.21 || ^6.3", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-mail": "^2.6" + }, + "suggest": { + "zendframework/zend-mail": "Zend\\Mail component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Mime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Create and parse MIME messages and parts", + "homepage": "https://github.com/zendframework/zend-mime", + "keywords": [ + "ZendFramework", + "mime", + "zf" + ], + "time": "2018-05-14T19:02:50+00:00" + }, { "name": "zendframework/zend-modulemanager", "version": "2.8.2", @@ -7637,7 +7796,7 @@ }, { "name": "symfony/browser-kit", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -7694,16 +7853,16 @@ }, { "name": "symfony/config", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "7b08223b7f6abd859651c56bcabf900d1627d085" + "reference": "e5389132dc6320682de3643091121c048ff796b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/7b08223b7f6abd859651c56bcabf900d1627d085", - "reference": "7b08223b7f6abd859651c56bcabf900d1627d085", + "url": "https://api.github.com/repos/symfony/config/zipball/e5389132dc6320682de3643091121c048ff796b3", + "reference": "e5389132dc6320682de3643091121c048ff796b3", "shasum": "" }, "require": { @@ -7754,20 +7913,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:19:56+00:00" + "time": "2018-09-08T13:15:14+00:00" }, { "name": "symfony/css-selector", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "edda5a6155000ff8c3a3f85ee5c421af93cca416" + "reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/edda5a6155000ff8c3a3f85ee5c421af93cca416", - "reference": "edda5a6155000ff8c3a3f85ee5c421af93cca416", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/3503415d4aafabc31cd08c3a4ebac7f43fde8feb", + "reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb", "shasum": "" }, "require": { @@ -7807,7 +7966,7 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:06:28+00:00" + "time": "2018-10-02T16:33:53+00:00" }, { "name": "symfony/dependency-injection", @@ -7881,16 +8040,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "452bfc854b60134438e3824b159b0d24a5892331" + "reference": "c705bee03ade5b47c087807dd9ffaaec8dda2722" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/452bfc854b60134438e3824b159b0d24a5892331", - "reference": "452bfc854b60134438e3824b159b0d24a5892331", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/c705bee03ade5b47c087807dd9ffaaec8dda2722", + "reference": "c705bee03ade5b47c087807dd9ffaaec8dda2722", "shasum": "" }, "require": { @@ -7934,20 +8093,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-07-26T10:03:52+00:00" + "time": "2018-10-02T12:28:39+00:00" }, { "name": "symfony/http-foundation", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "2fb33cb6eefe6e790e4023f7c534a9e4214252fc" + "reference": "3a4498236ade473c52b92d509303e5fd1b211ab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/2fb33cb6eefe6e790e4023f7c534a9e4214252fc", - "reference": "2fb33cb6eefe6e790e4023f7c534a9e4214252fc", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3a4498236ade473c52b92d509303e5fd1b211ab1", + "reference": "3a4498236ade473c52b92d509303e5fd1b211ab1", "shasum": "" }, "require": { @@ -7988,20 +8147,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-08-27T17:45:33+00:00" + "time": "2018-10-03T08:48:18+00:00" }, { "name": "symfony/options-resolver", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "6debc476953a45969ab39afe8dee0b825f356dc7" + "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6debc476953a45969ab39afe8dee0b825f356dc7", - "reference": "6debc476953a45969ab39afe8dee0b825f356dc7", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", + "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", "shasum": "" }, "require": { @@ -8042,7 +8201,7 @@ "configuration", "options" ], - "time": "2018-07-26T08:45:46+00:00" + "time": "2018-09-17T17:29:18+00:00" }, { "name": "symfony/polyfill-php54", @@ -8274,16 +8433,16 @@ }, { "name": "symfony/stopwatch", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "deda2765e8dab2fc38492e926ea690f2a681f59d" + "reference": "05e52a39de52ba690aebaed462b2bc8a9649f0a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/deda2765e8dab2fc38492e926ea690f2a681f59d", - "reference": "deda2765e8dab2fc38492e926ea690f2a681f59d", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/05e52a39de52ba690aebaed462b2bc8a9649f0a4", + "reference": "05e52a39de52ba690aebaed462b2bc8a9649f0a4", "shasum": "" }, "require": { @@ -8319,7 +8478,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2018-07-26T10:03:52+00:00" + "time": "2018-10-02T12:28:39+00:00" }, { "name": "symfony/yaml", diff --git a/dev/tests/integration/testsuite/Magento/Framework/Mail/MessageTest.php b/dev/tests/integration/testsuite/Magento/Framework/Mail/MessageTest.php deleted file mode 100644 index d2b220b38f695..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Framework/Mail/MessageTest.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Mail; - -class MessageTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var Message - */ - private $message; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->message = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(Message::class); - } - - public function testGetHeaderEncodingDefaultValue() - { - $this->assertEquals(\Zend_Mime::ENCODING_BASE64, $this->message->getHeaderEncoding()); - } - - public function testGetCharsetDefaultValue() - { - $this->assertEquals('utf-8', $this->message->getCharset()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php index 6e356694b2f03..c6ba498319ff8 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php @@ -33,7 +33,7 @@ public function testEmailConfirmation() $this->assertContains( '/newsletter/subscriber/confirm/id/' . $this->_model->getSubscriberId() . '/code/ysayquyajua23iq29gxwu2eax2qb6gvy', - $transportBuilder->getSentMessage()->getBodyHtml()->getRawContent() + $transportBuilder->getSentMessage()->getRawMessage() ); $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->_model->getSubscriberStatus()); } diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/Model/EmailTest.php b/dev/tests/integration/testsuite/Magento/ProductAlert/Model/EmailTest.php index b545bb8a9f742..aed9214899237 100644 --- a/dev/tests/integration/testsuite/Magento/ProductAlert/Model/EmailTest.php +++ b/dev/tests/integration/testsuite/Magento/ProductAlert/Model/EmailTest.php @@ -82,7 +82,7 @@ public function testSend($isCustomerIdUsed) ); $this->assertContains( 'John Smith,', - $transportBuilder->getSentMessage()->getBodyHtml()->getRawContent() + $transportBuilder->getSentMessage()->getRawMessage() ); } diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/Model/ObserverTest.php b/dev/tests/integration/testsuite/Magento/ProductAlert/Model/ObserverTest.php index 50213710233d2..0fc98d8d8380b 100644 --- a/dev/tests/integration/testsuite/Magento/ProductAlert/Model/ObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/ProductAlert/Model/ObserverTest.php @@ -56,7 +56,7 @@ public function testProcess() ); $this->assertContains( 'John Smith,', - $transportBuilder->getSentMessage()->getBodyHtml()->getRawContent() + $transportBuilder->getSentMessage()->getRawMessage() ); } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php index 4999e379e1d0a..92eae7a3fe3d7 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php @@ -170,9 +170,7 @@ public function testSendAction() \Magento\TestFramework\Mail\Template\TransportBuilderMock::class ); - $actualResult = \Zend_Mime_Decode::decodeQuotedPrintable( - $transportBuilder->getSentMessage()->getBodyHtml()->getContent() - ); + $actualResult = quoted_printable_decode($transportBuilder->getSentMessage()->getRawMessage()); $this->assertStringMatchesFormat( '%A' . $this->_customerViewHelper->getCustomerName($this->_customerSession->getCustomerDataObject()) diff --git a/lib/internal/Magento/Framework/Mail/MailMessageInterface.php b/lib/internal/Magento/Framework/Mail/MailMessageInterface.php new file mode 100644 index 0000000000000..4f16ad4392c0c --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/MailMessageInterface.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Mail; + +/** + * Mail Message interface + */ +interface MailMessageInterface extends MessageInterface +{ + /** + * Set mail message body in HTML format. + * + * @param string $html + * @return $this + */ + public function setBodyHtml($html); + + /** + * Set mail message body in text format. + * + * @param string $text + * @return $this + */ + public function setBodyText($text); + + /** + * Get message source code. + * + * @return string + */ + public function getRawMessage(); +} diff --git a/lib/internal/Magento/Framework/Mail/Message.php b/lib/internal/Magento/Framework/Mail/Message.php index f9511a260da1a..c8f0e75c52800 100644 --- a/lib/internal/Magento/Framework/Mail/Message.php +++ b/lib/internal/Magento/Framework/Mail/Message.php @@ -1,60 +1,176 @@ <?php /** - * Mail Message - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Mail; -class Message extends \Zend_Mail implements MessageInterface +use Zend\Mime\Mime; +use Zend\Mime\Part; + +class Message implements MailMessageInterface { /** + * @var \Zend\Mail\Message + */ + private $zendMessage; + + /** + * Message type + * + * @var string + */ + private $messageType = self::TYPE_TEXT; + + /** + * Initialize dependencies. + * * @param string $charset */ public function __construct($charset = 'utf-8') { - parent::__construct($charset); - $this->setHeaderEncoding(\Zend_Mime::ENCODING_BASE64); + $this->zendMessage = new \Zend\Mail\Message(); + $this->zendMessage->setEncoding($charset); } /** - * Message type + * {@inheritdoc} * - * @var string + * @deprecated + * @see \Magento\Framework\Mail\Message::setBodyText + * @see \Magento\Framework\Mail\Message::setBodyHtml */ - protected $messageType = self::TYPE_TEXT; + public function setMessageType($type) + { + $this->messageType = $type; + return $this; + } /** - * Set message body + * {@inheritdoc} * - * @param string $body - * @return $this + * @deprecated + * @see \Magento\Framework\Mail\Message::setBodyText + * @see \Magento\Framework\Mail\Message::setBodyHtml */ public function setBody($body) { - return $this->messageType == self::TYPE_TEXT ? $this->setBodyText($body) : $this->setBodyHtml($body); + if (is_string($body) && $this->messageType === MailMessageInterface::TYPE_HTML) { + $body = $this->createHtmlMimeFromString($body); + } + $this->zendMessage->setBody($body); + return $this; } /** - * Set message body - * - * @return string + * @inheritdoc + */ + public function setSubject($subject) + { + $this->zendMessage->setSubject($subject); + return $this; + } + + /** + * @inheritdoc + */ + public function getSubject() + { + return $this->zendMessage->getSubject(); + } + + /** + * @inheritdoc */ public function getBody() { - return $this->messageType == self::TYPE_TEXT ? $this->getBodyText() : $this->getBodyHtml(); + return $this->zendMessage->getBody(); } /** - * Set message type - * - * @param string $type - * @return $this + * @inheritdoc */ - public function setMessageType($type) + public function setFrom($fromAddress) { - $this->messageType = $type; + $this->zendMessage->setFrom($fromAddress); + return $this; + } + + /** + * @inheritdoc + */ + public function addTo($toAddress) + { + $this->zendMessage->addTo($toAddress); return $this; } + + /** + * @inheritdoc + */ + public function addCc($ccAddress) + { + $this->zendMessage->addCc($ccAddress); + return $this; + } + + /** + * @inheritdoc + */ + public function addBcc($bccAddress) + { + $this->zendMessage->addBcc($bccAddress); + return $this; + } + + /** + * @inheritdoc + */ + public function setReplyTo($replyToAddress) + { + $this->zendMessage->setReplyTo($replyToAddress); + return $this; + } + + /** + * @inheritdoc + */ + public function getRawMessage() + { + return $this->zendMessage->toString(); + } + + /** + * @inheritdoc + */ + public function setBodyHtml($html) + { + $this->setMessageType(self::TYPE_HTML); + return $this->setBody($html); + } + + /** + * @inheritdoc + */ + public function setBodyText($text) + { + $this->setMessageType(self::TYPE_TEXT); + return $this->setBody($text); + } + + /** + * Create HTML mime message from the string. + * + * @param string $htmlBody + * @return \Zend\Mime\Message + */ + private function createHtmlMimeFromString($htmlBody) + { + $htmlPart = new Part($htmlBody); + $htmlPart->setCharset($this->zendMessage->getEncoding()); + $htmlPart->setType(Mime::TYPE_HTML); + $mimeMessage = new \Zend\Mime\Message(); + $mimeMessage->addPart($htmlPart); + return $mimeMessage; + } } diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php index 95f17fed1123c..785c93824a57d 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php @@ -47,7 +47,6 @@ public function __construct( public function setFromByStore($from, $store) { $result = $this->senderResolver->resolve($from, $store); - $this->message->clearFrom(); $this->message->setFrom($result['email'], $result['name']); return $this; diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/MessageTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/MessageTest.php index ec484ede5f29c..0c33e25e0d9d0 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/MessageTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/MessageTest.php @@ -8,7 +8,7 @@ class MessageTest extends \PHPUnit\Framework\TestCase { /** - * @var \PHPUnit\Framework_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Mail\Message */ protected $_messageMock; @@ -16,78 +16,33 @@ protected function setUp() { $this->_messageMock = $this->createPartialMock( \Magento\Framework\Mail\Message::class, - ['getBodyText', 'getBodyHtml', 'setBodyText', 'setBodyHtml'] + ['setBody', 'setMessageType'] ); } - /** - * @param string $messageType - * @param string $method - * - * @covers \Magento\Framework\Mail\Message::setBody - * @covers \Magento\Framework\Mail\Message::setMessageType - * @dataProvider setBodyDataProvider - */ - public function testSetBody($messageType, $method) + public function testSetBodyHtml() { - $this->_messageMock->setMessageType($messageType); + $this->_messageMock->expects($this->once()) + ->method('setMessageType') + ->with('text/html'); $this->_messageMock->expects($this->once()) - ->method($method) + ->method('setBody') ->with('body'); - $this->_messageMock->setBody('body'); + $this->_messageMock->setBodyHtml('body'); } - /** - * @return array - */ - public function setBodyDataProvider() + public function testSetBodyText() { - return [ - [ - 'messageType' => 'text/plain', - 'method' => 'setBodyText', - ], - [ - 'messageType' => 'text/html', - 'method' => 'setBodyHtml' - ] - ]; - } - - /** - * @param string $messageType - * @param string $method - * - * @covers \Magento\Framework\Mail\Message::getBody - * @covers \Magento\Framework\Mail\Message::setMessageType - * @dataProvider getBodyDataProvider - */ - public function testGetBody($messageType, $method) - { - $this->_messageMock->setMessageType($messageType); - $this->_messageMock->expects($this->once()) - ->method($method); + ->method('setMessageType') + ->with('text/plain'); - $this->_messageMock->getBody('body'); - } + $this->_messageMock->expects($this->once()) + ->method('setBody') + ->with('body'); - /** - * @return array - */ - public function getBodyDataProvider() - { - return [ - [ - 'messageType' => 'text/plain', - 'method' => 'getBodyText', - ], - [ - 'messageType' => 'text/html', - 'method' => 'getBodyHtml' - ] - ]; + $this->_messageMock->setBodyText('body'); } } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php index 58c9b045eed8c..80df2887a3a93 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php @@ -58,9 +58,6 @@ public function testSetFromByStore() ->method('setFrom') ->with('from@example.com', 'name') ->willReturnSelf(); - $this->messageMock->expects($this->once()) - ->method('clearFrom') - ->willReturnSelf(); $this->model->setFromByStore($sender, $store); } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/TransportTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/TransportTest.php index be735e3a6fe2e..3cbe6fc923cdd 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/TransportTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/TransportTest.php @@ -7,48 +7,17 @@ class TransportTest extends \PHPUnit\Framework\TestCase { - /** - * @var \PHPUnit\Framework_MockObject - */ - protected $messageMock; - - /** - * @var \Magento\Framework\Mail\Transport - */ - protected $transport; - - protected function setUp() - { - $this->messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); - $this->transport = new \Magento\Framework\Mail\Transport($this->messageMock); - } - - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The message should be an instance of \Zend_Mail - */ - public function testTransportWithIncorrectMessageObject() - { - $this->messageMock = $this->createMock(\Magento\Framework\Mail\MessageInterface::class); - $this->transport = new \Magento\Framework\Mail\Transport($this->messageMock); - } - /** * @covers \Magento\Framework\Mail\Transport::sendMessage * @expectedException \Magento\Framework\Exception\MailException - * @expectedExceptionMessage No body specified + * @expectedExceptionMessage Invalid email; contains no at least one of "To", "Cc", and "Bcc" header */ public function testSendMessageBrokenMessage() { - $this->messageMock->expects($this->any()) - ->method('getParts') - ->will($this->returnValue(['a', 'b'])); - - $this->transport->sendMessage(); - } + $transport = new \Magento\Framework\Mail\Transport( + new \Magento\Framework\Mail\Message() + ); - public function testGetMessage() - { - $this->assertSame($this->messageMock, $this->transport->getMessage()); + $transport->sendMessage(); } } diff --git a/lib/internal/Magento/Framework/Mail/Transport.php b/lib/internal/Magento/Framework/Mail/Transport.php index dc3b48399c865..fd931c5cccecb 100644 --- a/lib/internal/Magento/Framework/Mail/Transport.php +++ b/lib/internal/Magento/Framework/Mail/Transport.php @@ -1,44 +1,48 @@ <?php /** - * Mail Transport * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Mail; -class Transport extends \Zend_Mail_Transport_Sendmail implements \Magento\Framework\Mail\TransportInterface +use Magento\Framework\Exception\MailException; +use Magento\Framework\Phrase; +use Zend\Mail\Message as ZendMessage; +use Zend\Mail\Transport\Sendmail; + +class Transport implements \Magento\Framework\Mail\TransportInterface { /** - * @var \Magento\Framework\Mail\MessageInterface + * @var Sendmail + */ + private $zendTransport; + + /** + * @var Message */ - protected $_message; + private $message; /** * @param MessageInterface $message - * @param null $parameters - * @throws \InvalidArgumentException + * @param null|string|array|\Traversable $parameters */ - public function __construct(\Magento\Framework\Mail\MessageInterface $message, $parameters = null) + public function __construct(MessageInterface $message, $parameters = null) { - if (!$message instanceof \Zend_Mail) { - throw new \InvalidArgumentException('The message should be an instance of \Zend_Mail'); - } - parent::__construct($parameters); - $this->_message = $message; + $this->zendTransport = new Sendmail($parameters); + $this->message = $message; } /** - * Send a mail using this transport - * - * @return void - * @throws \Magento\Framework\Exception\MailException + * @inheritdoc */ public function sendMessage() { try { - parent::send($this->_message); + $this->zendTransport->send( + ZendMessage::fromString($this->message->getRawMessage()) + ); } catch (\Exception $e) { - throw new \Magento\Framework\Exception\MailException(new \Magento\Framework\Phrase($e->getMessage()), $e); + throw new MailException(new Phrase($e->getMessage()), $e); } } @@ -47,6 +51,6 @@ public function sendMessage() */ public function getMessage() { - return $this->_message; + return $this->message; } } From f339363ef7e23a215445913de6ac357b3dbe1a91 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Wed, 10 Oct 2018 12:26:13 +0300 Subject: [PATCH 070/240] MAGETWO-93393: Table rate shipment is taken from wrong website ID when creating an order through the admin --- .../Controller/Adminhtml/Order/CreateTest.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php index 01eb917135730..7110f39ee532c 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php @@ -96,18 +96,27 @@ public function testLoadBlockShippingMethod() ScopeInterface::SCOPE_STORE, $store->getCode() ); - $website = $this->getWebsite('test'); $customer = $this->getCustomer('customer.web@example.com', (int)$website->getId()); $quote = $this->getQuoteById('0000032134'); $session = $this->_objectManager->get(SessionQuote::class); $session->setQuoteId($quote->getId()); - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $data = [ + 'firstname' => 'John', + 'lastname' => 'Doe', + 'street' => ['Soborna 23'], + 'city' => 'Testcity', + 'country_id' => 'US', + 'region' => 'Alabama', + 'region_id' => 1 + ]; $this->getRequest()->setPostValue( [ - 'customer_id' => $customer->getId(), + 'order' => ['billing_address' => $data], + 'reset_shipping' => 1, 'collect_shipping_rates' => 1, + 'customer_id' => $customer->getId(), 'store_id' => $store->getId(), 'json' => true ] From 38854b5fe8775a3b7a945ee8882a3661e10662b4 Mon Sep 17 00:00:00 2001 From: Thiago Lima <thiagolimaufrj@gmail.com> Date: Wed, 10 Oct 2018 11:26:56 +0200 Subject: [PATCH 071/240] 18082 fix phpunit --- .../Product/Initialization/Helper/Plugin/Configurable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php index 556939ec112f1..b5940e36aa792 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Configurable.php @@ -158,7 +158,7 @@ protected function getVariationMatrix() $configurableMatrix = json_decode($configurableMatrix, true); foreach ($configurableMatrix as $item) { - if (isset($item['newProduct'])) { + if (isset($item['newProduct']) && $item['newProduct']) { $result[$item['variationKey']] = $this->mapData($item); if (isset($item['qty'])) { From 1bc5adfea17626b44b8752d5f51b3d5bf27d7df1 Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Wed, 10 Oct 2018 12:50:09 +0300 Subject: [PATCH 072/240] MAGETWO-94837: Automate with MFTF Ability to configure Advanced Prices from Shared Catalog Page for different type of products --- ...AdminCreateApiBundleProductActionGroup.xml | 109 ++++++++++++++++++ .../Bundle/Test/Mftf/Data/ProductData.xml | 17 ++- .../Test/Mftf/Metadata/product-meta.xml | 35 +++++- .../Test/Mftf/Metadata/product_link-meta.xml | 14 +-- .../Test/Mftf/Metadata/product_links-meta.xml | 17 +++ ...reateApiConfigurableProductActionGroup.xml | 65 +++++++++++ .../Mftf/Data/ConfigurableProductData.xml | 13 ++- .../Test/Mftf/Data/CustomerGroupData.xml | 16 +++ .../Mftf/Metadata/customer-group-meta.xml | 25 ++++ .../Downloadable/Test/Mftf/Data/LinkData.xml | 20 ++++ .../Test/Mftf/Data/ProductData.xml | 26 +++++ .../Mftf/Metadata/downloadable_link-meta.xml | 31 +++++ .../Mftf/Metadata/link_file_content-meta.xml | 15 +++ .../Metadata/sample_file_content-meta.xml | 15 +++ .../Test/Mftf/Data/GroupedProductData.xml | 22 ++++ .../Test/Mftf/Data/ProductLinkData.xml | 27 +++++ .../ProductLinkExtensionAttributeData.xml | 14 +++ .../Test/Mftf/Data/ProductLinksData.xml | 23 ++++ 18 files changed, 494 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Metadata/customer-group-meta.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkExtensionAttributeData.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml new file mode 100644 index 0000000000000..72140cf6d5848 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateApiDynamicBundleProductActionGroup"> + <arguments> + <argument name="productName" defaultValue="Api Dynamic Bundle Product" type="string"/> + </arguments> + <!--Create 4 simple products--> + <createData entity="SimpleProduct3" stepKey="simpleProduct1"> + <field key="price">4.99</field> + </createData> + <createData entity="SimpleProduct3" stepKey="simpleProduct2"> + <field key="price">2.89</field> + </createData> + <createData entity="SimpleProduct3" stepKey="simpleProduct3"> + <field key="price">7.33</field> + </createData> + <createData entity="SimpleProduct3" stepKey="simpleProduct4"> + <field key="price">18.25</field> + </createData> + <!-- Create the bundle product based --> + <createData entity="ApiBundleProduct" stepKey="createBundleProduct"> + <field key="name">{{productName}}</field> + </createData> + <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">false</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct3"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct3"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct4"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct4"/> + </createData> + </actionGroup> + <actionGroup name="AdminCreateApiFixedBundleProductActionGroup"> + <arguments> + <argument name="productName" defaultValue="Api Fixed Bundle Product" type="string"/> + </arguments> + <!--Create 4 simple products--> + <createData entity="SimpleProduct3" stepKey="simpleProduct1"> + <field key="price">4.99</field> + </createData> + <createData entity="SimpleProduct3" stepKey="simpleProduct2"> + <field key="price">2.89</field> + </createData> + <createData entity="SimpleProduct3" stepKey="simpleProduct3"> + <field key="price">7.33</field> + </createData> + <createData entity="SimpleProduct3" stepKey="simpleProduct4"> + <field key="price">18.25</field> + </createData> + <!-- Create the bundle product based --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"> + <field key="name">{{productName}}</field> + </createData> + <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">false</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct3"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct3"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct4"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct4"/> + </createData> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml index c55b0166b04ba..ecdc4da3a75b6 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiBundleProduct" type="product2"> <data key="name" unique="suffix">Api Bundle Product</data> <data key="sku" unique="suffix">api-bundle-product</data> @@ -37,4 +37,19 @@ <requiredEntity type="custom_attribute">CustomAttributeDynamicPrice</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributePriceViewRange</requiredEntity> </entity> + <entity name="ApiFixedBundleProduct" type="product2"> + <data key="name" unique="suffix">Api Fixed Bundle Product</data> + <data key="sku" unique="suffix">api-fixed-bundle-product</data> + <data key="type_id">bundle</data> + <data key="attribute_set_id">4</data> + <data key="price">1.23</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="urlKey" unique="suffix">api-fixed-bundle-product</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute">ApiProductShortDescription</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributeFixPrice</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributePriceView</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml index 3ac0645fa13d2..2fe1e526cfa20 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProduct" dataType="product" type="create" auth="adminOauth" url="/V1/products" method="POST"> <contentType>application/json</contentType> <object dataType="product" key="product"> @@ -121,4 +121,37 @@ <operation name="deleteProduct2" dataType="product2" type="delete" auth="adminOauth" url="/V1/products/{sku}" method="DELETE"> <contentType>application/json</contentType> </operation> + <!-- Data type product3 is to work around MQE-1035 --> + <operation name="CreateProduct3" dataType="product3" type="create" auth="adminOauth" url="/V1/products" method="POST"> + <contentType>application/json</contentType> + <object dataType="product3" key="product"> + <field key="sku">string</field> + <field key="name">string</field> + <field key="attribute_set_id">integer</field> + <field key="price">number</field> + <field key="status">integer</field> + <field key="visibility">integer</field> + <field key="type_id">string</field> + <field key="created_at">string</field> + <field key="updated_at">string</field> + <field key="weight">integer</field> + <field key="extension_attributes">product_extension_attribute</field> + <array key="product_links"> + <value>product_link</value> + </array> + <array key="custom_attributes"> + <value>custom_attribute_array</value> + </array> + <array key="options"> + <value>product_option</value> + </array> + </object> + </operation> + <!-- Data type product3 is to work around MQE-1035 --> + <operation name="DeleteProduct3" dataType="product3" type="delete" auth="adminOauth" url="/V1/products/{sku}" method="DELETE"> + <contentType>application/json</contentType> + </operation> + <operation name="GetProduct3" dataType="product3" type="get" auth="adminOauth" url="/V1/products/{sku}" method="GET"> + <contentType>application/json</contentType> + </operation> </operations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml index 3c90f32990b7d..09544d05a9b56 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_link-meta.xml @@ -7,16 +7,16 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateProductLink" dataType="product_link" type="create"> <field key="sku">string</field> <field key="link_type">string</field> <field key="linked_product_sku">string</field> <field key="linked_product_type">string</field> <field key="position">integer</field> - <array key="extension_attributes"> - <value>product_link_extension_attribute</value> - </array> + <object key="extension_attributes" dataType="product_link_extension_attribute"> + <field key="qty">integer</field> + </object> </operation> <operation name="UpdateProductLink" dataType="product_link" type="update"> <field key="sku">string</field> @@ -24,8 +24,8 @@ <field key="linked_product_sku">string</field> <field key="linked_product_type">string</field> <field key="position">integer</field> - <array key="extension_attributes"> - <value>product_link_extension_attribute</value> - </array> + <object key="extension_attributes" dataType="product_link_extension_attribute"> + <field key="qty">integer</field> + </object> </operation> </operations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml new file mode 100644 index 0000000000000..3a8999b523f13 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product_links-meta.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateProductLinks" dataType="product_links" type="create" auth="adminOauth" url="/V1/products/{sku}/links" method="POST"> + <contentType>application/json</contentType> + <array key="items"> + <value>product_link</value> + </array> + </operation> +</operations> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..75686d23a11b9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateApiConfigurableProductActionGroup"> + <arguments> + <argument name="productName" defaultValue="ApiConfigurableProductWithOutCategory" type="string"/> + </arguments> + + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="ApiConfigurableProductWithOutCategory" stepKey="createConfigProduct"> + <field key="name">{{productName}}</field> + </createData> + + <!-- Create attribute with 2 options to be used in children products --> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="addAttributeToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml index 83e0aa1deaedf..a2f824dd8864e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="BaseConfigurableProduct" type="product"> <data key="sku" unique="suffix">configurable</data> <data key="type_id">configurable</data> @@ -38,4 +38,15 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <entity name="ApiConfigurableProductWithOutCategory" type="product"> + <data key="sku" unique="suffix">api-configurable-product-with-out-category</data> + <data key="type_id">configurable</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">API Configurable Product</data> + <data key="urlKey" unique="suffix">api-configurable-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml new file mode 100644 index 0000000000000..d7fc22f085344 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ApiCustomerGroup" type="customerGroup"> + <data key="code" unique="suffix">ApiCustomerGroup</data> + <data key="tax_class_id">0</data> + <data key="tax_class_name">Retail Customer</data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer-group-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/customer-group-meta.xml new file mode 100644 index 0000000000000..a45bf1645d088 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Metadata/customer-group-meta.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateCustomerGroup" dataType="customerGroup" type="create" auth="adminOauth" url="/V1/customerGroups" method="POST"> + <contentType>application/json</contentType> + <object dataType="customerGroup" key="group"> + <field key="code">string</field> + <field key="tax_class_id">integer</field> + <field key="tax_class_name">string</field> + <array key="extension_attributes"> + <value>extension_attributes</value> + </array> + </object> + </operation> + <operation name="DeleteCustomerGroup" dataType="customerGroup" type="delete" auth="adminOauth" url="/V1/customerGroups/{id}" method="DELETE"> + <contentType>application/json</contentType> + </operation> +</operations> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml b/app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml new file mode 100644 index 0000000000000..4f48ca7309baa --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Data/LinkData.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ApiDownloadableLink" type="downloadable_link"> + <data key="title" unique="suffix">Api Downloadable Link</data> + <data key="price">2.00</data> + <data key="link_type">url</data> + <data key="shareable">No</data> + <data key="number_of_downloads">1000</data> + <data key="sort_order">0</data> + <data key="link_url">https://static.magento.com/sites/all/themes/mag_redesign/images/magento-logo.svg</data> + </entity> +</entities> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml new file mode 100644 index 0000000000000..28b4aa261b941 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ApiDownloadableProduct" type="product"> + <data key="sku" unique="suffix">api-downloadable-product</data> + <data key="type_id">downloadable</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">Api Downloadable Product</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-downloadable-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> + <requiredEntity type="downloadable_link">apiDownloadableLink</requiredEntity> + </entity> +</entities> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml new file mode 100644 index 0000000000000..2511244d445c1 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Metadata/downloadable_link-meta.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateDownloadableLink" dataType="downloadable_link" type="create" auth="adminOauth" url="/V1/products/{sku}/downloadable-links" method="POST"> + <contentType>application/json</contentType> + <object dataType="downloadable_link" key="link"> + <field key="title">string</field> + <field key="sort_order">integer</field> + <field key="is_shareable">integer</field> + <field key="price">number</field> + <field key="number_of_downloads">integer</field> + <field key="link_type">string</field> + <field key="link_file">string</field> + <field key="link_file_content">link_file_content</field> + <field key="file_data">string</field> + <field key="link_url">string</field> + <field key="sample_type">string</field> + <field key="sample_file">string</field> + <field key="sample_file_content">sample_file_content</field> + <field key="sample_url">string</field> + </object> + <field key="isGlobalScopeContent">boolean</field> + </operation> +</operations> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml new file mode 100644 index 0000000000000..d5d6c16c71736 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Metadata/link_file_content-meta.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateLinkFileContent" dataType="link_file_content" type="create"> + <field key="file_data">string</field> + <field key="name">string</field> + </operation> +</operations> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml b/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml new file mode 100644 index 0000000000000..3da91807ceb48 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Metadata/sample_file_content-meta.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateSampleFileContent" dataType="sample_file_content" type="create"> + <field key="file_data">string</field> + <field key="name">string</field> + </operation> +</operations> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml new file mode 100644 index 0000000000000..e760b877fa33d --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ApiGroupedProduct" type="product3"> + <data key="sku" unique="suffix">api-grouped-product</data> + <data key="type_id">grouped</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">Api Grouped Product</data> + <data key="status">1</data> + <data key="urlKey" unique="suffix">api-grouped-product</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/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml new file mode 100644 index 0000000000000..882275d0060b4 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ProductLinkSimple1" type="product_link"> + <var key="sku" entityKey="sku" entityType="product3"/> + <var key="linked_product_sku" entityKey="sku" entityType="product"/> + <data key="link_type">associated</data> + <data key="linked_product_type">simple</data> + <data key="position">1</data> + <requiredEntity type="product_link_extension_attribute">Qty1000</requiredEntity> + </entity> + <entity name="ProductLinkSimple2" type="product_link"> + <var key="sku" entityKey="sku" entityType="product3"/> + <var key="linked_product_sku" entityKey="sku" entityType="product"/> + <data key="link_type">associated</data> + <data key="linked_product_type">simple</data> + <data key="position">2</data> + <requiredEntity type="product_link_extension_attribute">Qty1000</requiredEntity> + </entity> +</entities> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkExtensionAttributeData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkExtensionAttributeData.xml new file mode 100644 index 0000000000000..b580c876a6f30 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkExtensionAttributeData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="Qty1000" type="product_link_extension_attribute"> + <data key="qty">1000</data> + </entity> +</entities> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml new file mode 100644 index 0000000000000..68c95e856e2f8 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinksData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="OneSimpleProductLink" type="product_links"> + <requiredEntity type="product_link">ProductLinkSimple1</requiredEntity> + </entity> + <entity name="OneMoreSimpleProductLink" type="product_links"> + <requiredEntity type="product_link">ProductLinkSimple2</requiredEntity> + </entity> + <entity name="TwoSimpleProductLinks" type="product_links"> + <array key="items"> + <item>ProductLinkSimple1</item> + <item>ProductLinkSimple2</item> + </array> + </entity> +</entities> From 906faa659c720976e2355851fcbe0465ca77bb40 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Wed, 10 Oct 2018 14:22:10 +0300 Subject: [PATCH 073/240] MAGETWO-93155: Joins to grid collections causes MySQL exception due to ambiguous where clause --- .../ResourceModel/Order/Grid/Collection.php | 15 +++++++ .../Order/Grid/CollectionTest.php | 45 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Grid/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Grid/Collection.php index f6dd8f8527a53..82c612c1a781d 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Order/Grid/Collection.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Grid/Collection.php @@ -35,4 +35,19 @@ public function __construct( ) { parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $mainTable, $resourceModel); } + + /** + * @inheritdoc + */ + protected function _initSelect() + { + parent::_initSelect(); + + $tableDescription = $this->getConnection()->describeTable($this->getMainTable()); + foreach ($tableDescription as $columnInfo) { + $this->addFilterToMap($columnInfo['COLUMN_NAME'], 'main_table.' . $columnInfo['COLUMN_NAME']); + } + + return $this; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php new file mode 100644 index 0000000000000..1649706a51f6b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\ResourceModel\Order\Grid; + +use Magento\TestFramework\Helper\Bootstrap; + +class CollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * Tests collection properties. + * + * @throws \ReflectionException + * @return void + */ + public function testCollectionCreate(): void + { + $objectManager = Bootstrap::getObjectManager(); + + /** @var Collection $gridCollection */ + $gridCollection = $objectManager->get(Collection::class); + $tableDescription = $gridCollection->getConnection() + ->describeTable($gridCollection->getMainTable()); + + $mapper = new \ReflectionMethod( + Collection::class, + '_getMapper' + ); + $mapper->setAccessible(true); + $map = $mapper->invoke($gridCollection); + + self::assertInternalType('array', $map); + self::assertArrayHasKey('fields', $map); + self::assertInternalType('array', $map['fields']); + self::assertCount(count($tableDescription), $map['fields']); + + foreach ($map['fields'] as $mappedName) { + self::assertContains('main_table.', $mappedName); + } + } +} From 7e692fbdcae7a6924da167feef5f7384db72e25f Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 10 Oct 2018 14:46:27 +0300 Subject: [PATCH 074/240] MAGETWO-93183: Quans: How to bulk updated Set Product as New to No --- .../Magento/Backend/Block/Widget/Form.php | 61 +++++++++++-------- .../Widget/Form/Element/ElementCreator.php | 3 +- .../Edit/Action/Attribute/Tab/Attributes.php | 6 +- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Form.php b/app/code/Magento/Backend/Block/Widget/Form.php index 59b5cc060cc05..d288dc1c5d43e 100644 --- a/app/code/Magento/Backend/Block/Widget/Form.php +++ b/app/code/Magento/Backend/Block/Widget/Form.php @@ -6,7 +6,16 @@ namespace Magento\Backend\Block\Widget; +use Magento\Backend\Block\Widget\Form\Element\ElementCreator; use Magento\Framework\App\ObjectManager; +use Magento\Backend\Block\Template\Context; +use Magento\Framework\Data\Form as DataForm; +use Magento\Backend\Block\Widget\Form\Renderer\Element; +use Magento\Backend\Block\Widget\Form\Renderer\Fieldset; +use Magento\Backend\Block\Widget\Form\Renderer\Fieldset\Element as FieldsetElement; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\AbstractForm; /** * Backend form widget @@ -21,7 +30,7 @@ class Form extends \Magento\Backend\Block\Widget /** * Form Object * - * @var \Magento\Framework\Data\Form + * @var DataForm */ protected $_form; @@ -30,23 +39,25 @@ class Form extends \Magento\Backend\Block\Widget */ protected $_template = 'Magento_Backend::widget/form.phtml'; - /** @var Form\Element\ElementCreator */ + /** + * @var ElementCreator + * / private $creator; /** * Constructs form * - * @param \Magento\Backend\Block\Template\Context $context + * @param Context $context * @param array $data - * @param Form\Element\ElementCreator|null $creator + * @param ElementCreator|null $creator */ public function __construct( - \Magento\Backend\Block\Template\Context $context, + Context $context, array $data = [], - Form\Element\ElementCreator $creator = null + ElementCreator $creator = null ) { parent::__construct($context, $data); - $this->creator = $creator ?: ObjectManager::getInstance()->get(Form\Element\ElementCreator::class); + $this->creator = $creator ?: ObjectManager::getInstance()->get(ElementCreator::class); } /** @@ -71,21 +82,21 @@ protected function _construct() */ protected function _prepareLayout() { - \Magento\Framework\Data\Form::setElementRenderer( + DataForm::setElementRenderer( $this->getLayout()->createBlock( - \Magento\Backend\Block\Widget\Form\Renderer\Element::class, + Element::class, $this->getNameInLayout() . '_element' ) ); - \Magento\Framework\Data\Form::setFieldsetRenderer( + DataForm::setFieldsetRenderer( $this->getLayout()->createBlock( - \Magento\Backend\Block\Widget\Form\Renderer\Fieldset::class, + Fieldset::class, $this->getNameInLayout() . '_fieldset' ) ); - \Magento\Framework\Data\Form::setFieldsetElementRenderer( + DataForm::setFieldsetElementRenderer( $this->getLayout()->createBlock( - \Magento\Backend\Block\Widget\Form\Renderer\Fieldset\Element::class, + FieldsetElement::class, $this->getNameInLayout() . '_fieldset_element' ) ); @@ -96,7 +107,7 @@ protected function _prepareLayout() /** * Get form object * - * @return \Magento\Framework\Data\Form + * @return DataForm */ public function getForm() { @@ -119,10 +130,10 @@ public function getFormHtml() /** * Set form object * - * @param \Magento\Framework\Data\Form $form + * @param DataForm $form * @return $this */ - public function setForm(\Magento\Framework\Data\Form $form) + public function setForm(DataForm $form) { $this->_form = $form; $this->_form->setParent($this); @@ -183,7 +194,7 @@ protected function _setFieldset($attributes, $fieldset, $exclude = []) { $this->_addElementTypes($fieldset); foreach ($attributes as $attribute) { - /* @var $attribute \Magento\Eav\Model\Entity\Attribute */ + /* @var $attribute Attribute */ if (!$this->_isAttributeVisible($attribute)) { continue; } @@ -202,10 +213,10 @@ protected function _setFieldset($attributes, $fieldset, $exclude = []) /** * Check whether attribute is visible * - * @param \Magento\Eav\Model\Entity\Attribute $attribute + * @param Attribute $attribute * @return bool */ - protected function _isAttributeVisible(\Magento\Eav\Model\Entity\Attribute $attribute) + protected function _isAttributeVisible(Attribute $attribute) { return !(!$attribute || $attribute->hasIsVisible() && !$attribute->getIsVisible()); } @@ -214,11 +225,11 @@ protected function _isAttributeVisible(\Magento\Eav\Model\Entity\Attribute $attr * Apply configuration specific for different element type * * @param string $inputType - * @param \Magento\Framework\Data\Form\Element\AbstractElement $element - * @param \Magento\Eav\Model\Entity\Attribute $attribute + * @param AbstractElement $element + * @param Attribute $attribute * @return void */ - protected function _applyTypeSpecificConfig($inputType, $element, \Magento\Eav\Model\Entity\Attribute $attribute) + protected function _applyTypeSpecificConfig($inputType, $element, Attribute $attribute) { switch ($inputType) { case 'select': @@ -242,10 +253,10 @@ protected function _applyTypeSpecificConfig($inputType, $element, \Magento\Eav\M /** * Add new element type * - * @param \Magento\Framework\Data\Form\AbstractForm $baseElement + * @param AbstractForm $baseElement * @return void */ - protected function _addElementTypes(\Magento\Framework\Data\Form\AbstractForm $baseElement) + protected function _addElementTypes(AbstractForm $baseElement) { $types = $this->_getAdditionalElementTypes(); foreach ($types as $code => $className) { @@ -266,7 +277,7 @@ protected function _getAdditionalElementTypes() /** * Render additional element * - * @param \Magento\Framework\Data\Form\Element\AbstractElement $element + * @param AbstractElement $element * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ diff --git a/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php b/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php index 997e3061cf6ae..50cbeebe757d5 100644 --- a/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php +++ b/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php @@ -13,8 +13,7 @@ /** * Class ElementCreator * - * @deprecated 100.3.0 in favour of UI component implementation - * @package Magento\Backend\Block\Widget\Form\Element + * @deprecated 100.2.0 in favour of UI component implementation */ class ElementCreator { diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php index ccea42ad5575f..2b7074e4c26d2 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Attributes.php @@ -46,7 +46,7 @@ class Attributes extends \Magento\Catalog\Block\Adminhtml\Form implements * @param \Magento\Catalog\Model\ProductFactory $productFactory * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeAction * @param array $data - * @param array|null $excludeFields + * @param array $excludeFields */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -55,11 +55,11 @@ public function __construct( \Magento\Catalog\Model\ProductFactory $productFactory, \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeAction, array $data = [], - array $excludeFields = null + array $excludeFields = [] ) { $this->_attributeAction = $attributeAction; $this->_productFactory = $productFactory; - $this->excludeFields = $excludeFields ?: []; + $this->excludeFields = $excludeFields; parent::__construct($context, $registry, $formFactory, $data); } From ee8f3b1ca91eb6dc76baf2d5e8fad331e25814e5 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Wed, 10 Oct 2018 14:57:35 +0300 Subject: [PATCH 075/240] MAGETWO-93155: Joins to grid collections causes MySQL exception due to ambiguous where clause --- .../Sales/Model/ResourceModel/Order/Grid/CollectionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php index 1649706a51f6b..8eda87ae43c29 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/Order/Grid/CollectionTest.php @@ -17,7 +17,7 @@ class CollectionTest extends \PHPUnit\Framework\TestCase * @throws \ReflectionException * @return void */ - public function testCollectionCreate(): void + public function testCollectionCreate() { $objectManager = Bootstrap::getObjectManager(); From f08793ebf1def59a94593537570f401104a0bc38 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Wed, 10 Oct 2018 15:25:16 +0300 Subject: [PATCH 076/240] MAGETWO-93222: Missing URL Keys upon product import --- .../Magento/CatalogImportExport/Model/Import/Product.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index ce7239abb7fd5..3cf167e9e37e7 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1574,10 +1574,10 @@ protected function _saveProducts() $rowScope = $this->getRowScope($rowData); $urlKey = $this->getUrlKey($rowData); - if (!empty($rowData[self::URL_KEY])) { + if (!empty($rowData[self::URL_KEY])) { // If url_key column and its value were in the CSV file $rowData[self::URL_KEY] = $urlKey; - } else if($this->isNeedToChangeUrlKey($rowData)) { + } else if ($this->isNeedToChangeUrlKey($rowData)) { // If url_key column was empty or even not declared in the CSV file but by the rules it is need to // be setteed. In case when url_key is generating from name column we have to ensure that the bunch // of products will pass for the event with url_key column. @@ -2825,7 +2825,6 @@ protected function getResource() /** * Whether a url key is needed to be change. - * Returns false if * * @param array $rowData * @return bool From 3e8d72aeea51e1cbfc08fdf7e078d439d5f901d8 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 10 Oct 2018 15:26:40 +0300 Subject: [PATCH 077/240] ENGCOM-970: Fix integration test failures. --- .../Model/Product/Type/GroupedTest.php | 47 ++++++------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index 7e1dc4246e3fb..d53c7983e5b1e 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -170,15 +170,14 @@ public function testOutOfStockSubProduct(bool $outOfStockShown, array $data, arr count($expected), count($actual) ); - /** @var array $element */ - foreach ($expected as $number => $element) { - foreach ($element as $key => $value) { - self::assertEquals( - $value, - $actual[$number]->getData($key), - "Failed asserting that value $key matches expected" - ); - } + /** @var Product $product */ + foreach ($actual as $product) { + $sku = $product->getSku(); + self::assertEquals( + $expected[$sku], + $product->getCartQty(), + "Failed asserting that Product Cart Quantity matches expected" + ); } } @@ -201,14 +200,8 @@ public function outOfStockSubProductDataProvider() ], ], [ - [ - 'sku' => 'virtual-product', - 'cart_qty' => 5, - ], - [ - 'sku' => 'simple', - 'cart_qty' => 4, - ], + 'virtual-product' => 5, + 'simple' => 4 ], ], 'Out of stock product are shown #2' => [ @@ -221,10 +214,7 @@ public function outOfStockSubProductDataProvider() ], ], [ - [ - 'sku' => 'virtual-product', - 'cart_qty' => 2, // This is a default quantity. - ], + 'virtual-product' => 2, // This is a default quantity. ], ], 'Out of stock product are hidden #1' => [ @@ -238,14 +228,8 @@ public function outOfStockSubProductDataProvider() ], ], [ - [ - 'sku' => 'virtual-product', - 'cart_qty' => 5, - ], - [ - 'sku' => 'simple', - 'cart_qty' => 4, - ], + 'virtual-product' => 5, + 'simple' => 4, ], ], 'Out of stock product are hidden #2' => [ @@ -258,10 +242,7 @@ public function outOfStockSubProductDataProvider() ], ], [ - [ - 'sku' => 'virtual-product', - 'cart_qty' => 2, // This is a default quantity. - ], + 'virtual-product' => 2, // This is a default quantity. ], ], ]; From 0bb8a167b3ae7a5afa72cca5678ac236a9597a8e Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Wed, 10 Oct 2018 18:56:44 +0300 Subject: [PATCH 078/240] MAGETWO-95585: Can't generate Plugin if object method returns self --- .../SourceClassWithNamespace.php | 12 +++++++++++ ...ceClassWithNamespaceInterceptor.php.sample | 13 ++++++++++++ .../SourceClassWithNamespaceProxy.php.sample | 8 +++++++ .../Code/Generator/Interceptor.php | 21 ++++++++++++++++++- .../ObjectManager/Code/Generator/Proxy.php | 21 ++++++++++++++++++- 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php index ba7df011eec9b..3d88ba391ee0a 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/GeneratorTest/SourceClassWithNamespace.php @@ -110,4 +110,16 @@ public static function publicChildStatic() final public function publicChildFinal() { } + + /** + * Test method + * + * @param bool $arg + * @return SourceClassWithNamespace + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function publicWithSelf($arg = false): self + { + } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample index cf8611866a885..3e0389052ac07 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample @@ -54,6 +54,19 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN } } + /** + * {@inheritdoc} + */ + public function publicWithSelf($arg = false) : \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespace + { + $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicWithSelf'); + if (!$pluginInfo) { + return parent::publicWithSelf($arg); + } else { + return $this->___callPlugins('publicWithSelf', func_get_args(), $pluginInfo); + } + } + /** * {@inheritdoc} */ diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample index 345ac49c6a4f4..30112dcb51485 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceProxy.php.sample @@ -114,6 +114,14 @@ class Proxy extends \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespa return $this->_getSubject()->publicChildWithoutParameters(); } + /** + * {@inheritdoc} + */ + public function publicWithSelf($arg = false) : \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespace + { + return $this->_getSubject()->publicWithSelf($arg); + } + /** * {@inheritdoc} */ diff --git a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php index 608907abcc0e1..59bba2c49631c 100644 --- a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php +++ b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php @@ -113,7 +113,7 @@ protected function _getMethodInfo(\ReflectionMethod $method) "} else {\n" . " return \$this->___callPlugins('{$method->getName()}', func_get_args(), \$pluginInfo);\n" . "}", - 'returnType' => $method->getReturnType(), + 'returnType' => $this->getReturnTypeValue($method->getReturnType()), 'docblock' => ['shortDescription' => '{@inheritdoc}'], ]; @@ -183,4 +183,23 @@ protected function _validateData() } return $result; } + + /** + * Returns return type + * + * @param mixed $returnType + * @return null|string + */ + private function getReturnTypeValue($returnType) + { + $returnTypeValue = null; + + if ($returnType) { + $returnTypeValue = ($returnType->getName() === 'self') + ? $this->getSourceClassName() + : $returnType->getName(); + } + + return $returnTypeValue; + } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php index 0aaf67b5c7cfc..a08ff4677b3a9 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php @@ -165,7 +165,7 @@ protected function _getMethodInfo(\ReflectionMethod $method) 'parameters' => $parameters, 'body' => $this->_getMethodBody($method->getName(), $parameterNames), 'docblock' => ['shortDescription' => '{@inheritdoc}'], - 'returnType' => $method->getReturnType(), + 'returnType' => $this->getReturnTypeValue($method->getReturnType()), ]; return $methodInfo; @@ -245,4 +245,23 @@ protected function _validateData() } return $result; } + + /** + * Returns return type + * + * @param mixed $returnType + * @return null|string + */ + private function getReturnTypeValue($returnType) + { + $returnTypeValue = null; + + if ($returnType) { + $returnTypeValue = ($returnType->getName() === 'self') + ? $this->getSourceClassName() + : $returnType->getName(); + } + + return $returnTypeValue; + } } From de8d6991c29b8e7d783613b6c6c9b10d6ea698ac Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Thu, 11 Oct 2018 09:21:29 +0300 Subject: [PATCH 079/240] MAGETWO-94857: Unable to create invoice for an order via Braintree PayPal --- .../Request/PayPal/VaultDataBuilder.php | 2 + .../Request/VaultCaptureDataBuilder.php | 5 ++ .../Request/VaultCaptureDataBuilderTest.php | 72 +++++++++++++++++-- app/code/Magento/Braintree/i18n/en_US.csv | 1 + 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php index a035c84b4cafd..4d63ee4125b74 100644 --- a/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/PayPal/VaultDataBuilder.php @@ -49,6 +49,8 @@ public function build(array $buildSubject) $payment = $paymentDO->getPayment(); $data = $payment->getAdditionalInformation(); + // the payment token could be stored only if a customer checks the Vault flow on storefront + // see https://developers.braintreepayments.com/guides/paypal/vault/javascript/v2#invoking-the-vault-flow if (!empty($data[VaultConfigProvider::IS_ACTIVE_CODE])) { $result[self::$optionsKey] = [ self::$storeInVaultOnSuccess => true diff --git a/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php index 4280663178efb..78753af0ab11b 100644 --- a/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php @@ -6,6 +6,7 @@ namespace Magento\Braintree\Gateway\Request; use Magento\Braintree\Gateway\SubjectReader; +use Magento\Payment\Gateway\Command\CommandException; use Magento\Payment\Gateway\Request\BuilderInterface; use Magento\Payment\Helper\Formatter; @@ -31,6 +32,7 @@ public function __construct(SubjectReader $subjectReader) $this->subjectReader = $subjectReader; } + /** * @inheritdoc */ @@ -41,6 +43,9 @@ public function build(array $buildSubject) $payment = $paymentDO->getPayment(); $extensionAttributes = $payment->getExtensionAttributes(); $paymentToken = $extensionAttributes->getVaultPaymentToken(); + if ($paymentToken === null) { + throw new CommandException(__('The Payment Token is not available to perform the request.')); + } return [ 'amount' => $this->formatPrice($this->subjectReader->readAmount($buildSubject)), 'paymentMethodToken' => $paymentToken->getGatewayToken() diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php index 5af050002eb2d..80d333db80f0a 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php @@ -3,16 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Braintree\Test\Unit\Gateway\Request; -use Magento\Braintree\Gateway\SubjectReader; use Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder; +use Magento\Braintree\Gateway\SubjectReader; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\Sales\Api\Data\OrderPaymentExtension; use Magento\Sales\Model\Order\Payment; use Magento\Vault\Model\PaymentToken; use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * Tests VaultCaptureDataBuilder. + */ class VaultCaptureDataBuilderTest extends \PHPUnit\Framework\TestCase { /** @@ -30,7 +34,15 @@ class VaultCaptureDataBuilderTest extends \PHPUnit\Framework\TestCase */ private $payment; - public function setUp() + /** + * @var SubjectReader|MockObject + */ + private $subjectReader; + + /** + * @inheritdoc + */ + protected function setUp() { $this->paymentDO = $this->createMock(PaymentDataObjectInterface::class); $this->payment = $this->getMockBuilder(Payment::class) @@ -39,11 +51,15 @@ public function setUp() $this->paymentDO->method('getPayment') ->willReturn($this->payment); - $this->builder = new VaultCaptureDataBuilder(new SubjectReader()); + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->builder = new VaultCaptureDataBuilder($this->subjectReader); } /** - * \Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder::build + * Checks the result after builder execution. */ public function testBuild() { @@ -51,19 +67,28 @@ public function testBuild() $token = '5tfm4c'; $buildSubject = [ 'payment' => $this->paymentDO, - 'amount' => $amount + 'amount' => $amount, ]; $expected = [ 'amount' => $amount, - 'paymentMethodToken' => $token + 'paymentMethodToken' => $token, ]; + $this->subjectReader->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + $this->subjectReader->method('readAmount') + ->with($buildSubject) + ->willReturn($amount); + + /** @var OrderPaymentExtension|MockObject $paymentExtension */ $paymentExtension = $this->getMockBuilder(OrderPaymentExtension::class) ->setMethods(['getVaultPaymentToken']) ->disableOriginalConstructor() ->getMockForAbstractClass(); + /** @var PaymentToken|MockObject $paymentToken */ $paymentToken = $this->getMockBuilder(PaymentToken::class) ->disableOriginalConstructor() ->getMock(); @@ -79,4 +104,39 @@ public function testBuild() $result = $this->builder->build($buildSubject); self::assertEquals($expected, $result); } + + /** + * Checks a builder execution if Payment Token doesn't exist. + * + * @expectedException \Magento\Payment\Gateway\Command\CommandException + * @expectedExceptionMessage The Payment Token is not available to perform the request. + */ + public function testBuildWithoutPaymentToken(): void + { + $amount = 30.00; + $buildSubject = [ + 'payment' => $this->paymentDO, + 'amount' => $amount, + ]; + + $this->subjectReader->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + $this->subjectReader->method('readAmount') + ->with($buildSubject) + ->willReturn($amount); + + /** @var OrderPaymentExtension|MockObject $paymentExtension */ + $paymentExtension = $this->getMockBuilder(OrderPaymentExtension::class) + ->setMethods(['getVaultPaymentToken']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->payment->method('getExtensionAttributes') + ->willReturn($paymentExtension); + $paymentExtension->method('getVaultPaymentToken') + ->willReturn(null); + + $this->builder->build($buildSubject); + } } diff --git a/app/code/Magento/Braintree/i18n/en_US.csv b/app/code/Magento/Braintree/i18n/en_US.csv index 6bf677151ed0d..7bd305f546dc6 100644 --- a/app/code/Magento/Braintree/i18n/en_US.csv +++ b/app/code/Magento/Braintree/i18n/en_US.csv @@ -192,3 +192,4 @@ Currency,Currency "Too many concurrent attempts to refund this transaction. Try again later.","Too many concurrent attempts to refund this transaction. Try again later." "Too many concurrent attempts to void this transaction. Try again later.","Too many concurrent attempts to void this transaction. Try again later." "Braintree Settlement","Braintree Settlement" +"The Payment Token is not available to perform the request.","The Payment Token is not available to perform the request." From ff68de826d58a04913c6d7db1309946a9e1cfc3a Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Thu, 11 Oct 2018 09:38:58 +0300 Subject: [PATCH 080/240] MAGETWO-94857: Unable to create invoice for an order via Braintree PayPal --- .../Braintree/Gateway/Request/VaultCaptureDataBuilder.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php index 78753af0ab11b..5b773e03c8870 100644 --- a/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/VaultCaptureDataBuilder.php @@ -32,7 +32,6 @@ public function __construct(SubjectReader $subjectReader) $this->subjectReader = $subjectReader; } - /** * @inheritdoc */ From aa9a838e0d8ace2f8e24f16862ec75cdadd61861 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Thu, 11 Oct 2018 10:03:08 +0300 Subject: [PATCH 081/240] MAGETWO-93183: Quans: How to bulk updated Set Product as New to No --- .../Backend/Block/Widget/Form/Element/ElementCreator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php b/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php index 50cbeebe757d5..c81f8dc369242 100644 --- a/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php +++ b/app/code/Magento/Backend/Block/Widget/Form/Element/ElementCreator.php @@ -13,7 +13,7 @@ /** * Class ElementCreator * - * @deprecated 100.2.0 in favour of UI component implementation + * @deprecated 100.2.7 in favour of UI component implementation */ class ElementCreator { From 0a3352c66c8aac99dd0d8974f50a9548aaa0fa62 Mon Sep 17 00:00:00 2001 From: Vitalii Zabaznov <vzabaznov@magento.com> Date: Thu, 11 Oct 2018 11:01:01 +0300 Subject: [PATCH 082/240] MAGETWO-94670: Product Export fails --- .../Model/Export/Product.php | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index e77c01b658eea..bf46c0efb2a74 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -352,7 +352,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity * @param \Magento\Framework\App\ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection + * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory * @param \Magento\ImportExport\Model\Export\ConfigInterface $exportConfig * @param \Magento\Catalog\Model\ResourceModel\ProductFactory $productFactory * @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFactory @@ -361,9 +361,10 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity * @param \Magento\Catalog\Model\ResourceModel\Product\Option\CollectionFactory $optionColFactory * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeColFactory * @param Product\Type\Factory $_typeFactory - * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider - * @param \Magento\CatalogImportExport\Model\Export\RowCustomizerInterface $rowCustomizer + * @param ProductEntity\LinkTypeProvider $linkTypeProvider + * @param RowCustomizerInterface $rowCustomizer * @param array $dateAttrCodes + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -692,6 +693,8 @@ protected function updateDataWithCategoryColumns(&$dataRow, &$rowCategories, $pr } /** + * Get header columns + * * {@inheritdoc} */ public function _getHeaderColumns() @@ -751,6 +754,8 @@ protected function _getExportMainAttrCodes() } /** + * Get entity collection + * * {@inheritdoc} */ protected function _getEntityCollection($resetCollection = false) @@ -821,9 +826,8 @@ protected function paginateCollection($page, $pageSize) } /** - * Export process - * * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ public function export() { @@ -857,7 +861,11 @@ public function export() } /** - * {@inheritdoc} + * Apply filter to collection and add not skipped attributes to select. + * + * @param \Magento\Eav\Model\Entity\Collection\AbstractCollection $collection + * @return \Magento\Eav\Model\Entity\Collection\AbstractCollection + * * @since 100.2.0 */ protected function _prepareEntityCollection(\Magento\Eav\Model\Entity\Collection\AbstractCollection $collection) @@ -919,11 +927,10 @@ protected function getExportData() } /** - * Load products' data from the collection - * and filter it (if needed). + * Load products' data from the collection and filter it (if needed). * - * @return array Keys are product IDs, values arrays with keys as store IDs - * and values as store-specific versions of Product entity. + * @return array Keys are product IDs, values arrays with keys as store ID + * and values as store-specific versions of Product entity. */ protected function loadCollection(): array { From e8d51bc32f6b51642685e0d5baaa3c15a52ab8e1 Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Thu, 11 Oct 2018 15:01:40 +0300 Subject: [PATCH 083/240] MAGETWO-94837: Automate with MFTF Ability to configure Advanced Prices from Shared Catalog Page for different type of products --- .../Test/Mftf/Metadata/product-meta.xml | 33 -------------- .../Test/Mftf/Data/ProductLinkData.xml | 7 +-- .../Test/Mftf/Metadata/product-meta.xml | 44 +++++++++++++++++++ .../AdminDataGridFilterActionGroup.xml | 15 ++++++- .../Section/AdminDataGridHeaderSection.xml | 1 + 5 files changed, 60 insertions(+), 40 deletions(-) create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Metadata/product-meta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml index 2fe1e526cfa20..1bf7d0b0d988f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml @@ -121,37 +121,4 @@ <operation name="deleteProduct2" dataType="product2" type="delete" auth="adminOauth" url="/V1/products/{sku}" method="DELETE"> <contentType>application/json</contentType> </operation> - <!-- Data type product3 is to work around MQE-1035 --> - <operation name="CreateProduct3" dataType="product3" type="create" auth="adminOauth" url="/V1/products" method="POST"> - <contentType>application/json</contentType> - <object dataType="product3" key="product"> - <field key="sku">string</field> - <field key="name">string</field> - <field key="attribute_set_id">integer</field> - <field key="price">number</field> - <field key="status">integer</field> - <field key="visibility">integer</field> - <field key="type_id">string</field> - <field key="created_at">string</field> - <field key="updated_at">string</field> - <field key="weight">integer</field> - <field key="extension_attributes">product_extension_attribute</field> - <array key="product_links"> - <value>product_link</value> - </array> - <array key="custom_attributes"> - <value>custom_attribute_array</value> - </array> - <array key="options"> - <value>product_option</value> - </array> - </object> - </operation> - <!-- Data type product3 is to work around MQE-1035 --> - <operation name="DeleteProduct3" dataType="product3" type="delete" auth="adminOauth" url="/V1/products/{sku}" method="DELETE"> - <contentType>application/json</contentType> - </operation> - <operation name="GetProduct3" dataType="product3" type="get" auth="adminOauth" url="/V1/products/{sku}" method="GET"> - <contentType>application/json</contentType> - </operation> </operations> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml index 882275d0060b4..362e595593783 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/ProductLinkData.xml @@ -16,12 +16,7 @@ <data key="position">1</data> <requiredEntity type="product_link_extension_attribute">Qty1000</requiredEntity> </entity> - <entity name="ProductLinkSimple2" type="product_link"> - <var key="sku" entityKey="sku" entityType="product3"/> - <var key="linked_product_sku" entityKey="sku" entityType="product"/> - <data key="link_type">associated</data> - <data key="linked_product_type">simple</data> + <entity name="ProductLinkSimple2" type="product_link" extends="ProductLinkSimple1"> <data key="position">2</data> - <requiredEntity type="product_link_extension_attribute">Qty1000</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Metadata/product-meta.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Metadata/product-meta.xml new file mode 100644 index 0000000000000..87fa0a6379dd3 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Metadata/product-meta.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <!-- Data type product3 is to work around MQE-1035 --> + <operation name="CreateProduct3" dataType="product3" type="create" auth="adminOauth" url="/V1/products" method="POST"> + <contentType>application/json</contentType> + <object dataType="product3" key="product"> + <field key="sku">string</field> + <field key="name">string</field> + <field key="attribute_set_id">integer</field> + <field key="price">number</field> + <field key="status">integer</field> + <field key="visibility">integer</field> + <field key="type_id">string</field> + <field key="created_at">string</field> + <field key="updated_at">string</field> + <field key="weight">integer</field> + <field key="extension_attributes">product_extension_attribute</field> + <array key="product_links"> + <value>product_link</value> + </array> + <array key="custom_attributes"> + <value>custom_attribute_array</value> + </array> + <array key="options"> + <value>product_option</value> + </array> + </object> + </operation> + <!-- Data type product3 is to work around MQE-1035 --> + <operation name="DeleteProduct3" dataType="product3" type="delete" auth="adminOauth" url="/V1/products/{sku}" method="DELETE"> + <contentType>application/json</contentType> + </operation> + <operation name="GetProduct3" dataType="product3" type="get" auth="adminOauth" url="/V1/products/{sku}" method="GET"> + <contentType>application/json</contentType> + </operation> +</operations> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml index 2c65f4d548c31..8fbd5342b24c4 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Search grid with keyword search--> <actionGroup name="searchAdminDataGridByKeyword"> <arguments> @@ -32,4 +32,17 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> </actionGroup> + + <actionGroup name="AdminGridFilterSearchResultsByInput"> + <arguments> + <argument name="inputName" type="string" defaultValue="name"/> + <argument name="value" type="string"/> + </arguments> + <!--Clear all filters in grid if they present--> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearTheFiltersIfPresent"/> + + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="clickOnFilters"/> + <fillField userInput="{{value}}" selector="{{AdminDataGridHeaderSection.filterFieldInput('inputName')}}" stepKey="fillCodeField"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOnApplyFilters"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml index e5766300b0e87..c23909dfdbe24 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridHeaderSection.xml @@ -19,6 +19,7 @@ <element name="cancelFilters" type="button" selector="button[data-action='grid-filter-cancel']" timeout="30"/> <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="clearFilters" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" timeout="30"/> + <element name="removeFilter" type="button" selector=".admin__data-grid-filters-current._show .action-remove" timeout="30"/> <!--Grid view bookmarks--> <element name="bookmarkToggle" type="button" selector="div.admin__data-grid-action-bookmarks button[data-bind='toggleCollapsible']" timeout="30"/> <element name="bookmarkOption" type="button" selector="//div[contains(@class, 'admin__data-grid-action-bookmarks')]/ul/li/div/a[text() = '{{label}}']" parameterized="true" timeout="30"/> From 5139e6fefc0625e8471b75f44e12ddaf9ef58ea2 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Thu, 11 Oct 2018 16:23:04 +0300 Subject: [PATCH 084/240] MAGETWO-95585: Can't generate Plugin if object method returns self --- .../Framework/Interception/Code/Generator/Interceptor.php | 4 ++-- .../Magento/Framework/ObjectManager/Code/Generator/Proxy.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php index 59bba2c49631c..05f5f01efb29e 100644 --- a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php +++ b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php @@ -195,9 +195,9 @@ private function getReturnTypeValue($returnType) $returnTypeValue = null; if ($returnType) { - $returnTypeValue = ($returnType->getName() === 'self') + $returnTypeValue = ((string)$returnType === 'self') ? $this->getSourceClassName() - : $returnType->getName(); + : (string)$returnType; } return $returnTypeValue; diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php index a08ff4677b3a9..39cbea74847f9 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Proxy.php @@ -257,9 +257,9 @@ private function getReturnTypeValue($returnType) $returnTypeValue = null; if ($returnType) { - $returnTypeValue = ($returnType->getName() === 'self') + $returnTypeValue = ((string)$returnType === 'self') ? $this->getSourceClassName() - : $returnType->getName(); + : (string)$returnType; } return $returnTypeValue; From 07ee1786beb48ea160d3a2b4f0f0617b59cfae5f Mon Sep 17 00:00:00 2001 From: "m.matrafailo" <myroslav.matrafayilo@gmail.com> Date: Thu, 11 Oct 2018 16:48:23 +0300 Subject: [PATCH 085/240] Fixed issues-18534: 2 wysiwyg on catalog category edit page Fixed issues-18534: 2 wysiwyg on catalog category edit page --- .../Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml index 9c568cab16d84..ed380722d8b43 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/wysiwyg/js.phtml @@ -43,6 +43,7 @@ var catalogWysiwygEditor = { if (this.modal) { this.modal.html(jQuery(data).html()); + this.modal.modal('option', 'firedElementId', elementId); } else { this.modal = jQuery(data).modal({ title: '<?= /* @escapeNotVerified */ __('WYSIWYG Editor') ?>', From b09adb4b55f96497dc629479da2409716858578e Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 12 Oct 2018 13:13:20 +0300 Subject: [PATCH 086/240] MAGETWO-90929: Billing Address in Braintree PayPal response is absent --- .../Model/Paypal/Helper/QuoteUpdater.php | 2 +- .../Model/Paypal/Helper/QuoteUpdaterTest.php | 211 ++++++++++-------- 2 files changed, 116 insertions(+), 97 deletions(-) diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php index fe5895541543d..aa23fa767d1ed 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php @@ -148,7 +148,7 @@ private function updateBillingAddress(Quote $quote, array $details) { $billingAddress = $quote->getBillingAddress(); - if ($this->config->isRequiredBillingAddress()) { + if ($this->config->isRequiredBillingAddress() && !empty($details['billingAddress'])) { $this->updateAddressData($billingAddress, $details['billingAddress']); } else { $this->updateAddressData($billingAddress, $details['shippingAddress']); 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..c1ecdbd555e16 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 @@ -3,22 +3,24 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Braintree\Test\Unit\Model\Paypal\Helper; +use Magento\Braintree\Gateway\Config\PayPal\Config; +use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater; +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; +use Magento\Braintree\Observer\DataAssignObserver; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartExtensionInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\Quote\Payment; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; -use Magento\Braintree\Observer\DataAssignObserver; -use Magento\Braintree\Gateway\Config\PayPal\Config; -use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class QuoteUpdaterTest * - * @see \Magento\Braintree\Model\Paypal\Helper\QuoteUpdater - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase @@ -26,39 +28,42 @@ class QuoteUpdaterTest extends \PHPUnit\Framework\TestCase const TEST_NONCE = '3ede7045-2aea-463e-9754-cd658ffeeb48'; /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ - private $configMock; + private $config; /** - * @var CartRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CartRepositoryInterface|MockObject */ - private $quoteRepositoryMock; + private $quoteRepository; /** - * @var Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ - private $billingAddressMock; + private $billingAddress; /** - * @var Address|\PHPUnit_Framework_MockObject_MockObject + * @var Address|MockObject */ - private $shippingAddressMock; + private $shippingAddress; /** * @var QuoteUpdater */ private $quoteUpdater; + /** + * @inheritdoc + */ protected function setUp() { - $this->configMock = $this->getMockBuilder(Config::class) + $this->config = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->quoteRepositoryMock = $this->getMockBuilder(CartRepositoryInterface::class) + $this->quoteRepository = $this->getMockBuilder(CartRepositoryInterface::class) ->getMockForAbstractClass(); - $this->billingAddressMock = $this->getMockBuilder(Address::class) + $this->billingAddress = $this->getMockBuilder(Address::class) ->setMethods( [ 'setLastname', @@ -73,9 +78,10 @@ protected function setUp() 'setShouldIgnoreValidation', 'getEmail' ] - )->disableOriginalConstructor() + ) + ->disableOriginalConstructor() ->getMock(); - $this->shippingAddressMock = $this->getMockBuilder(Address::class) + $this->shippingAddress = $this->getMockBuilder(Address::class) ->setMethods( [ 'setLastname', @@ -89,54 +95,61 @@ protected function setUp() 'setPostcode', 'setShouldIgnoreValidation' ] - )->disableOriginalConstructor() + ) + ->disableOriginalConstructor() ->getMock(); $this->quoteUpdater = new QuoteUpdater( - $this->configMock, - $this->quoteRepositoryMock + $this->config, + $this->quoteRepository ); } + /** + * Checks if quote details can be update by the response from Braintree. + * + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testExecute() { $details = $this->getDetails(); - $quoteMock = $this->getQuoteMock(); - $paymentMock = $this->getPaymentMock(); + $quote = $this->getQuoteMock(); + $payment = $this->getPaymentMock(); - $quoteMock->expects(self::once()) - ->method('getPayment') - ->willReturn($paymentMock); + $quote->method('getPayment') + ->willReturn($payment); - $paymentMock->expects(self::once()) - ->method('setMethod') + $payment->method('setMethod') ->with(ConfigProvider::PAYPAL_CODE); - $paymentMock->expects(self::once()) - ->method('setAdditionalInformation') + $payment->method('setAdditionalInformation') ->with(DataAssignObserver::PAYMENT_METHOD_NONCE, self::TEST_NONCE); - $this->updateQuoteStep($quoteMock, $details); + $this->updateQuoteStep($quote, $details); - $this->quoteUpdater->execute(self::TEST_NONCE, $details, $quoteMock); + $this->quoteUpdater->execute(self::TEST_NONCE, $details, $quote); } + /** + * Disables quote's addresses validation. + * + * @return void + */ private function disabledQuoteAddressValidationStep() { - $this->billingAddressMock->expects(self::once()) - ->method('setShouldIgnoreValidation') + $this->billingAddress->method('setShouldIgnoreValidation') ->with(true); - $this->shippingAddressMock->expects(self::once()) - ->method('setShouldIgnoreValidation') + $this->shippingAddress->method('setShouldIgnoreValidation') ->with(true); - $this->billingAddressMock->expects(self::once()) - ->method('getEmail') + $this->billingAddress->method('getEmail') ->willReturn('bt_buyer_us@paypal.com'); } /** + * Gets quote's details. + * * @return array */ - private function getDetails() + private function getDetails(): array { return [ 'email' => 'bt_buyer_us@paypal.com', @@ -166,54 +179,51 @@ private function getDetails() } /** + * Updates shipping address details. + * * @param array $details */ private function updateShippingAddressStep(array $details) { - $this->shippingAddressMock->expects(self::once()) - ->method('setLastname') + $this->shippingAddress->method('setLastname') ->with($details['lastName']); - $this->shippingAddressMock->expects(self::once()) - ->method('setFirstname') + $this->shippingAddress->method('setFirstname') ->with($details['firstName']); - $this->shippingAddressMock->expects(self::once()) - ->method('setEmail') + $this->shippingAddress->method('setEmail') ->with($details['email']); - $this->shippingAddressMock->expects(self::once()) - ->method('setCollectShippingRates') + $this->shippingAddress->method('setCollectShippingRates') ->with(true); - $this->updateAddressDataStep($this->shippingAddressMock, $details['shippingAddress']); + $this->updateAddressDataStep($this->shippingAddress, $details['shippingAddress']); } /** - * @param \PHPUnit_Framework_MockObject_MockObject $addressMock + * Updates address details. + * + * @param MockObject $address * @param array $addressData */ - private function updateAddressDataStep(\PHPUnit_Framework_MockObject_MockObject $addressMock, array $addressData) + private function updateAddressDataStep(MockObject $address, array $addressData) { - $addressMock->expects(self::once()) - ->method('setStreet') + $address->method('setStreet') ->with([$addressData['streetAddress'], $addressData['extendedAddress']]); - $addressMock->expects(self::once()) - ->method('setCity') + $address->method('setCity') ->with($addressData['locality']); - $addressMock->expects(self::once()) - ->method('setRegionCode') + $address->method('setRegionCode') ->with($addressData['region']); - $addressMock->expects(self::once()) - ->method('setCountryId') + $address->method('setCountryId') ->with($addressData['countryCodeAlpha2']); - $addressMock->expects(self::once()) - ->method('setPostcode') + $address->method('setPostcode') ->with($addressData['postalCode']); } /** - * @param \PHPUnit_Framework_MockObject_MockObject $quoteMock + * Updates quote's address details. + * + * @param MockObject $quoteMock * @param array $details */ - private function updateQuoteAddressStep(\PHPUnit_Framework_MockObject_MockObject $quoteMock, array $details) + private function updateQuoteAddressStep(MockObject $quoteMock, array $details) { $quoteMock->expects(self::exactly(2)) ->method('getIsVirtual') @@ -224,64 +234,61 @@ private function updateQuoteAddressStep(\PHPUnit_Framework_MockObject_MockObject } /** + * Updates billing address details. + * * @param array $details */ private function updateBillingAddressStep(array $details) { - $this->configMock->expects(self::once()) - ->method('isRequiredBillingAddress') + $this->config->method('isRequiredBillingAddress') ->willReturn(true); - $this->updateAddressDataStep($this->billingAddressMock, $details['billingAddress']); + $this->updateAddressDataStep($this->billingAddress, $details['billingAddress']); - $this->billingAddressMock->expects(self::once()) - ->method('setLastname') + $this->billingAddress->method('setLastname') ->with($details['lastName']); - $this->billingAddressMock->expects(self::once()) - ->method('setFirstname') + $this->billingAddress->method('setFirstname') ->with($details['firstName']); - $this->billingAddressMock->expects(self::once()) - ->method('setEmail') + $this->billingAddress->method('setEmail') ->with($details['email']); } /** - * @param \PHPUnit_Framework_MockObject_MockObject $quoteMock + * Updates quote details. + * + * @param MockObject $quote * @param array $details */ - private function updateQuoteStep(\PHPUnit_Framework_MockObject_MockObject $quoteMock, array $details) + private function updateQuoteStep(MockObject $quote, array $details) { - $quoteMock->expects(self::once()) - ->method('setMayEditShippingAddress') + $quote->method('setMayEditShippingAddress') ->with(false); - $quoteMock->expects(self::once()) - ->method('setMayEditShippingMethod') + $quote->method('setMayEditShippingMethod') ->with(true); - $quoteMock->expects(self::exactly(2)) - ->method('getShippingAddress') - ->willReturn($this->shippingAddressMock); - $quoteMock->expects(self::exactly(2)) + $quote->method('getShippingAddress') + ->willReturn($this->shippingAddress); + $quote->expects(self::exactly(2)) ->method('getBillingAddress') - ->willReturn($this->billingAddressMock); + ->willReturn($this->billingAddress); - $this->updateQuoteAddressStep($quoteMock, $details); + $this->updateQuoteAddressStep($quote, $details); $this->disabledQuoteAddressValidationStep(); - $quoteMock->expects(self::once()) - ->method('collectTotals'); + $quote->method('collectTotals'); - $this->quoteRepositoryMock->expects(self::once()) - ->method('save') - ->with($quoteMock); + $this->quoteRepository->method('save') + ->with($quote); } /** - * @return Quote|\PHPUnit_Framework_MockObject_MockObject + * Creates a mock for Quote object. + * + * @return Quote|MockObject */ - private function getQuoteMock() + private function getQuoteMock(): MockObject { - return $this->getMockBuilder(Quote::class) + $quote = $this->getMockBuilder(Quote::class) ->setMethods( [ 'getIsVirtual', @@ -292,14 +299,26 @@ private function getQuoteMock() 'getShippingAddress', 'getBillingAddress', ] - )->disableOriginalConstructor() + ) + ->disableOriginalConstructor() ->getMock(); + + $cartExtension = $this->getMockBuilder(CartExtensionInterface::class) + ->setMethods(['setShippingAssignments']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $quote->method('getExtensionAttributes') + ->willReturn($cartExtension); + return $quote; } /** - * @return Payment|\PHPUnit_Framework_MockObject_MockObject + * Creates a mock for Payment object. + * + * @return Payment|MockObject */ - private function getPaymentMock() + private function getPaymentMock(): MockObject { return $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() From 7fd27fe87cbdd38607efb89df6c94a71b3302558 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Fri, 5 Oct 2018 17:22:49 +0100 Subject: [PATCH 087/240] added component status based filtering --- .../view/base/web/js/core/renderer/layout.js | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/core/renderer/layout.js b/app/code/Magento/Ui/view/base/web/js/core/renderer/layout.js index fe312738469e7..ac1de4631e908 100644 --- a/app/code/Magento/Ui/view/base/web/js/core/renderer/layout.js +++ b/app/code/Magento/Ui/view/base/web/js/core/renderer/layout.js @@ -224,7 +224,7 @@ define([ */ build: function (parent, node, name) { var defaults = parent && parent.childDefaults || {}, - children = node.children, + children = this.filterDisabledChildren(node.children), type = getNodeType(parent, node), dataScope = getDataScope(parent, node), component, @@ -294,6 +294,35 @@ define([ return node; }, + /** + * Filter out all disabled components. + * + * @param {Object} children + * @returns {*} + */ + filterDisabledChildren: function (children) { + var cIds; + + //cleanup children config.componentDisabled = true + if (children && typeof children === 'object') { + cIds = Object.keys(children); + + if (cIds) { + _.each(cIds, function (cId) { + if (typeof children[cId] === 'object' && + children[cId].hasOwnProperty('config') && + typeof children[cId].config === 'object' && + children[cId].config.hasOwnProperty('componentDisabled') && + children[cId].config.componentDisabled === true) { + delete children[cId]; + } + }); + } + } + + return children; + }, + /** * Init component. * From e2f89c8543ca3461538f36cfb0f428ef9d502f66 Mon Sep 17 00:00:00 2001 From: utietze <ulf@tietze-digital.de> Date: Wed, 26 Sep 2018 19:59:54 +0200 Subject: [PATCH 088/240] Update CategoryProcessor.php --- .../Model/Import/Product/CategoryProcessor.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php index 54fdecbaaf967..6ed6add15c9dc 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php @@ -112,6 +112,9 @@ protected function createCategory($name, $parentId) if (!($parentCategory = $this->getCategoryById($parentId))) { $parentCategory = $this->categoryFactory->create()->load($parentId); } + + // Set StoreId to 0 to generate URL Keys global and prevent generating url rewrites just for default website + $category->setStoreId(0); $category->setPath($parentCategory->getPath()); $category->setParentId($parentId); $category->setName($name); From 4ad4849565650f1ddf77164b901afb42adbfa571 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi <mankivsk@adobe.com> Date: Tue, 2 Oct 2018 13:27:17 -0500 Subject: [PATCH 089/240] Fixed static test --- .../Model/Import/Product/CategoryProcessor.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php index 6ed6add15c9dc..375fb00b5eae9 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/CategoryProcessor.php @@ -66,6 +66,8 @@ public function __construct( } /** + * Initialize categories to be processed + * * @return $this */ protected function initCategories() From 7169f0442658ec21c6a5c215269d8bafcff71093 Mon Sep 17 00:00:00 2001 From: Oleksii Lisovyi <olexii.lisovyi@ven.com> Date: Fri, 12 Oct 2018 15:02:38 +0300 Subject: [PATCH 090/240] Module Catalog URL Rewrite: fix issue with product URL Rewrites re-generation after changing product URL Key for product with existing url_path attribute value --- ...ProductProcessUrlRewriteSavingObserver.php | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index c4d67f447e2cf..fd4f7b8edff4d 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -6,11 +6,15 @@ namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\ObjectManager; use Magento\UrlRewrite\Model\UrlPersistInterface; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\Framework\Event\ObserverInterface; +/** + * Class ProductProcessUrlRewriteSavingObserver + */ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface { /** @@ -24,21 +28,32 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface private $urlPersist; /** - * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator - * @param UrlPersistInterface $urlPersist + * @var ProductUrlPathGenerator + */ + private $productUrlPathGenerator; + + /** + * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator + * @param UrlPersistInterface $urlPersist + * @param ProductUrlPathGenerator|null $productUrlPathGenerator */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, - UrlPersistInterface $urlPersist + UrlPersistInterface $urlPersist, + ProductUrlPathGenerator $productUrlPathGenerator = null ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; + $this->productUrlPathGenerator = $productUrlPathGenerator ?: ObjectManager::getInstance() + ->get(ProductUrlPathGenerator::class); } /** * Generate urls for UrlRewrite and save it in storage + * * @param \Magento\Framework\Event\Observer $observer * @return void + * @throws \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException */ public function execute(\Magento\Framework\Event\Observer $observer) { @@ -51,6 +66,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) || $product->dataHasChangedFor('visibility') ) { if ($product->isVisibleInSiteVisibility()) { + $product->unsUrlPath(); + $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); } } From e49f37e65b1d5d514b45b0f24af2bc0580fb6708 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sun, 26 Aug 2018 19:12:04 +0200 Subject: [PATCH 091/240] Make it possible to disable report bugs link --- app/code/Magento/Theme/etc/config.xml | 1 + app/code/Magento/Theme/etc/di.xml | 4 ++++ app/code/Magento/Theme/i18n/en_US.csv | 4 ++++ .../adminhtml/ui_component/design_config_form.xml | 14 ++++++++++++++ .../Magento/Theme/view/frontend/layout/default.xml | 2 +- 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/etc/config.xml b/app/code/Magento/Theme/etc/config.xml index a6984b449d944..b44691c0df963 100644 --- a/app/code/Magento/Theme/etc/config.xml +++ b/app/code/Magento/Theme/etc/config.xml @@ -46,6 +46,7 @@ Disallow: /*SID= </header> <footer translate="copyright"> <copyright>Copyright © 2013-present Magento, Inc. All rights reserved.</copyright> + <report_bugs>1</report_bugs> </footer> </design> <theme> diff --git a/app/code/Magento/Theme/etc/di.xml b/app/code/Magento/Theme/etc/di.xml index c20184fec6bc4..f83e0779789d8 100644 --- a/app/code/Magento/Theme/etc/di.xml +++ b/app/code/Magento/Theme/etc/di.xml @@ -213,6 +213,10 @@ <item name="path" xsi:type="string">design/footer/absolute_footer</item> <item name="fieldset" xsi:type="string">other_settings/footer</item> </item> + <item name="footer_report_bugs" xsi:type="array"> + <item name="path" xsi:type="string">design/footer/report_bugs</item> + <item name="fieldset" xsi:type="string">other_settings/footer</item> + </item> <item name="default_robots" xsi:type="array"> <item name="path" xsi:type="string">design/search_engine_robots/default_robots</item> <item name="fieldset" xsi:type="string">other_settings/search_engine_robots</item> diff --git a/app/code/Magento/Theme/i18n/en_US.csv b/app/code/Magento/Theme/i18n/en_US.csv index b454b7222bd1c..e18798f4bf165 100644 --- a/app/code/Magento/Theme/i18n/en_US.csv +++ b/app/code/Magento/Theme/i18n/en_US.csv @@ -185,3 +185,7 @@ Settings,Settings "2 columns with left bar","2 columns with left bar" "2 columns with right bar","2 columns with right bar" "3 columns","3 columns" +ID,ID +View,View +Action,Action +"Display Report Bugs Link","Display Report Bugs Link" diff --git a/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml index ac734699e4d71..3dcc265147cf2 100644 --- a/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml +++ b/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml @@ -234,6 +234,20 @@ <dataScope>footer_copyright</dataScope> </settings> </field> + <field name="footer_report_bugs" formElement="select"> + <settings> + <dataType>text</dataType> + <label translate="true">Display Report Bugs Link</label> + <dataScope>footer_report_bugs</dataScope> + </settings> + <formElements> + <select> + <settings> + <options class="Magento\Config\Model\Config\Source\Yesno"/> + </settings> + </select> + </formElements> + </field> </fieldset> <fieldset name="search_engine_robots" sortOrder="120"> <settings> diff --git a/app/code/Magento/Theme/view/frontend/layout/default.xml b/app/code/Magento/Theme/view/frontend/layout/default.xml index 716341f5a64a4..c84222be19c3c 100644 --- a/app/code/Magento/Theme/view/frontend/layout/default.xml +++ b/app/code/Magento/Theme/view/frontend/layout/default.xml @@ -119,7 +119,7 @@ </arguments> </block> <block class="Magento\Theme\Block\Html\Footer" name="copyright" template="Magento_Theme::html/copyright.phtml"/> - <block class="Magento\Framework\View\Element\Template" name="report.bugs" template="Magento_Theme::html/bugreport.phtml" /> + <block class="Magento\Framework\View\Element\Template" name="report.bugs" template="Magento_Theme::html/bugreport.phtml" ifconfig="design/footer/report_bugs"/> </container> </referenceContainer> <referenceContainer name="before.body.end"> From 9b0586e96248c6753688a417cde68a89e532907c Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 12 Oct 2018 15:50:36 +0300 Subject: [PATCH 092/240] Fixed code style issue --- .../Observer/ProductProcessUrlRewriteSavingObserver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index fd4f7b8edff4d..6eda8dd0b61ee 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -33,8 +33,8 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface private $productUrlPathGenerator; /** - * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator - * @param UrlPersistInterface $urlPersist + * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator + * @param UrlPersistInterface $urlPersist * @param ProductUrlPathGenerator|null $productUrlPathGenerator */ public function __construct( From 0c25f572581d008eba6de78ed5bd4c222444be60 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Fri, 12 Oct 2018 05:58:21 -0700 Subject: [PATCH 093/240] MAGETWO-95173: Sorting by price for Configurable with Catalog Rule applied --- .../Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml | 3 +-- app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml | 2 +- .../Test/Mftf/Section/AdminCreateProductAttributeSection.xml | 2 +- .../Test/Mftf/Section/StorefrontCategoryMainSection.xml | 2 +- .../Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml | 2 +- .../Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml | 2 +- .../Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml | 2 +- .../Test/Mftf/Section/AdminCatalogPriceRuleSection.xml | 2 +- .../Test/Mftf/Data/ConfigurableProductOptionData.xml | 2 +- .../ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml | 2 +- 10 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 22d3f656e0e0c..8e5cc3141b0d4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -6,8 +6,7 @@ */ --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="navigateToEditProductAttribute"> <arguments> <argument name="ProductAttribute" type="string"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml index 6c3d04970fd32..77cb6e13a95e3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ProductAttributeFrontendLabel" type="FrontendLabel"> <data key="store_id">0</data> <data key="label" unique="suffix">attribute</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 607dabe6757f2..c151f2b67a8b2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AttributePropertiesSection"> <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> <element name="Save" type="button" selector="#save"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index 00e819b5e9cf7..7cdb1e82ed54e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategoryMainSection"> <element name="modeListButton" type="button" selector="#mode-list" timeout="10"/> <element name="categoryTitle" type="text" selector="#page-title-heading span"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index cd6b18adf0536..d6bd37910cd83 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- action group to create a new catalog price rule giving a catalogRule entity --> <actionGroup name="CreateCatalogPriceRule"> <arguments> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml index 5b394ba4a7bf5..7e2dbcd53f922 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomCatalogRule" type="catalogRule"> <data key="name" unique="suffix">CatalogPriceRule</data> <data key="description">Catalog Price Rule Description</data> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml index f7e8fda942c23..ad90a749bff45 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogPriceRuleGridSection"> <element name="filterByRuleName" type="input" selector="#promo_catalog_grid_filter_name"/> <element name="attribute" type="text" selector="//td[contains(text(), '{{arg}}')]" parameterized="true"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml index 2b6662afaa058..71c64987eb280 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogPriceRuleSection"> <element name="saveAndApply" type="button" selector="#save_and_apply" timeout="30"/> <element name="saveAndContinue" type="button" selector="#save_and_continue" timeout="30"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml index 969bb3d45258d..e98175bc8d40b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductOptionData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ConfigurableProductTwoOptions" type="ConfigurableProductOption"> <var key="attribute_id" entityKey="attribute_id" entityType="ProductAttribute" /> <data key="label">option</data> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml index 9ada1fc97471a..e1cd70790a6b2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ValueIndexData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ValueIndex1" type="ValueIndex"> <var key="value_index" entityKey="value" entityType="ProductAttributeOption"/> </entity> From 00df1a2d6111fb3bb40152421a78a78097520bf7 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 12 Oct 2018 17:28:49 +0300 Subject: [PATCH 094/240] MAGETWO-88898: Mobile PDP accordion widget hides accordion content on phones with iOS --- .../Ui/base/js/grid/sticky/sticky.test.js | 2 + lib/web/mage/collapsible.js | 43 ++++++++++++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sticky/sticky.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sticky/sticky.test.js index 40bfad0519ec6..9a6c28757e89f 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sticky/sticky.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/sticky/sticky.test.js @@ -46,8 +46,10 @@ define([ expect(stickyObj.initListeners).toHaveBeenCalled(); }); it('has initOnScroll method', function () { + spyOn(document, 'addEventListener'); stickyObj.initOnScroll(); expect(stickyObj.lastHorizontalScrollPos).toBeDefined(); + expect(document.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function)); }); it('has initOnListingScroll method', function () { spyOn(stickyObj, 'initOnListingScroll'); diff --git a/lib/web/mage/collapsible.js b/lib/web/mage/collapsible.js index 656afc8f89e25..2262c1f74fbe7 100644 --- a/lib/web/mage/collapsible.js +++ b/lib/web/mage/collapsible.js @@ -444,14 +444,19 @@ define([ * Activate. */ activate: function () { - if (!this.options.disabled) { - if (this.options.animate) { - this._animate(showProps); - } else { - this.content.show(); + if (this.options.disabled) { + return; + } + + if (this.options.animate) { + this._animate(showProps); + } else { + if (this.content.length) { + this._scrollToTopIfVisible(this.content.get(0).parentElement); } - this._open(); + this.content.show(); } + this._open(); }, /** @@ -553,6 +558,32 @@ define([ }, 1); }); } + }, + + /** + * @param {HTMLElement} elem + * @private + */ + _scrollToTopIfVisible: function (elem) { + if (this._isElementOutOfViewport(elem)) { + elem.scrollIntoView(); + } + }, + + /** + * @param {HTMLElement} elem + * @private + * @return {Boolean} + */ + _isElementOutOfViewport: function (elem) { + var rect = elem.getBoundingClientRect(); + + return ( + rect.left < 0 || + rect.top < 0 || + rect.right > window.innerWidth || + rect.bottom > window.innerHeight + ); } }); From 13a3a894e587e90fecb0db8c505d1f4d9e3eed94 Mon Sep 17 00:00:00 2001 From: Andrii Dimov <adimov@adobe.com> Date: Fri, 12 Oct 2018 19:46:46 +0300 Subject: [PATCH 095/240] MAGETWO-95206: Investigate all becnhmark improvements delivery to infra mainline --- setup/performance-toolkit/benchmark.jmx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 9d07cf3193266..f4f3f3052bb68 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -2535,21 +2535,21 @@ if (props.get("category_names_list") == null) { <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> <collectionProp name="Arguments.arguments"> <elementProp name="blocks" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">false</boolProp> + <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">["customer_form_login"]</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">blocks</stringProp> </elementProp> <elementProp name="handles" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">false</boolProp> + <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">["default","customer_account_login"]</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">handles</stringProp> </elementProp> <elementProp name="originalRequest" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">false</boolProp> + <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">{"route":"customer","controller":"account","action":"login","uri":"/customer/account/login/"}</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> From ac126a87c0f26857570c595bf2f8c791cd08584d Mon Sep 17 00:00:00 2001 From: peterjaap <peterjaap@elgentos.nl> Date: Wed, 10 Oct 2018 11:39:11 +0200 Subject: [PATCH 096/240] Added missing throw tag for exception to docblock of construct --- app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index b67eb1b45fe45..cead9f838f4c0 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -60,6 +60,7 @@ class Tablerate extends \Magento\Shipping\Model\Carrier\AbstractCarrier implemen * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $resultMethodFactory * @param \Magento\OfflineShipping\Model\ResourceModel\Carrier\TablerateFactory $tablerateFactory * @param array $data + * @throws LocalizedException * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function __construct( From e1b5f517126100d2d1b59e16a5574d482317dbef Mon Sep 17 00:00:00 2001 From: Sam Granger <sam.granger@gmail.com> Date: Tue, 9 Oct 2018 15:36:02 +0200 Subject: [PATCH 097/240] Do not output html for region field due to xss --- .../web/template/shipping-address/address-renderer/default.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html b/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html index 05ced7a978f82..2a5dc27328a43 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html @@ -8,7 +8,7 @@ <text args="address().prefix"/> <text args="address().firstname"/> <text args="address().middlename"/> <text args="address().lastname"/> <text args="address().suffix"/><br/> <text args="_.values(address().street).join(', ')"/><br/> - <text args="address().city "/>, <span html="address().region"></span> <text args="address().postcode"/><br/> + <text args="address().city "/>, <span text="address().region"></span> <text args="address().postcode"/><br/> <text args="getCountryName(address().countryId)"/><br/> <a if="address().telephone" attr="'href': 'tel:' + address().telephone" text="address().telephone"></a><br/> From c1a7d1930d6cdcd112836a57022a65345d05e84d Mon Sep 17 00:00:00 2001 From: Sam Granger <sam.granger@gmail.com> Date: Tue, 9 Oct 2018 15:36:44 +0200 Subject: [PATCH 098/240] Do not output html for region field due to xss --- .../template/shipping-information/address-renderer/default.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html b/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html index 97286a28552d2..541413955cb47 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html @@ -8,7 +8,7 @@ <text args="address().prefix"/> <text args="address().firstname"/> <text args="address().middlename"/> <text args="address().lastname"/> <text args="address().suffix"/><br/> <text args="_.values(address().street).join(', ')"/><br/> - <text args="address().city "/>, <span html="address().region"></span> <text args="address().postcode"/><br/> + <text args="address().city "/>, <span text="address().region"></span> <text args="address().postcode"/><br/> <text args="getCountryName(address().countryId)"/><br/> <a if="address().telephone" attr="'href': 'tel:' + address().telephone" text="address().telephone"></a><br/> From fca4023cffbe281e66f72f7c9f7643caf846af86 Mon Sep 17 00:00:00 2001 From: Sam Granger <sam.granger@gmail.com> Date: Tue, 9 Oct 2018 15:37:48 +0200 Subject: [PATCH 099/240] Do not output html for region field due to xss --- .../view/frontend/web/template/billing-address/details.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html index cc1d960bbe44b..ea521b3a8afd4 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html @@ -8,7 +8,7 @@ <text args="currentBillingAddress().prefix"/> <text args="currentBillingAddress().firstname"/> <text args="currentBillingAddress().middlename"/> <text args="currentBillingAddress().lastname"/> <text args="currentBillingAddress().suffix"/><br/> <text args="_.values(currentBillingAddress().street).join(', ')"/><br/> - <text args="currentBillingAddress().city "/>, <span html="currentBillingAddress().region"></span> <text args="currentBillingAddress().postcode"/><br/> + <text args="currentBillingAddress().city "/>, <span text="currentBillingAddress().region"></span> <text args="currentBillingAddress().postcode"/><br/> <text args="getCountryName(currentBillingAddress().countryId)"/><br/> <a if="currentBillingAddress().telephone" attr="'href': 'tel:' + currentBillingAddress().telephone" text="currentBillingAddress().telephone"></a><br/> From e59dee011c199ec0f0fad819ca6be981db4778f1 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Sat, 13 Oct 2018 18:06:00 +0530 Subject: [PATCH 100/240] Fix SKU limit in import new products --- .../Magento/CatalogImportExport/Model/Import/Product.php | 5 +++++ .../CatalogImportExport/Model/Import/Product/Validator.php | 2 ++ 2 files changed, 7 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 19e33ee267da7..7c73f7f7dba2e 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -159,6 +159,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ const URL_KEY = 'url_key'; + /** + * SKU max length + */ + const DB_MAX_SKU_LENGTH = 65; + /** * Attribute cache * diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 60bfdd56a718e..818f28aa5f27a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -69,6 +69,8 @@ protected function textValidation($attrCode, $type) $val = $this->string->cleanString($this->_rowData[$attrCode]); if ($type == 'text') { $valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH; + } else if ($attrCode == Product::COL_SKU) { + $valid = $this->string->strlen($val) < Product::DB_MAX_SKU_LENGTH; } else { $valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH; } From a2b163b8d1dce7d8cc8c1e34a2e0ffa9e83d74d3 Mon Sep 17 00:00:00 2001 From: speedy008 <kajal10395@gmail.com> Date: Sat, 13 Oct 2018 18:25:56 +0530 Subject: [PATCH 101/240] Calender icon in advance pricing alignment solved --- .../backend/web/css/source/components/_calendar-temp.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less index 416fb2a3071a5..032df8cf4373a 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less @@ -43,7 +43,7 @@ height: @action__height; margin-left: -@action__height; overflow: hidden; - position: relative; + position: absolute; vertical-align: top; z-index: 1; From ad09de9f80ff70a2bb4e6a22bce9eabeda40812b Mon Sep 17 00:00:00 2001 From: Luuk Schakenraad <luuk@chessweb.eu> Date: Sat, 13 Oct 2018 13:56:47 +0200 Subject: [PATCH 102/240] Fix disappearing navigation arrows in fotorama zoom --- lib/web/fotorama/fotorama.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js index 7f54002bf499c..4062c501cb798 100644 --- a/lib/web/fotorama/fotorama.js +++ b/lib/web/fotorama/fotorama.js @@ -2092,7 +2092,7 @@ fotoramaVersion = '4.6.4'; o_navTop = opts.navposition === 'top'; classes.remove.push(selectClass); - $arrs.toggle(opts.arrows); + $arrs.toggle(!!opts.arrows); } else { o_nav = false; $arrs.hide(); From 7acc46a590c36f5c8c4292c3c65792c0684eb195 Mon Sep 17 00:00:00 2001 From: Luuk Schakenraad <luuk@chessweb.eu> Date: Sat, 13 Oct 2018 15:47:34 +0200 Subject: [PATCH 103/240] Fix empty cart button --- .../Magento/Checkout/view/frontend/templates/cart/form.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml index 71b1392d5391f..6762033412274 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml @@ -24,7 +24,7 @@ <?php endif ?> <table id="shopping-cart-table" class="cart items data table" - data-mage-init='{"shoppingCart":{"emptyCartButton": "action.clear", + data-mage-init='{"shoppingCart":{"emptyCartButton": ".action.clear", "updateCartActionContainer": "#update_cart_action_container"}}'> <caption role="heading" aria-level="2" class="table-caption"><?= /* @escapeNotVerified */ __('Shopping Cart Items') ?></caption> <thead> From a3b6af1114c0df30e58f6768c3f835934f7c83c8 Mon Sep 17 00:00:00 2001 From: Vincent MARMIESSE <vincent.marmiesse@ph2m.com> Date: Thu, 27 Sep 2018 16:42:55 +0200 Subject: [PATCH 104/240] Do not use new Phrase in Link Current class --- .../Test/Integrity/Library/DependencyTest.php | 29 ------------------- .../View/Element/Html/Link/Current.php | 6 ++-- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php index 81088522bccb2..a4baacbe4d169 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php @@ -96,35 +96,6 @@ function ($file) { ); } - public function testAppCodeUsage() - { - $files = Files::init(); - $componentRegistrar = new ComponentRegistrar(); - $libPaths = $componentRegistrar->getPaths(ComponentRegistrar::LIBRARY); - $invoker = new AggregateInvoker($this); - $invoker( - function ($file) use ($libPaths) { - $content = file_get_contents($file); - foreach ($libPaths as $libPath) { - if (strpos($file, $libPath) === 0) { - $this->assertSame( - 0, - preg_match('~(?<![a-z\\d_:]|->|function\\s)__\\s*\\(~iS', $content), - 'Function __() is defined outside of the library and must not be used there. ' . - 'Replacement suggestion: new \\Magento\\Framework\\Phrase()' - ); - } - } - }, - $files->getPhpFiles( - Files::INCLUDE_PUB_CODE | - Files::INCLUDE_LIBS | - Files::AS_DATA_SET | - Files::INCLUDE_NON_CLASSES - ) - ); - } - /** * @inheritdoc */ diff --git a/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php b/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php index ab1bca9f4548c..43bfd46c1193a 100644 --- a/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php +++ b/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php @@ -104,13 +104,13 @@ protected function _toHtml() if ($this->isCurrent()) { $html = '<li class="nav item current">'; $html .= '<strong>' - . $this->escapeHtml((string)new \Magento\Framework\Phrase($this->getLabel())) + . $this->escapeHtml(__($this->getLabel())) . '</strong>'; $html .= '</li>'; } else { $html = '<li class="nav item' . $highlight . '"><a href="' . $this->escapeHtml($this->getHref()) . '"'; $html .= $this->getTitle() - ? ' title="' . $this->escapeHtml((string)new \Magento\Framework\Phrase($this->getTitle())) . '"' + ? ' title="' . $this->escapeHtml(__($this->getTitle())) . '"' : ''; $html .= $this->getAttributesHtml() . '>'; @@ -118,7 +118,7 @@ protected function _toHtml() $html .= '<strong>'; } - $html .= $this->escapeHtml((string)new \Magento\Framework\Phrase($this->getLabel())); + $html .= $this->escapeHtml(__($this->getLabel())); if ($this->getIsHighlighted()) { $html .= '</strong>'; From 8084aa985c10208c1ee5bfd14ab31014bd4edfd4 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh@Maheshs-MacBook-Air.local> Date: Sun, 14 Oct 2018 15:47:13 +0530 Subject: [PATCH 105/240] White space removed at the end of line --- app/code/Magento/Catalog/Pricing/Price/BasePrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php index 54a13be864db7..9009c8c437e07 100644 --- a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php @@ -30,7 +30,7 @@ public function getValue() $this->value = false; foreach ($this->priceInfo->getPrices() as $price) { if ($price instanceof BasePriceProviderInterface && $price->getValue() !== false) { - $this->value = min($price->getValue(), $this->value ?: $price->getValue()); + $this->value = min($price->getValue(), $this->value !== false ?: $price->getValue()); } } } From 8296637b258a9d03f81304bf00724cee553da029 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh@Maheshs-MacBook-Air.local> Date: Sun, 14 Oct 2018 15:47:13 +0530 Subject: [PATCH 106/240] Issue#18604 fixed in this commit --- app/code/Magento/Catalog/Pricing/Price/BasePrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php index 54a13be864db7..9009c8c437e07 100644 --- a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php @@ -30,7 +30,7 @@ public function getValue() $this->value = false; foreach ($this->priceInfo->getPrices() as $price) { if ($price instanceof BasePriceProviderInterface && $price->getValue() !== false) { - $this->value = min($price->getValue(), $this->value ?: $price->getValue()); + $this->value = min($price->getValue(), $this->value !== false ?: $price->getValue()); } } } From 42839360380bc3783e1840b0ff69eb922e97a99d Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh@Maheshs-MacBook-Air.local> Date: Sun, 14 Oct 2018 17:44:02 +0530 Subject: [PATCH 107/240] The Travis CI build, Fixed --- app/code/Magento/Catalog/Pricing/Price/BasePrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php index 9009c8c437e07..77368517a3155 100644 --- a/app/code/Magento/Catalog/Pricing/Price/BasePrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/BasePrice.php @@ -30,7 +30,7 @@ public function getValue() $this->value = false; foreach ($this->priceInfo->getPrices() as $price) { if ($price instanceof BasePriceProviderInterface && $price->getValue() !== false) { - $this->value = min($price->getValue(), $this->value !== false ?: $price->getValue()); + $this->value = min($price->getValue(), $this->value !== false ? $this->value: $price->getValue()); } } } From e031bdc1007a980150635b2681880ac4475dd71a Mon Sep 17 00:00:00 2001 From: Luuk Schakenraad <luuk@chessweb.eu> Date: Sun, 14 Oct 2018 19:26:43 +0200 Subject: [PATCH 108/240] Fix disappearing navigation arrows in fotorama zoom also in fotorama.min.js --- lib/web/fotorama/fotorama.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/fotorama/fotorama.min.js b/lib/web/fotorama/fotorama.min.js index ea38823c4305b..e8eb9fbda63ef 100644 --- a/lib/web/fotorama/fotorama.min.js +++ b/lib/web/fotorama/fotorama.min.js @@ -1,4 +1,4 @@ /*! * Fotorama 4.6.4 | http://fotorama.io/license/ */ -fotoramaVersion="4.6.4";(function(bo,k,a3,bV,aP){var ag="fotorama",bH="fotorama__fullscreen",ae=ag+"__wrap",ah=ae+"--css2",aX=ae+"--css3",bt=ae+"--video",ar=ae+"--fade",aw=ae+"--slide",P=ae+"--no-controls",aM=ae+"--no-shadows",U=ae+"--pan-y",a0=ae+"--rtl",az=ae+"--only-active",bN=ae+"--no-captions",f=ae+"--toggle-arrows",a7=ag+"__stage",x=a7+"__frame",l=x+"--video",B=a7+"__shaft",aB=ag+"__grab",bC=ag+"__pointer",aK=ag+"__arr",F=aK+"--disabled",bc=aK+"--prev",r=aK+"--next",bO=ag+"__nav",bq=bO+"-wrap",aH=bO+"__shaft",b=bq+"--vertical",ax=bq+"--list",bZ=bq+"--horizontal",bW=bO+"--dots",ai=bO+"--thumbs",aG=bO+"__frame",br=ag+"__fade",al=br+"-front",n=br+"-rear",aW=ag+"__shadow",bz=aW+"s",S=bz+"--left",aL=bz+"--right",a2=bz+"--top",aR=bz+"--bottom",a4=ag+"__active",a9=ag+"__select",bs=ag+"--hidden",M=ag+"--fullscreen",aJ=ag+"__fullscreen-icon",bP=ag+"__error",bM=ag+"__loading",c=ag+"__loaded",b3=c+"--full",bg=c+"--img",bR=ag+"__grabbing",J=ag+"__img",Y=J+"--full",bS=ag+"__thumb",b0=bS+"__arr--left",H=bS+"__arr--right",cb=bS+"-border",bd=ag+"__html",af=ag+"-video-container",bJ=ag+"__video",T=bJ+"-play",w=bJ+"-close",au=ag+"_horizontal_ratio",aY=ag+"_vertical_ratio",ca=ag+"__spinner",Z=ca+"--show";var E=bV&&bV.fn.jquery.split(".");if(!E||E[0]<1||(E[0]==1&&E[1]<8)){throw"Fotorama requires jQuery 1.8 or later and will not run without it."}var bx={};var ap=(function(co,ct,cj){var cf="2.8.3",cm={},cD=ct.documentElement,cE="modernizr",cB=ct.createElement(cE),cp=cB.style,cg,cw={}.toString,cy=" -webkit- -moz- -o- -ms- ".split(" "),cd="Webkit Moz O ms",cG=cd.split(" "),cq=cd.toLowerCase().split(" "),ck={},ce={},cu={},cA=[],cv=cA.slice,cc,cz=function(cQ,cS,cK,cR){var cJ,cP,cM,cN,cI=ct.createElement("div"),cO=ct.body,cL=cO||ct.createElement("body");if(parseInt(cK,10)){while(cK--){cM=ct.createElement("div");cM.id=cR?cR[cK]:cE+(cK+1);cI.appendChild(cM)}}cJ=["­",'<style id="s',cE,'">',cQ,"</style>"].join("");cI.id=cE;(cO?cI:cL).innerHTML+=cJ;cL.appendChild(cI);if(!cO){cL.style.background="";cL.style.overflow="hidden";cN=cD.style.overflow;cD.style.overflow="hidden";cD.appendChild(cL)}cP=cS(cI,cQ);if(!cO){cL.parentNode.removeChild(cL);cD.style.overflow=cN}else{cI.parentNode.removeChild(cI)}return !!cP},cs=({}).hasOwnProperty,cC;if(!cl(cs,"undefined")&&!cl(cs.call,"undefined")){cC=function(cI,cJ){return cs.call(cI,cJ)}}else{cC=function(cI,cJ){return((cJ in cI)&&cl(cI.constructor.prototype[cJ],"undefined"))}}if(!Function.prototype.bind){Function.prototype.bind=function cH(cK){var cL=this;if(typeof cL!="function"){throw new TypeError()}var cI=cv.call(arguments,1),cJ=function(){if(this instanceof cJ){var cO=function(){};cO.prototype=cL.prototype;var cN=new cO();var cM=cL.apply(cN,cI.concat(cv.call(arguments)));if(Object(cM)===cM){return cM}return cN}else{return cL.apply(cK,cI.concat(cv.call(arguments)))}};return cJ}}function cr(cI){cp.cssText=cI}function ci(cJ,cI){return cr(cy.join(cJ+";")+(cI||""))}function cl(cJ,cI){return typeof cJ===cI}function cn(cJ,cI){return !!~(""+cJ).indexOf(cI)}function cF(cK,cI){for(var cJ in cK){var cL=cK[cJ];if(!cn(cL,"-")&&cp[cL]!==cj){return cI=="pfx"?cL:true}}return false}function cx(cJ,cM,cL){for(var cI in cJ){var cK=cM[cJ[cI]];if(cK!==cj){if(cL===false){return cJ[cI]}if(cl(cK,"function")){return cK.bind(cL||cM)}return cK}}return false}function i(cM,cI,cL){var cJ=cM.charAt(0).toUpperCase()+cM.slice(1),cK=(cM+" "+cG.join(cJ+" ")+cJ).split(" ");if(cl(cI,"string")||cl(cI,"undefined")){return cF(cK,cI)}else{cK=(cM+" "+(cq).join(cJ+" ")+cJ).split(" ");return cx(cK,cI,cL)}}ck.touch=function(){var cI;if(("ontouchstart" in co)||co.DocumentTouch&&ct instanceof DocumentTouch){cI=true}else{cz(["@media (",cy.join("touch-enabled),("),cE,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(cJ){cI=cJ.offsetTop===9})}return cI};ck.csstransforms3d=function(){var cI=!!i("perspective");if(cI&&"webkitPerspective" in cD.style){cz("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(cJ,cK){cI=cJ.offsetLeft===9&&cJ.offsetHeight===3})}return cI};ck.csstransitions=function(){return i("transition")};for(var ch in ck){if(cC(ck,ch)){cc=ch.toLowerCase();cm[cc]=ck[ch]();cA.push((cm[cc]?"":"no-")+cc)}}cm.addTest=function(cJ,cK){if(typeof cJ=="object"){for(var cI in cJ){if(cC(cJ,cI)){cm.addTest(cI,cJ[cI])}}}else{cJ=cJ.toLowerCase();if(cm[cJ]!==cj){return cm}cK=typeof cK=="function"?cK():cK;if(typeof enableClasses!=="undefined"&&enableClasses){cD.className+=" "+(cK?"":"no-")+cJ}cm[cJ]=cK}return cm};cr("");cB=cg=null;cm._version=cf;cm._prefixes=cy;cm._domPrefixes=cq;cm._cssomPrefixes=cG;cm.testProp=function(cI){return cF([cI])};cm.testAllProps=i;cm.testStyles=cz;cm.prefixed=function(cK,cJ,cI){if(!cJ){return i(cK,"pfx")}else{return i(cK,cJ,cI)}};return cm})(bo,k);var bB={ok:false,is:function(){return false},request:function(){},cancel:function(){},event:"",prefix:""},h="webkit moz o ms khtml".split(" ");if(typeof k.cancelFullScreen!="undefined"){bB.ok=true}else{for(var bv=0,N=h.length;bv<N;bv++){bB.prefix=h[bv];if(typeof k[bB.prefix+"CancelFullScreen"]!="undefined"){bB.ok=true;break}}}if(bB.ok){bB.event=bB.prefix+"fullscreenchange";bB.is=function(){switch(this.prefix){case"":return k.fullScreen;case"webkit":return k.webkitIsFullScreen;default:return k[this.prefix+"FullScreen"]}};bB.request=function(i){return(this.prefix==="")?i.requestFullScreen():i[this.prefix+"RequestFullScreen"]()};bB.cancel=function(i){return(this.prefix==="")?k.cancelFullScreen():k[this.prefix+"CancelFullScreen"]()}}function a6(i){var cc="bez_"+bV.makeArray(arguments).join("_").replace(".","p");if(typeof bV.easing[cc]!=="function"){var cd=function(ck,ci){var cf=[null,null],cl=[null,null],cj=[null,null],ch=function(cm,cn){cj[cn]=3*ck[cn];cl[cn]=3*(ci[cn]-ck[cn])-cj[cn];cf[cn]=1-cj[cn]-cl[cn];return cm*(cj[cn]+cm*(cl[cn]+cm*cf[cn]))},cg=function(cm){return cj[0]+cm*(2*cl[0]+3*cf[0]*cm)},ce=function(co){var cm=co,cn=0,cp;while(++cn<14){cp=ch(cm,0)-co;if(Math.abs(cp)<0.001){break}cm-=cp/cg(cm)}return cm};return function(cm){return ch(ce(cm),1)}};bV.easing[cc]=function(cf,cg,ce,ci,ch){return ci*cd([i[0],i[1]],[i[2],i[3]])(cg/ch)+ce}}return cc}var bf=bV(bo),bw=bV(k),R,I,bT=a3.hash.replace("#","")==="quirks",ac=ap.csstransforms3d,aA=ac&&!bT,aN=ac||k.compatMode==="CSS1Compat",s=bB.ok,am=navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i),bh=!aA||am,aZ=navigator.msPointerEnabled,y="onwheel" in k.createElement("div")?"wheel":k.onmousewheel!==aP?"mousewheel":"DOMMouseScroll",b8=250,ba=300,bY=1400,bQ=5000,b9=2,L=64,bk=500,bE=333,bu="$stageFrame",b7="$navDotFrame",bl="$navThumbFrame",bF="auto",u=a6([0.1,0,0.25,1]),bK=1200,b4=1,Q={width:null,minwidth:null,maxwidth:"100%",height:null,minheight:null,maxheight:null,ratio:null,margin:b9,nav:"dots",navposition:"bottom",navwidth:null,thumbwidth:L,thumbheight:L,thumbmargin:b9,thumbborderwidth:b9,allowfullscreen:false,transition:"slide",clicktransition:null,transitionduration:ba,captions:true,startindex:0,loop:false,autoplay:false,stopautoplayontouch:true,keyboard:false,arrows:true,click:true,swipe:false,trackpad:false,shuffle:false,direction:"ltr",shadows:true,showcaption:true,navdir:"horizontal",navarrows:true,navtype:"thumbs"},p={left:true,right:true,down:true,up:true,space:false,home:false,end:false};function g(){}function bb(cd,cc,i){return Math.max(isNaN(cc)?-Infinity:cc,Math.min(isNaN(i)?Infinity:i,cd))}function bi(cc,i){return cc.match(/ma/)&&cc.match(/-?\d+(?!d)/g)[cc.match(/3d/)?(i==="vertical"?13:12):(i==="vertical"?5:4)]}function aa(cc,i){if(aA){return +bi(cc.css("transform"),i)}else{return +cc.css(i==="vertical"?"top":"left").replace("px","")}}function b2(cd,cc){var i={};if(aA){switch(cc){case"vertical":i.transform="translate3d(0, "+(cd)+"px,0)";break;case"list":break;default:i.transform="translate3d("+(cd)+"px,0,0)";break}}else{cc==="vertical"?i.top=cd:i.left=cd}return i}function b6(i){return{"transition-duration":i+"ms"}}function aV(cc,i){return isNaN(cc)?i:cc}function m(cc,i){return aV(+String(cc).replace(i||"px",""))}function K(i){return/%$/.test(i)?m(i,"%"):aP}function d(cc,i){return aV(K(cc)/100*i,m(cc))}function t(i){return(!isNaN(m(i))||!isNaN(m(i,"%")))&&i}function a8(cc,cd,ce,i){return(cc-(i||0))*(cd+(ce||0))}function by(ce,cc,cd,i){return -Math.round(ce/(cc+(cd||0))-(i||0))}function aO(cd){var cc=cd.data();if(cc.tEnd){return}var ce=cd[0],i={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",msTransition:"MSTransitionEnd",transition:"transitionend"};D(ce,i[ap.prefixed("transition")],function(cf){cc.tProp&&cf.propertyName.match(cc.tProp)&&cc.onEndFn()});cc.tEnd=true}function X(cd,cf,ce,cg){var cc,i=cd.data();if(i){i.onEndFn=function(){if(cc){return}cc=true;clearTimeout(i.tT);ce()};i.tProp=cf;clearTimeout(i.tT);i.tT=setTimeout(function(){i.onEndFn()},cg*1.5);aO(cd)}}function a1(cd,cf){var cc=cd.navdir||"horizontal";if(cd.length){var i=cd.data();if(aA){cd.css(b6(0));i.onEndFn=g;clearTimeout(i.tT)}else{cd.stop()}var ce=bj(cf,function(){return aa(cd,cc)});cd.css(b2(ce,cc));return ce}}function bj(){var cc;for(var cd=0,i=arguments.length;cd<i;cd++){cc=cd?arguments[cd]():arguments[cd];if(typeof cc==="number"){break}}return cc}function aF(cc,i){return Math.round(cc+((i-cc)/1.5))}function aU(){aU.p=aU.p||(a3.protocol==="https:"?"https://":"http://");return aU.p}function ak(cc){var i=k.createElement("a");i.href=cc;return i}function at(i,cd){if(typeof i!=="string"){return i}i=ak(i);var cf,ce;if(i.host.match(/youtube\.com/)&&i.search){cf=i.search.split("v=")[1];if(cf){var cc=cf.indexOf("&");if(cc!==-1){cf=cf.substring(0,cc)}ce="youtube"}}else{if(i.host.match(/youtube\.com|youtu\.be/)){cf=i.pathname.replace(/^\/(embed\/|v\/)?/,"").replace(/\/.*/,"");ce="youtube"}else{if(i.host.match(/vimeo\.com/)){ce="vimeo";cf=i.pathname.replace(/^\/(video\/)?/,"").replace(/\/.*/,"")}}}if((!cf||!ce)&&cd){cf=i.href;ce="custom"}return cf?{id:cf,type:ce,s:i.search.replace(/^\?/,""),p:aU()}:false}function aQ(cg,ce,cf){var cc,i,cd=cg.video;if(cd.type==="youtube"){i=aU()+"img.youtube.com/vi/"+cd.id+"/default.jpg";cc=i.replace(/\/default.jpg$/,"/hqdefault.jpg");cg.thumbsReady=true}else{if(cd.type==="vimeo"){bV.ajax({url:aU()+"vimeo.com/api/v2/video/"+cd.id+".json",dataType:"jsonp",success:function(ch){cg.thumbsReady=true;v(ce,{img:ch[0].thumbnail_large,thumb:ch[0].thumbnail_small},cg.i,cf)}})}else{cg.thumbsReady=true}}return{img:cc,thumb:i}}function v(ch,cd,cf,ci){for(var cg=0,ce=ch.length;cg<ce;cg++){var cj=ch[cg];if(cj.i===cf&&cj.thumbsReady){var cc={videoReady:true};cc[bu]=cc[bl]=cc[b7]=false;ci.splice(cg,1,bV.extend({},cj,cc,cd));break}}}function bI(cc){var ce=[];function cd(cf,ch,cn){var ci=cf.children("img").eq(0),cm=cf.attr("href"),ck=cf.attr("src"),cl=ci.attr("src"),cj=ch.video,cg=cn?at(cm,cj===true):false;if(cg){cm=false}else{cg=cj}i(cf,ci,bV.extend(ch,{video:cg,img:ch.img||cm||ck||cl,thumb:ch.thumb||cl||ck||cm}))}function i(cg,cj,ck){var ci=ck.thumb&&ck.img!==ck.thumb,ch=m(ck.width||cg.attr("width")),cf=m(ck.height||cg.attr("height"));bV.extend(ck,{width:ch,height:cf,thumbratio:bm(ck.thumbratio||(m(ck.thumbwidth||(cj&&cj.attr("width"))||ci||ch)/m(ck.thumbheight||(cj&&cj.attr("height"))||ci||cf)))})}cc.children().each(function(){var cf=bV(this),cg=bA(bV.extend(cf.data(),{id:cf.attr("id")}));if(cf.is("a, img")){cd(cf,cg,true)}else{if(!cf.is(":empty")){i(cf,null,bV.extend(cg,{html:this,_html:cf.html()}))}else{return}}ce.push(cg)});return ce}function W(i){return i.offsetWidth===0&&i.offsetHeight===0}function be(i){return !bV.contains(k.documentElement,i)}function bX(cf,cd,ce,cc){if(!bX.i){bX.i=1;bX.ii=[true]}cc=cc||bX.i;if(typeof bX.ii[cc]==="undefined"){bX.ii[cc]=true}if(cf()){cd()}else{bX.ii[cc]&&setTimeout(function(){bX.ii[cc]&&bX(cf,cd,ce,cc)},ce||100)}return bX.i++}bX.stop=function(cc){bX.ii[cc]=false};function V(ce,cd){var cc=ce.data(),cg=cc.measures;if(cg&&(!cc.l||cc.l.W!==cg.width||cc.l.H!==cg.height||cc.l.r!==cg.ratio||cc.l.w!==cd.w||cc.l.h!==cd.h)){var i=bb(cd.h,0,cg.height),cf=i*cg.ratio;aS.setRatio(ce,cf,i);cc.l={W:cg.width,H:cg.height,r:cg.ratio,w:cd.w,h:cd.h}}return true}function an(i,cd){var cc=i[0];if(cc.styleSheet){cc.styleSheet.cssText=cd}else{i.html(cd)}}function bp(ce,cd,i,cc){return cd===i?false:cc==="vertical"?(ce<=cd?"top":ce>=i?"bottom":"top bottom"):(ce<=cd?"left":ce>=i?"right":"left right")}function z(cc,cd,i){i=i||{};cc.each(function(){var cg=bV(this),cf=cg.data(),ce;if(cf.clickOn){return}cf.clickOn=true;bV.extend(aI(cg,{onStart:function(ch){ce=ch;(i.onStart||g).call(this,ch)},onMove:i.onMove||g,onTouchEnd:i.onTouchEnd||g,onEnd:function(ch){if(ch.moved){return}cd.call(this,ce)}}),{noMove:true})})}function ab(i,cc){return'<div class="'+i+'">'+(cc||"")+"</div>"}function aT(i){return"."+i}function q(i){var cc='<iframe src="'+i.p+i.type+".com/embed/"+i.id+'" frameborder="0" allowfullscreen></iframe>';return cc}function aC(cf){var cc=cf.length;while(cc){var ce=Math.floor(Math.random()*cc--);var cd=cf[cc];cf[cc]=cf[ce];cf[ce]=cd}return cf}function bG(i){return Object.prototype.toString.call(i)=="[object Array]"&&bV.map(i,function(cc){return bV.extend({},cc)})}function bU(i,cd,cc){i.scrollLeft(cd||0).scrollTop(cc||0)}function bA(i){if(i){var cc={};bV.each(i,function(cd,ce){cc[cd.toLowerCase()]=ce});return cc}}function bm(i){if(!i){return}var cc=+i;if(!isNaN(cc)){return cc}else{cc=i.split("/");return +cc[0]/+cc[1]||aP}}function D(cd,ce,cc,i){if(!ce){return}cd.addEventListener?cd.addEventListener(ce,cc,!!i):cd.attachEvent("on"+ce,cc)}function a5(i,cc){if(i>cc.max){i=cc.max}else{if(i<cc.min){i=cc.min}}return i}function aD(i,ck,ch,cf,ce,cd,cc){var cg,cj,ci;if(cc==="horizontal"){cj=i.thumbwidth;ci=cd.width()}else{cj=i.thumbheight;ci=cd.height()}if((cj+i.margin)*(ch+1)>=(ci-cf)){if(cc==="horizontal"){cg=-ce.position().left}else{cg=-ce.position().top}}else{if((cj+i.margin)*(ch)<=Math.abs(cf)){if(cc==="horizontal"){cg=-ce.position().left+ci-(cj+i.margin)}else{cg=-ce.position().top+ci-(cj+i.margin)}}else{cg=cf}}cg=a5(cg,ck);return cg||0}function aj(i){return !!i.getAttribute("disabled")}function ad(cc,i){if(i){return{disabled:cc}}else{return{tabindex:cc*-1+"",disabled:cc}}}function a(cc,i){D(cc,"keyup",function(cd){aj(cc)||cd.keyCode==13&&i.call(cc,cd)})}function bL(cc,i){D(cc,"focus",cc.onfocusin=function(cd){i.call(cc,cd)},true)}function O(cc,i){cc.preventDefault?cc.preventDefault():(cc.returnValue=false);i&&cc.stopPropagation&&cc.stopPropagation()}function aE(cd,cc){var i=/iP(ad|hone|od)/i.test(bo.navigator.userAgent);if(i&&cc==="touchend"){cd.on("touchend",function(ce){bw.trigger("mouseup",ce)})}cd.on(cc,function(ce){O(ce,true);return false})}function ay(i){return i?">":"<"}var aS=(function(){function cd(ch,ce,cg){var cf=ce/cg;if(cf<=1){ch.parent().removeClass(au);ch.parent().addClass(aY)}else{ch.parent().removeClass(aY);ch.parent().addClass(au)}}function i(cf,cg,ch){var ce=ch;if(!cf.attr(ce)&&cf.attr(ce)!==aP){cf.attr(ce,cg)}if(cf.find("["+ce+"]").length){cf.find("["+ce+"]").each(function(){bV(this).attr(ce,cg)})}}function cc(cf,ce,ci){var cg=false,ch;cf.showCaption===ci||cf.showCaption===true?ch=true:ch=false;if(!ce){return false}if(cf.caption&&ch){cg=true}return cg}return{setRatio:cd,setThumbAttr:i,isExpectedCaption:cc}}(aS||{},jQuery));function A(ce,cd){var cc=ce.data(),i=Math.round(cd.pos),cf=function(){if(cc&&cc.sliding){cc.sliding=false}(cd.onEnd||g)()};if(typeof cd.overPos!=="undefined"&&cd.overPos!==cd.pos){i=cd.overPos}var cg=bV.extend(b2(i,cd.direction),cd.width&&{width:cd.width},cd.height&&{height:cd.height});if(cc&&cc.sliding){cc.sliding=true}if(aA){ce.css(bV.extend(b6(cd.time),cg));if(cd.time>10){X(ce,"transform",cf,cd.time)}else{cf()}}else{ce.stop().animate(cg,cd.time,u,cf)}}function aq(ck,cj,cc,cm,ce,i){var ch=typeof i!=="undefined";if(!ch){ce.push(arguments);Array.prototype.push.call(arguments,ce.length);if(ce.length>1){return}}ck=ck||bV(ck);cj=cj||bV(cj);var ci=ck[0],cg=cj[0],cf=cm.method==="crossfade",cl=function(){if(!cl.done){cl.done=true;var cn=(ch||ce.shift())&&ce.shift();cn&&aq.apply(this,cn);(cm.onEnd||g)(!!cn)}},cd=cm.time/(i||1);cc.removeClass(n+" "+al);ck.stop().addClass(n);cj.stop().addClass(al);cf&&cg&&ck.fadeTo(0,0);ck.fadeTo(cf?cd:0,1,cf&&cl);cj.fadeTo(cd,0,cl);(ci&&cf)||cg||cl()}var G,b5,e,j,bD;function bn(i){var cc=(i.touches||[])[0]||i;i._x=cc.pageX||cc.originalEvent.pageX;i._y=cc.clientY||cc.originalEvent.clientY;i._now=bV.now()}function aI(cr,cg){var cc=cr[0],cj={},i,cl,cf,cn,cs,cd,ce,co,ch;function cq(ct){cf=bV(ct.target);cj.checked=cd=ce=ch=false;if(i||cj.flow||(ct.touches&&ct.touches.length>1)||ct.which>1||(G&&G.type!==ct.type&&e)||(cd=cg.select&&cf.is(cg.select,cc))){return cd}cs=ct.type==="touchstart";ce=cf.is("a, a *",cc);cn=cj.control;co=(cj.noMove||cj.noSwipe||cn)?16:!cj.snap?4:0;bn(ct);cl=G=ct;b5=ct.type.replace(/down|start/,"move").replace(/Down/,"Move");(cg.onStart||g).call(cc,ct,{control:cn,$target:cf});i=cj.flow=true;if(!cs||cj.go){O(ct)}}function ck(cx){if((cx.touches&&cx.touches.length>1)||(aZ&&!cx.isPrimary)||b5!==cx.type||!i){i&&ci();(cg.onTouchEnd||g)();return}bn(cx);var cy=Math.abs(cx._x-cl._x),cu=Math.abs(cx._y-cl._y),cw=cy-cu,cv=(cj.go||cj.x||cw>=0)&&!cj.noSwipe,ct=cw<0;if(cs&&!cj.checked){if(i=cv){O(cx)}}else{O(cx);(cg.onMove||g).call(cc,cx,{touch:cs})}if(!ch&&Math.sqrt(Math.pow(cy,2)+Math.pow(cu,2))>co){ch=true}cj.checked=cj.checked||cv||ct}function ci(cu){(cg.onTouchEnd||g)();var ct=i;cj.control=i=false;if(ct){cj.flow=false}if(!ct||(ce&&!cj.checked)){return}cu&&O(cu);e=true;clearTimeout(j);j=setTimeout(function(){e=false},1000);(cg.onEnd||g).call(cc,{moved:ch,$target:cf,control:cn,touch:cs,startEvent:cl,aborted:!cu||cu.type==="MSPointerCancel"})}function cm(){if(cj.flow){return}cj.flow=true}function cp(){if(!cj.flow){return}cj.flow=false}if(aZ){D(cc,"MSPointerDown",cq);D(k,"MSPointerMove",ck);D(k,"MSPointerCancel",ci);D(k,"MSPointerUp",ci)}else{D(cc,"touchstart",cq);D(cc,"touchmove",ck);D(cc,"touchend",ci);D(k,"touchstart",cm);D(k,"touchend",cp);D(k,"touchcancel",cp);bf.on("scroll",cp);cr.on("mousedown pointerdown",cq);bw.on("mousemove pointermove",ck).on("mouseup pointerup",ci)}if(ap.touch){bD="a"}else{bD="div"}cr.on("click",bD,function(ct){cj.checked&&O(ct)});return cj}function ao(cz,cd){var cc=cz[0],ce=cz.data(),cm={},cw,cf,cx,cj,ch,cy,co,cg,cr,ct,cp,cq,i,cv,ci,cn;function cs(cA,cB){cn=true;cw=cf=(cq==="vertical")?cA._y:cA._x;co=cA._now;cy=[[co,cw]];cx=cj=cm.noMove||cB?0:a1(cz,(cd.getPos||g)());(cd.onStart||g).call(cc,cA)}function cu(cB,cA){cr=cm.min;ct=cm.max;cp=cm.snap,cq=cm.direction||"horizontal",cz.navdir=cq;i=cB.altKey;cn=ci=false;cv=cA.control;if(!cv&&!ce.sliding){cs(cB)}}function cl(cB,cA){if(!cm.noSwipe){if(!cn){cs(cB)}cf=(cq==="vertical")?cB._y:cB._x;cy.push([cB._now,cf]);cj=cx-(cw-cf);ch=bp(cj,cr,ct,cq);if(cj<=cr){cj=aF(cj,cr)}else{if(cj>=ct){cj=aF(cj,ct)}}if(!cm.noMove){cz.css(b2(cj,cq));if(!ci){ci=true;cA.touch||aZ||cz.addClass(bR)}(cd.onMove||g).call(cc,cB,{pos:cj,edge:ch})}}}function ck(cJ){if(cm.noSwipe&&cJ.moved){return}if(!cn){cs(cJ.startEvent,true)}cJ.touch||aZ||cz.removeClass(bR);cg=bV.now();var cG=cg-b8,cK,cP,cQ,cS=null,cA,cE,cN,cD,cF,cI=ba,cO,cH=cd.friction;for(var cC=cy.length-1;cC>=0;cC--){cK=cy[cC][0];cP=Math.abs(cK-cG);if(cS===null||cP<cQ){cS=cK;cA=cy[cC][1]}else{if(cS===cG||cP>cQ){break}}cQ=cP}cD=bb(cj,cr,ct);var cT=cA-cf,cR=cT>=0,cL=cg-cS,cB=cL>b8,cM=!cB&&cj!==cx&&cD===cj;if(cp){cD=bb(Math[cM?(cR?"floor":"ceil"):"round"](cj/cp)*cp,cr,ct);cr=ct=cD}if(cM&&(cp||cD===cj)){cO=-(cT/cL);cI*=bb(Math.abs(cO),cd.timeLow,cd.timeHigh);cE=Math.round(cj+cO*cI/cH);if(!cp){cD=cE}if(!cR&&cE>ct||cR&&cE<cr){cN=cR?cr:ct;cF=cE-cN;if(!cp){cD=cN}cF=bb(cD+cF*0.03,cN-50,cN+50);cI=Math.abs((cj-cF)/(cO/cH))}}cI*=i?10:1;(cd.onEnd||g).call(cc,bV.extend(cJ,{moved:cJ.moved||cB&&cp,pos:cj,newPos:cD,overPos:cF,time:cI,dir:cq}))}cm=bV.extend(aI(cd.$wrap,bV.extend({},cd,{onStart:cu,onMove:cl,onEnd:ck})),cm);return cm}function o(ce,cd){var cg=ce[0],ch,cf,i,cc={prevent:{}};D(cg,y,function(co){var cl=co.wheelDeltaY||-1*co.deltaY||0,cn=co.wheelDeltaX||-1*co.deltaX||0,ck=Math.abs(cn)&&!Math.abs(cl),cm=ay(cn<0),cp=cf===cm,ci=bV.now(),cj=ci-i<b8;cf=cm;i=ci;if(!ck||!cc.ok||cc.prevent[cm]&&!ch){return}else{O(co,true);if(ch&&cp&&cj){return}}if(cd.shift){ch=true;clearTimeout(cc.t);cc.t=setTimeout(function(){ch=false},bY)}(cd.onEnd||g)(co,cd.shift?cm:cn)});return cc}jQuery.Fotorama=function(d6,c3){R=bV("html");I=bV("body");var cg=this,cC=bV.now(),cQ=ag+cC,eu=d6[0],dP,cY=1,cR=d6.data(),c1,de=bV("<style></style>"),c9=bV(ab(bs)),dk=d6.find(aT(ae)),cf=dk.find(aT(a7)),dY=cf[0],cl=d6.find(aT(B)),c8=bV(),dW=d6.find(aT(bc)),da=d6.find(aT(r)),cU=d6.find(aT(aK)),dU=d6.find(aT(bq)),dO=dU.find(aT(bO)),cF=dO.find(aT(aH)),dA,cB=bV(),cW=bV(),dS=cl.data(),cX=cF.data(),c7=d6.find(aT(cb)),eg=d6.find(aT(b0)),dX=d6.find(aT(H)),dM=d6.find(aT(aJ)),dD=dM[0],cH=bV(ab(T)),dt=d6.find(aT(w)),d1=dt[0],eb=d6.find(aT(ca)),dg,eo=false,dF,ea,c2,ed,dw,d4,cN,cK,dx,dj,cq,c0,d8,c4,d2,cv,ch,ej,ds,cu,ec,dH,dE,d0={},en={},dG,d5={},cG={},dy={},ef={},cs,cT,ee,cj,el,cd={},er={},dZ,c6,dz,dr,d3=0,cI=[];dk[bu]=bV('<div class="'+x+'"></div>');dk[bl]=bV(bV.Fotorama.jst.thumb());dk[b7]=bV(bV.Fotorama.jst.dots());cd[bu]=[];cd[bl]=[];cd[b7]=[];er[bu]={};dk.addClass(aA?aX:ah);cR.fotorama=this;function ep(){bV.each(dP,function(ey,eA){if(!eA.i){eA.i=cY++;var ez=at(eA.video,true);if(ez){var ex={};eA.video=ez;if(!eA.img&&!eA.thumb){ex=aQ(eA,dP,cg)}else{eA.thumbsReady=true}v(dP,{img:ex.img,thumb:ex.thumb},eA.i,cg)}}})}function df(ex){return dE[ex]}function i(){if(cf!==aP){if(c3.navdir=="vertical"){var ex=c3.thumbwidth+c3.thumbmargin;cf.css("left",ex);da.css("right",ex);dM.css("right",ex);dk.css("width",dk.css("width")+ex);cl.css("max-width",dk.width()-ex)}else{cf.css("left","");da.css("right","");dM.css("right","");dk.css("width",dk.css("width")+ex);cl.css("max-width","")}}}function ek(eB){var eC="keydown."+ag,eD=ag+cC,ex="keydown."+eD,eA="keyup."+eD,ey="resize."+eD+" orientationchange."+eD,ez;if(eB){bw.on(ex,function(eG){var eF,eE;if(dg&&eG.keyCode===27){eF=true;cO(dg,true,true)}else{if(cg.fullScreen||(c3.keyboard&&!cg.index)){if(eG.keyCode===27){eF=true;cg.cancelFullScreen()}else{if((eG.shiftKey&&eG.keyCode===32&&df("space"))||(!eG.altKey&&!eG.metaKey&&eG.keyCode===37&&df("left"))||(eG.keyCode===38&&df("up")&&bV(":focus").attr("data-gallery-role"))){cg.longPress.progress();eE="<"}else{if((eG.keyCode===32&&df("space"))||(!eG.altKey&&!eG.metaKey&&eG.keyCode===39&&df("right"))||(eG.keyCode===40&&df("down")&&bV(":focus").attr("data-gallery-role"))){cg.longPress.progress();eE=">"}else{if(eG.keyCode===36&&df("home")){cg.longPress.progress();eE="<<"}else{if(eG.keyCode===35&&df("end")){cg.longPress.progress();eE=">>"}}}}}}}(eF||eE)&&O(eG);ez={index:eE,slow:eG.altKey,user:true};eE&&(cg.longPress.inProgress?cg.showWhileLongPress(ez):cg.show(ez))});if(eB){bw.on(eA,function(eE){if(cg.longPress.inProgress){cg.showEndLongPress({user:true})}cg.longPress.reset()})}if(!cg.index){bw.off(eC).on(eC,"textarea, input, select",function(eE){!I.hasClass(bH)&&eE.stopPropagation()})}bf.on(ey,cg.resize)}else{bw.off(ex);bf.off(ey)}}function dd(ex){if(ex===dd.f){return}if(ex){d6.addClass(ag+" "+cQ).before(c9).before(de);C(cg)}else{c9.detach();de.detach();d6.html(cR.urtext).removeClass(cQ);av(cg)}ek(ex);dd.f=ex}function dn(){dP=cg.data=dP||bG(c3.data)||bI(d6);c1=cg.size=dP.length;eq.ok&&c3.shuffle&&aC(dP);ep();eo=cn(eo);c1&&dd(true)}function em(){var ex=c1<2||dg;d5.noMove=ex||cv;d5.noSwipe=ex||!c3.swipe;!cu&&cl.toggleClass(aB,!c3.click&&!d5.noMove&&!d5.noSwipe);aZ&&dk.toggleClass(U,!d5.noSwipe)}function dq(ex){if(ex===true){ex=""}c3.autoplay=Math.max(+ex||bQ,ds*1.5)}function db(ex){if(ex.navarrows&&ex.nav==="thumbs"){eg.show();dX.show()}else{eg.hide();dX.hide()}}function ck(ex,ey){return Math.floor(dk.width()/(ey.thumbwidth+ey.thumbmargin))}function dQ(){if(!c3.nav||c3.nav==="dots"){c3.navdir="horizontal"}cg.options=c3=bA(c3);b4=ck(dk,c3);cv=(c3.transition==="crossfade"||c3.transition==="dissolve");dj=c3.loop&&(c1>2||(cv&&(!cu||cu!=="slide")));ds=+c3.transitionduration||ba;dH=c3.direction==="rtl";dE=bV.extend({},c3.keyboard&&p,c3.keyboard);db(c3);var ey={add:[],remove:[]};function ex(ez,eA){ey[ez?"add":"remove"].push(eA)}if(c1>1){cq=c3.nav;d8=c3.navposition==="top";ey.remove.push(a9);cU.toggle(c3.arrows)}else{cq=false;cU.hide()}dh();cJ();ev();if(c3.autoplay){dq(c3.autoplay)}ch=m(c3.thumbwidth)||L;ej=m(c3.thumbheight)||L;cG.ok=ef.ok=c3.trackpad&&!bh;em();dL(c3,[en]);c0=cq==="thumbs";if(dU.filter(":hidden")&&!!cq){dU.show()}if(c0){dl(c1,"navThumb");dA=cW;dr=bl;an(de,bV.Fotorama.jst.style({w:ch,h:ej,b:c3.thumbborderwidth,m:c3.thumbmargin,s:cC,q:!aN}));dO.addClass(ai).removeClass(bW)}else{if(cq==="dots"){dl(c1,"navDot");dA=cB;dr=b7;dO.addClass(bW).removeClass(ai)}else{dU.hide();cq=false;dO.removeClass(ai+" "+bW)}}if(cq){if(d8){dU.insertBefore(cf)}else{dU.insertAfter(cf)}cz.nav=false;cz(dA,cF,"nav")}c4=c3.allowfullscreen;if(c4){dM.prependTo(cf);d2=s&&c4==="native";aE(dM,"touchend")}else{dM.detach();d2=false}ex(cv,ar);ex(!cv,aw);ex(!c3.captions,bN);ex(dH,a0);ex(c3.arrows,f);ec=c3.shadows&&!bh;ex(!ec,aM);dk.addClass(ey.add.join(" ")).removeClass(ey.remove.join(" "));d0=bV.extend({},c3);i()}function cZ(ex){return ex<0?(c1+(ex%c1))%c1:ex>=c1?ex%c1:ex}function cn(ex){return bb(ex,0,c1-1)}function du(ex){return dj?cZ(ex):cn(ex)}function dB(ex){return ex>0||dj?ex-1:false}function ci(ex){return ex<c1-1||dj?ex+1:false}function d9(){d5.min=dj?-Infinity:-a8(c1-1,en.w,c3.margin,c2);d5.max=dj?Infinity:-a8(0,en.w,c3.margin,c2);d5.snap=en.w+c3.margin}function c5(){var ex=(c3.navdir==="vertical");var ez=ex?cF.height():cF.width();var ey=ex?en.h:en.nw;dy.min=Math.min(0,ey-ez);dy.max=0;dy.direction=c3.navdir;cF.toggleClass(aB,!(dy.noMove=dy.min===dy.max))}function dm(ey,eA,ez){if(typeof ey==="number"){ey=new Array(ey);var ex=true}return bV.each(ey,function(eD,eB){if(ex){eB=eD}if(typeof eB==="number"){var eF=dP[cZ(eB)];if(eF){var eC="$"+eA+"Frame",eE=eF[eC];ez.call(this,eD,eB,eF,eE,eC,eE&&eE.data())}}})}function cc(eA,ex,ez,ey){if(!dG||(dG==="*"&&ey===dx)){eA=t(c3.width)||t(eA)||bk;ex=t(c3.height)||t(ex)||bE;cg.resize({width:eA,ratio:c3.ratio||ez||eA/ex},0,ey!==dx&&"*")}}function cx(ex,ey,eA,ez){dm(ex,ey,function(eM,eE,eD,eC,eR,eB){if(!eC){return}var eN=cg.fullScreen&&!eB.$full&&ey==="stage";if(eB.$img&&!ez&&!eN){return}var eS=new Image(),eG=bV(eS),eO=eG.data();eB[eN?"$full":"$img"]=eG;var eJ=ey==="stage"?(eN?"full":"img"):"thumb",eF=eD[eJ],eP=eN?eD.img:eD[ey==="stage"?"thumb":"img"];if(ey==="navThumb"){eC=eB.$wrap}function eH(eT){var eU=cZ(eE);dc(eT,{index:eU,src:eF,frame:dP[eU]})}function eK(){eG.remove();bV.Fotorama.cache[eF]="error";if((!eD.html||ey!=="stage")&&eP&&eP!==eF){eD[eJ]=eF=eP;eB.$full=null;cx([eE],ey,eA,true)}else{if(eF&&!eD.html&&!eN){eC.trigger("f:error").removeClass(bM).addClass(bP);eH("error")}else{if(ey==="stage"){eC.trigger("f:load").removeClass(bM+" "+bP).addClass(c);eH("load");cc()}}eB.state="error";if(c1>1&&dP[eE]===eD&&!eD.html&&!eD.deleted&&!eD.video&&!eN){eD.deleted=true;cg.splice(eE,1)}}}function eL(){bV.Fotorama.measures[eF]=eO.measures=bV.Fotorama.measures[eF]||{width:eS.width,height:eS.height,ratio:eS.width/eS.height};cc(eO.measures.width,eO.measures.height,eO.measures.ratio,eE);eG.off("load error").addClass(""+(eN?Y:J)).attr("aria-hidden","false").prependTo(eC);if(eC.hasClass(x)&&!eC.hasClass(af)){eC.attr("href",eG.attr("src"))}V(eG,(bV.isFunction(eA)?eA():eA)||en);bV.Fotorama.cache[eF]=eB.state="loaded";setTimeout(function(){eC.trigger("f:load").removeClass(bM+" "+bP).addClass(c+" "+(eN?b3:bg));if(ey==="stage"){eH("load")}else{if(eD.thumbratio===bF||!eD.thumbratio&&c3.thumbratio===bF){eD.thumbratio=eO.measures.ratio;dV()}}},0)}if(!eF){eK();return}function eI(){var eT=10;bX(function(){return !c6||!eT--&&!bh},function(){eL()})}if(!bV.Fotorama.cache[eF]){bV.Fotorama.cache[eF]="*";eG.on("load",eI).on("error",eK)}else{(function eQ(){if(bV.Fotorama.cache[eF]==="error"){eK()}else{if(bV.Fotorama.cache[eF]==="loaded"){setTimeout(eI,0)}else{setTimeout(eQ,100)}}})()}eB.state="";eS.src=eF;if(eB.data.caption){eS.alt=eB.data.caption||""}if(eB.data.full){bV(eS).data("original",eB.data.full)}if(aS.isExpectedCaption(eD,c3.showcaption)){bV(eS).attr("aria-labelledby",eD.labelledby)}})}function cy(){var ex=dF[bu];if(ex&&!ex.data().state){eb.addClass(Z);ex.on("f:load f:error",function(){ex.off("f:load f:error");eb.removeClass(Z)})}}function cL(ex){a(ex,dJ);bL(ex,function(){setTimeout(function(){bU(dO)},0);dT({time:ds,guessIndex:bV(this).data().eq,minMax:dy})})}function dl(ex,ey){dm(ex,ey,function(eB,ez,eG,eD,eA,eC){if(eD){return}eD=eG[eA]=dk[eA].clone();eC=eD.data();eC.data=eG;var eF=eD[0],eE="labelledby"+bV.now();if(ey==="stage"){if(eG.html){bV('<div class="'+bd+'"></div>').append(eG._html?bV(eG.html).removeAttr("id").html(eG._html):eG.html).appendTo(eD)}if(eG.id){eE=eG.id||eE}eG.labelledby=eE;if(aS.isExpectedCaption(eG,c3.showcaption)){bV(bV.Fotorama.jst.frameCaption({caption:eG.caption,labelledby:eE})).appendTo(eD)}eG.video&&eD.addClass(l).append(cH.clone());bL(eF,function(){setTimeout(function(){bU(cf)},0);cm({index:eC.eq,user:true})});c8=c8.add(eD)}else{if(ey==="navDot"){cL(eF);cB=cB.add(eD)}else{if(ey==="navThumb"){cL(eF);eC.$wrap=eD.children(":first");cW=cW.add(eD);if(eG.video){eC.$wrap.append(cH.clone())}}}}})}function cM(ey,ex){return ey&&ey.length&&V(ey,ex)}function di(ex){dm(ex,"stage",function(eB,ez,eE,eD,eA,eC){if(!eD){return}var ey=cZ(ez);eC.eq=ey;er[bu][ey]=eD.css(bV.extend({left:cv?0:a8(ez,en.w,c3.margin,c2)},cv&&b6(0)));if(be(eD[0])){eD.appendTo(cl);cO(eE.$video)}cM(eC.$img,en);cM(eC.$full,en);if(eD.hasClass(x)&&!(eD.attr("aria-hidden")==="false"&&eD.hasClass(a4))){eD.attr("aria-hidden","true")}})}function dp(eB,ex){var ey,ez,eA;if(cq!=="thumbs"||isNaN(eB)){return}ey=-eB;ez=-eB+en.nw;if(c3.navdir==="vertical"){eB=eB-c3.thumbheight;ez=-eB+en.h}cW.each(function(){var eH=bV(this),eD=eH.data(),eC=eD.eq,eG=function(){return{h:ej,w:eD.w}},eF=eG(),eE=c3.navdir==="vertical"?eD.t>ez:eD.l>ez;eF.w=eD.w;if(eD.l+eD.w<ey||eE||cM(eD.$img,eF)){return}ex&&cx([eC],"navThumb",eG)})}function cz(ex,eC,ey){if(!cz[ey]){var eB=ey==="nav"&&c0,eA=0,ez=0;eC.append(ex.filter(function(){var eH,eG=bV(this),eE=eG.data();for(var eF=0,eD=dP.length;eF<eD;eF++){if(eE.data===dP[eF]){eH=true;eE.eq=eF;break}}return eH||eG.remove()&&false}).sort(function(eE,eD){return bV(eE).data().eq-bV(eD).data().eq}).each(function(){var eE=bV(this),eD=eE.data();aS.setThumbAttr(eE,eD.data.caption,"aria-label")}).each(function(){if(!eB){return}var eF=bV(this),eE=eF.data(),eG=Math.round(ej*eE.data.thumbratio)||ch,eD=Math.round(ch/eE.data.thumbratio)||ej;eE.t=ez;eE.h=eD;eE.l=eA;eE.w=eG;eF.css({width:eG});ez+=eD+c3.thumbmargin;eA+=eG+c3.thumbmargin}));cz[ey]=true}}function eh(ex){return ex-d3>en.w/3}function cE(ex){return !dj&&(!(eo+ex)||!(eo-c1+ex))&&!dg}function dh(){var ey=cE(0),ex=cE(1);dW.toggleClass(F,ey).attr(ad(ey,false));da.toggleClass(F,ex).attr(ad(ex,false))}function ev(){var ex=false,ey=false;if(c3.navtype==="thumbs"&&!c3.loop){(eo==0)?ex=true:ex=false;(eo==c3.data.length-1)?ey=true:ey=false}if(c3.navtype==="slides"){var ez=aa(cF,c3.navdir);ez>=dy.max?ex=true:ex=false;ez<=dy.min?ey=true:ey=false}eg.toggleClass(F,ex).attr(ad(ex,true));dX.toggleClass(F,ey).attr(ad(ey,true))}function cJ(){if(cG.ok){cG.prevent={"<":cE(0),">":cE(1)}}}function dI(eD){var eA=eD.data(),eC,eB,ez,ex;if(c0){eC=eA.l;eB=eA.t;ez=eA.w;ex=eA.h}else{eC=eD.position().left;ez=eD.width()}var ey={c:eC+ez/2,min:-eC+c3.thumbmargin*10,max:-eC+en.w-ez-c3.thumbmargin*10};var eE={c:eB+ex/2,min:-eB+c3.thumbmargin*10,max:-eB+en.h-ex-c3.thumbmargin*10};return c3.navdir==="vertical"?eE:ey}function d7(ey){var ex=dF[dr].data();A(c7,{time:ey*1.2,pos:(c3.navdir==="vertical"?ex.t:ex.l),width:ex.w,height:ex.h,direction:c3.navdir})}function dT(eH){var eB=dP[eH.guessIndex][dr],ez=c3.navtype;var eD,ex,eA,eG,eC,ey,eE,eF;if(eB){if(ez==="thumbs"){eD=dy.min!==dy.max;eA=eH.minMax||eD&&dI(dF[dr]);eG=eD&&(eH.keep&&dT.t?dT.l:bb((eH.coo||en.nw/2)-dI(eB).c,eA.min,eA.max));eC=eD&&(eH.keep&&dT.l?dT.l:bb((eH.coo||en.nw/2)-dI(eB).c,eA.min,eA.max));ey=(c3.navdir==="vertical"?eG:eC);eE=eD&&bb(ey,dy.min,dy.max)||0;ex=eH.time*1.1;A(cF,{time:ex,pos:eE,direction:c3.navdir,onEnd:function(){dp(eE,true);ev()}});co(dO,bp(eE,dy.min,dy.max,c3.navdir));dT.l=ey}else{eF=aa(cF,c3.navdir);ex=eH.time*1.11;eE=aD(c3,dy,eH.guessIndex,eF,eB,dU,c3.navdir);A(cF,{time:ex,pos:eE,direction:c3.navdir,onEnd:function(){dp(eE,true);ev()}});co(dO,bp(eE,dy.min,dy.max,c3.navdir))}}}function cS(){dN(dr);cd[dr].push(dF[dr].addClass(a4).attr("data-active",true))}function dN(ey){var ex=cd[ey];while(ex.length){ex.shift().removeClass(a4).attr("data-active",false)}}function ce(ey){var ex=er[ey];bV.each(ea,function(eA,ez){delete ex[cZ(ez)]});bV.each(ex,function(ez,eA){delete ex[ez];eA.detach()})}function dC(ey){c2=ed=eo;var ex=dF[bu];if(ex){dN(bu);cd[bu].push(ex.addClass(a4).attr("data-active",true));if(ex.hasClass(x)){ex.attr("aria-hidden","false")}ey||cg.showStage.onEnd(true);a1(cl,0,true);ce(bu);di(ea);d9();c5();a(cl[0],function(){if(!d6.hasClass(M)){cg.requestFullScreen();dM.focus()}})}}function dL(ey,ex){if(!ey){return}bV.each(ex,function(ez,eA){if(!eA){return}bV.extend(eA,{width:ey.width||eA.width,height:ey.height,minwidth:ey.minwidth,maxwidth:ey.maxwidth,minheight:ey.minheight,maxheight:ey.maxheight,ratio:bm(ey.ratio)})})}function dc(ey,ex){d6.trigger(ag+":"+ey,[cg,ex])}function dR(){clearTimeout(cr.t);c6=1;if(c3.stopautoplayontouch){cg.stopAutoplay()}else{cj=true}}function cr(){if(!c6){return}if(!c3.stopautoplayontouch){cw();es()}cr.t=setTimeout(function(){c6=0},ba+b8)}function cw(){cj=!!(dg||el)}function es(){clearTimeout(es.t);bX.stop(es.w);if(!c3.autoplay||cj){if(cg.autoplay){cg.autoplay=false;dc("stopautoplay")}return}if(!cg.autoplay){cg.autoplay=true;dc("startautoplay")}var ey=eo;var ex=dF[bu].data();es.w=bX(function(){return ex.state||ey!==eo},function(){es.t=setTimeout(function(){if(cj||ey!==eo){return}var ez=cK,eA=dP[ez][bu].data();es.w=bX(function(){return eA.state||ez!==cK},function(){if(cj||ez!==cK){return}cg.show(dj?ay(!dH):cK)})},c3.autoplay)})}cg.startAutoplay=function(ex){if(cg.autoplay){return this}cj=el=false;dq(ex||c3.autoplay);es();return this};cg.stopAutoplay=function(){if(cg.autoplay){cj=el=true;es()}return this};cg.showSlide=function(ez){var eA=aa(cF,c3.navdir),eC,eB=500*1.1,ey=c3.navdir==="horizontal"?c3.thumbwidth:c3.thumbheight,ex=function(){ev()};if(ez==="next"){eC=eA-(ey+c3.margin)*b4}if(ez==="prev"){eC=eA+(ey+c3.margin)*b4}eC=a5(eC,dy);dp(eC,true);A(cF,{time:eB,pos:eC,direction:c3.navdir,onEnd:ex})};cg.showWhileLongPress=function(eA){if(cg.longPress.singlePressInProgress){return}var ez=dK(eA);ew(ez);var eB=cA(eA)/50;var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showNav(ex,eA,eB);return this};cg.showEndLongPress=function(eA){if(cg.longPress.singlePressInProgress){return}var ez=dK(eA);ew(ez);var eB=cA(eA)/50;var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showStage(ex,eA,eB);ee=typeof dw!=="undefined"&&dw!==eo;dw=eo;return this};function dK(ey){var ex;if(typeof ey!=="object"){ex=ey;ey={}}else{ex=ey.index}ex=ex===">"?ed+1:ex==="<"?ed-1:ex==="<<"?0:ex===">>"?c1-1:ex;ex=isNaN(ex)?aP:ex;ex=typeof ex==="undefined"?eo||0:ex;return ex}function ew(ex){cg.activeIndex=eo=du(ex);d4=dB(eo);cN=ci(eo);cK=cZ(eo+(dH?-1:1));ea=[eo,d4,cN];ed=dj?ex:eo}function cA(ey){var ex=Math.abs(dw-ed),ez=bj(ey.time,function(){return Math.min(ds*(1+(ex-1)/12),ds*2)});if(ey.slow){ez*=10}return ez}cg.showStage=function(ey,eA,eD){cO(dg,dF.i!==dP[cZ(c2)].i);dl(ea,"stage");di(bh?[ed]:[ed,dB(ed),ci(ed)]);cD("go",true);ey||dc("show",{user:eA.user,time:eD});cj=true;var eC=eA.overPos;var ez=cg.showStage.onEnd=function(eE){if(ez.ok){return}ez.ok=true;eE||dC(true);if(!ey){dc("showend",{user:eA.user})}if(!eE&&cu&&cu!==c3.transition){cg.setOptions({transition:cu});cu=false;return}cy();cx(ea,"stage");cD("go",false);cJ();ei();cw();es();if(cg.fullScreen){dF[bu].find("."+Y).attr("aria-hidden",false);dF[bu].find("."+J).attr("aria-hidden",true)}else{dF[bu].find("."+Y).attr("aria-hidden",true);dF[bu].find("."+J).attr("aria-hidden",false)}};if(!cv){A(cl,{pos:-a8(ed,en.w,c3.margin,c2),overPos:eC,time:eD,onEnd:ez})}else{var ex=dF[bu],eB=dP[dw]&&eo!==dw?dP[dw][bu]:null;aq(ex,eB,c8,{time:eD,method:c3.transition,onEnd:ez},cI)}dh()};cg.showNav=function(ey,ez,eA){ev();if(cq){cS();var ex=cn(eo+bb(ed-dw,-1,1));dT({time:eA,coo:ex!==eo&&ez.coo,guessIndex:typeof ez.coo!=="undefined"?ex:eo,keep:ey});if(c0){d7(eA)}}};cg.show=function(eA){cg.longPress.singlePressInProgress=true;var ez=dK(eA);ew(ez);var eB=cA(eA);var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showStage(ex,eA,eB);cg.showNav(ex,eA,eB);ee=typeof dw!=="undefined"&&dw!==eo;dw=eo;cg.longPress.singlePressInProgress=false;return this};cg.requestFullScreen=function(){if(c4&&!cg.fullScreen){var ex=bV((cg.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container");if(ex){return}cs=bf.scrollTop();cT=bf.scrollLeft();bU(bf);cD("x",true);dZ=bV.extend({},en);d6.addClass(M).appendTo(I.addClass(bH));R.addClass(bH);cO(dg,true,true);cg.fullScreen=true;if(d2){bB.request(eu)}cg.resize();cx(ea,"stage");cy();dc("fullscreenenter");if(!("ontouchstart" in bo)){dM.focus()}}return this};function cP(){if(cg.fullScreen){cg.fullScreen=false;if(s){bB.cancel(eu)}I.removeClass(bH);R.removeClass(bH);d6.removeClass(M).insertAfter(c9);en=bV.extend({},dZ);cO(dg,true,true);cD("x",false);cg.resize();cx(ea,"stage");bU(bf,cT,cs);dc("fullscreenexit")}}cg.cancelFullScreen=function(){if(d2&&bB.is()){bB.cancel(k)}else{cP()}return this};cg.toggleFullScreen=function(){return cg[(cg.fullScreen?"cancel":"request")+"FullScreen"]()};cg.resize=function(ez){if(!dP){return this}var eC=arguments[1]||0,ey=arguments[2];b4=ck(dk,c3);dL(!cg.fullScreen?bA(ez):{width:bV(bo).width(),maxwidth:null,minwidth:null,height:bV(bo).height(),maxheight:null,minheight:null},[en,ey||cg.fullScreen||c3]);var eB=en.width,ex=en.height,eA=en.ratio,eD=bf.height()-(cq?dO.height():0);if(t(eB)){dk.css({width:""});dk.css({height:""});cf.css({width:""});cf.css({height:""});cl.css({width:""});cl.css({height:""});dO.css({width:""});dO.css({height:""});dk.css({minWidth:en.minwidth||0,maxWidth:en.maxwidth||bK});if(cq==="dots"){dU.hide()}eB=en.W=en.w=dk.width();en.nw=cq&&d(c3.navwidth,eB)||eB;cl.css({width:en.w,marginLeft:(en.W-en.w)/2});ex=d(ex,eD);ex=ex||(eA&&eB/eA);if(ex){eB=Math.round(eB);ex=en.h=Math.round(bb(ex,d(en.minheight,eD),d(en.maxheight,eD)));cf.css({width:eB,height:ex});if(c3.navdir==="vertical"&&!cg.fullscreen){dO.width(c3.thumbwidth+c3.thumbmargin*2)}if(c3.navdir==="horizontal"&&!cg.fullscreen){dO.height(c3.thumbheight+c3.thumbmargin*2)}if(cq==="dots"){dO.width(eB).height("auto");dU.show()}if(c3.navdir==="vertical"&&cg.fullScreen){cf.css("height",bf.height())}if(c3.navdir==="horizontal"&&cg.fullScreen){cf.css("height",bf.height()-dO.height())}if(cq){switch(c3.navdir){case"vertical":dU.removeClass(bZ);dU.removeClass(ax);dU.addClass(b);dO.stop().animate({height:en.h,width:c3.thumbwidth},eC);break;case"list":dU.removeClass(b);dU.removeClass(bZ);dU.addClass(ax);break;default:dU.removeClass(b);dU.removeClass(ax);dU.addClass(bZ);dO.stop().animate({width:en.nw},eC);break}dC();dT({guessIndex:eo,time:eC,keep:true});if(c0&&cz.nav){d7(eC)}}dG=ey||true;eq.ok=true;eq()}}d3=cf.offset().left;i();return this};cg.setOptions=function(ex){bV.extend(c3,ex);dV();return this};cg.shuffle=function(){dP&&aC(dP)&&dV();return this};function co(ex,ey){if(ec){ex.removeClass(S+" "+aL);ex.removeClass(a2+" "+aR);ey&&!dg&&ex.addClass(ey.replace(/^|\s/g," "+bz+"--"))}}cg.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){if(!this.inProgress){this.count++;this.inProgress=this.count>this.threshold}},end:function(){if(this.inProgress){this.isEnded=true}},reset:function(){this.count=0;this.inProgress=false;this.isEnded=false}};cg.destroy=function(){cg.cancelFullScreen();cg.stopAutoplay();dP=cg.data=null;dd();ea=[];ce(bu);dV.ok=false;return this};cg.playVideo=function(){var ez=dF,ex=ez.video,ey=eo;if(typeof ex==="object"&&ez.videoReady){d2&&cg.fullScreen&&cg.cancelFullScreen();bX(function(){return !bB.is()||ey!==eo},function(){if(ey===eo){ez.$video=ez.$video||bV(ab(bJ)).append(q(ex));ez.$video.appendTo(ez[bu]);dk.addClass(bt);dg=ez.$video;em();cU.blur();dM.blur();dc("loadvideo")}})}return this};cg.stopVideo=function(){cO(dg,true,true);return this};cg.spliceByIndex=function(ex,ey){ey.i=ex+1;ey.img&&bV.ajax({url:ey.img,type:"HEAD",success:function(){dP.splice(ex,1,ey);dV()}})};function cO(ex,ez,ey){if(ez){dk.removeClass(bt);dg=false;em()}if(ex&&ex!==dg){ex.remove();dc("unloadvideo")}if(ey){cw();es()}}function cp(ex){dk.toggleClass(P,ex)}function ei(ez){if(d5.flow){return}var ex=ez?ez.pageX:ei.x,ey=ex&&!cE(eh(ex))&&c3.click;if(ei.p!==ey&&cf.toggleClass(bC,ey)){ei.p=ey;ei.x=ex}}cf.on("mousemove",ei);function cm(ex){clearTimeout(cm.t);if(c3.clicktransition&&c3.clicktransition!==c3.transition){setTimeout(function(){var ey=c3.transition;cg.setOptions({transition:c3.clicktransition});cu=ey;cm.t=setTimeout(function(){cg.show(ex)},10)},0)}else{cg.show(ex)}}function ct(eA,ey){var ez=eA.target,ex=bV(ez);if(ex.hasClass(T)){cg.playVideo()}else{if(ez===dD){cg.toggleFullScreen()}else{if(dg){ez===d1&&cO(dg,true,true)}else{if(!d6.hasClass(M)){cg.requestFullScreen()}}}}O(eA,true)}function cD(ex,ey){d5[ex]=dy[ex]=ey}d5=ao(cl,{onStart:dR,onMove:function(ey,ex){co(cf,ex.edge)},onTouchEnd:cr,onEnd:function(ex){var ez;co(cf);ez=(aZ&&!dz||ex.touch)&&c3.arrows;if((ex.moved||(ez&&ex.pos!==ex.newPos&&!ex.control))&&ex.$target[0]!==dM[0]){var ey=by(ex.newPos,en.w,c3.margin,c2);cg.show({index:ey,time:cv?ds:ex.time,overPos:ex.overPos,user:true})}else{if(!ex.aborted&&!ex.control){ct(ex.startEvent,ez)}}},timeLow:1,timeHigh:1,friction:2,select:"."+a9+", ."+a9+" *",$wrap:cf,direction:"horizontal"});dy=ao(cF,{onStart:dR,onMove:function(ey,ex){co(dO,ex.edge)},onTouchEnd:cr,onEnd:function(ex){function ey(){dT.l=ex.newPos;cw();es();dp(ex.newPos,true);ev()}if(!ex.moved){var ez=ex.$target.closest("."+aG,cF)[0];ez&&dJ.call(ez,ex.startEvent)}else{if(ex.pos!==ex.newPos){cj=true;A(cF,{time:ex.time,pos:ex.newPos,overPos:ex.overPos,direction:c3.navdir,onEnd:ey});dp(ex.newPos);ec&&co(dO,bp(ex.newPos,dy.min,dy.max,ex.dir))}else{ey()}}},timeLow:0.5,timeHigh:2,friction:5,$wrap:dO,direction:c3.navdir});cG=o(cf,{shift:true,onEnd:function(ey,ex){dR();cr();cg.show({index:ex,slow:ey.altKey})}});ef=o(dO,{onEnd:function(ez,ey){dR();cr();var ex=a1(cF)+ey*0.25;cF.css(b2(bb(ex,dy.min,dy.max),c3.navdir));ec&&co(dO,bp(ex,dy.min,dy.max,c3.navdir));ef.prevent={"<":ex>=dy.max,">":ex<=dy.min};clearTimeout(ef.t);ef.t=setTimeout(function(){dT.l=ex;dp(ex,true)},b8);dp(ex)}});dk.hover(function(){setTimeout(function(){if(c6){return}cp(!(dz=true))},0)},function(){if(!dz){return}cp(!(dz=false))});function dJ(ey){var ex=bV(this).data().eq;if(c3.navtype==="thumbs"){cm({index:ex,slow:ey.altKey,user:true,coo:ey._x-dO.offset().left})}else{cm({index:ex,slow:ey.altKey,user:true})}}function et(ex){cm({index:cU.index(this)?">":"<",slow:ex.altKey,user:true})}z(cU,function(ex){O(ex);et.call(this,ex)},{onStart:function(){dR();d5.control=true},onTouchEnd:cr});z(eg,function(ex){O(ex);if(c3.navtype==="thumbs"){cg.show("<")}else{cg.showSlide("prev")}});z(dX,function(ex){O(ex);if(c3.navtype==="thumbs"){cg.show(">")}else{cg.showSlide("next")}});function dv(ex){bL(ex,function(){setTimeout(function(){bU(cf)},0);cp(false)})}cU.each(function(){a(this,function(ex){et.call(this,ex)});dv(this)});a(dD,function(){if(d6.hasClass(M)){cg.cancelFullScreen();cl.focus()}else{cg.requestFullScreen();dM.focus()}});dv(dD);function dV(){dn();dQ();if(!dV.i){dV.i=true;var ex=c3.startindex;eo=c2=ed=dw=dx=du(ex)||0}if(c1){if(cV()){return}if(dg){cO(dg,true)}ea=[];ce(bu);dV.ok=true;cg.show({index:eo,time:0});cg.resize()}else{cg.destroy()}}function cV(){if(!cV.f===dH){cV.f=dH;eo=c1-1-eo;cg.reverse();return true}}bV.each("load push pop shift unshift reverse sort splice".split(" "),function(ex,ey){cg[ey]=function(){dP=dP||[];if(ey!=="load"){Array.prototype[ey].apply(dP,arguments)}else{if(arguments[0]&&typeof arguments[0]==="object"&&arguments[0].length){dP=bG(arguments[0])}}dV();return cg}});function eq(){if(eq.ok){eq.ok=false;dc("ready")}}dV()};bV.fn.fotorama=function(i){return this.each(function(){var ce=this,cd=bV(this),cc=cd.data(),cf=cc.fotorama;if(!cf){bX(function(){return !W(ce)},function(){cc.urtext=cd.html();new bV.Fotorama(cd,bV.extend({},Q,bo.fotoramaDefaults,i,cc))})}else{cf.setOptions(i,true)}})};bV.Fotorama.instances=[];function b1(){bV.each(bV.Fotorama.instances,function(cc,i){i.index=cc})}function C(i){bV.Fotorama.instances.push(i);b1()}function av(i){bV.Fotorama.instances.splice(i.index,1);b1()}bV.Fotorama.cache={};bV.Fotorama.measures={};bV=bV||{};bV.Fotorama=bV.Fotorama||{};bV.Fotorama.jst=bV.Fotorama.jst||{};bV.Fotorama.jst.dots=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__nav__frame fotorama__nav__frame--dot" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__dot"></div>\r\n</div>';return ce};bV.Fotorama.jst.frameCaption=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__caption" aria-hidden="true">\r\n <div class="fotorama__caption__wrap" id="'+((i=(cc.labelledby))==null?"":i)+'">'+((i=(cc.caption))==null?"":i)+"</div>\r\n</div>\r\n";return ce};bV.Fotorama.jst.style=function(cc){var i,ce="",cd=bx.escape;ce+=".fotorama"+((i=(cc.s))==null?"":i)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+((i=(cc.m))==null?"":i)+"px;\r\nheight:"+((i=(cc.h))==null?"":i)+"px}\r\n.fotorama"+((i=(cc.s))==null?"":i)+" .fotorama__thumb-border{\r\nheight:"+((i=(cc.h))==null?"":i)+"px;\r\nborder-width:"+((i=(cc.b))==null?"":i)+"px;\r\nmargin-top:"+((i=(cc.m))==null?"":i)+"px}";return ce};bV.Fotorama.jst.thumb=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__nav__frame fotorama__nav__frame--thumb" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__thumb">\r\n </div>\r\n</div>';return ce}})(window,document,location,typeof jQuery!=="undefined"&&jQuery); +fotoramaVersion="4.6.4";(function(bo,k,a3,bV,aP){var ag="fotorama",bH="fotorama__fullscreen",ae=ag+"__wrap",ah=ae+"--css2",aX=ae+"--css3",bt=ae+"--video",ar=ae+"--fade",aw=ae+"--slide",P=ae+"--no-controls",aM=ae+"--no-shadows",U=ae+"--pan-y",a0=ae+"--rtl",az=ae+"--only-active",bN=ae+"--no-captions",f=ae+"--toggle-arrows",a7=ag+"__stage",x=a7+"__frame",l=x+"--video",B=a7+"__shaft",aB=ag+"__grab",bC=ag+"__pointer",aK=ag+"__arr",F=aK+"--disabled",bc=aK+"--prev",r=aK+"--next",bO=ag+"__nav",bq=bO+"-wrap",aH=bO+"__shaft",b=bq+"--vertical",ax=bq+"--list",bZ=bq+"--horizontal",bW=bO+"--dots",ai=bO+"--thumbs",aG=bO+"__frame",br=ag+"__fade",al=br+"-front",n=br+"-rear",aW=ag+"__shadow",bz=aW+"s",S=bz+"--left",aL=bz+"--right",a2=bz+"--top",aR=bz+"--bottom",a4=ag+"__active",a9=ag+"__select",bs=ag+"--hidden",M=ag+"--fullscreen",aJ=ag+"__fullscreen-icon",bP=ag+"__error",bM=ag+"__loading",c=ag+"__loaded",b3=c+"--full",bg=c+"--img",bR=ag+"__grabbing",J=ag+"__img",Y=J+"--full",bS=ag+"__thumb",b0=bS+"__arr--left",H=bS+"__arr--right",cb=bS+"-border",bd=ag+"__html",af=ag+"-video-container",bJ=ag+"__video",T=bJ+"-play",w=bJ+"-close",au=ag+"_horizontal_ratio",aY=ag+"_vertical_ratio",ca=ag+"__spinner",Z=ca+"--show";var E=bV&&bV.fn.jquery.split(".");if(!E||E[0]<1||(E[0]==1&&E[1]<8)){throw"Fotorama requires jQuery 1.8 or later and will not run without it."}var bx={};var ap=(function(co,ct,cj){var cf="2.8.3",cm={},cD=ct.documentElement,cE="modernizr",cB=ct.createElement(cE),cp=cB.style,cg,cw={}.toString,cy=" -webkit- -moz- -o- -ms- ".split(" "),cd="Webkit Moz O ms",cG=cd.split(" "),cq=cd.toLowerCase().split(" "),ck={},ce={},cu={},cA=[],cv=cA.slice,cc,cz=function(cQ,cS,cK,cR){var cJ,cP,cM,cN,cI=ct.createElement("div"),cO=ct.body,cL=cO||ct.createElement("body");if(parseInt(cK,10)){while(cK--){cM=ct.createElement("div");cM.id=cR?cR[cK]:cE+(cK+1);cI.appendChild(cM)}}cJ=["­",'<style id="s',cE,'">',cQ,"</style>"].join("");cI.id=cE;(cO?cI:cL).innerHTML+=cJ;cL.appendChild(cI);if(!cO){cL.style.background="";cL.style.overflow="hidden";cN=cD.style.overflow;cD.style.overflow="hidden";cD.appendChild(cL)}cP=cS(cI,cQ);if(!cO){cL.parentNode.removeChild(cL);cD.style.overflow=cN}else{cI.parentNode.removeChild(cI)}return !!cP},cs=({}).hasOwnProperty,cC;if(!cl(cs,"undefined")&&!cl(cs.call,"undefined")){cC=function(cI,cJ){return cs.call(cI,cJ)}}else{cC=function(cI,cJ){return((cJ in cI)&&cl(cI.constructor.prototype[cJ],"undefined"))}}if(!Function.prototype.bind){Function.prototype.bind=function cH(cK){var cL=this;if(typeof cL!="function"){throw new TypeError()}var cI=cv.call(arguments,1),cJ=function(){if(this instanceof cJ){var cO=function(){};cO.prototype=cL.prototype;var cN=new cO();var cM=cL.apply(cN,cI.concat(cv.call(arguments)));if(Object(cM)===cM){return cM}return cN}else{return cL.apply(cK,cI.concat(cv.call(arguments)))}};return cJ}}function cr(cI){cp.cssText=cI}function ci(cJ,cI){return cr(cy.join(cJ+";")+(cI||""))}function cl(cJ,cI){return typeof cJ===cI}function cn(cJ,cI){return !!~(""+cJ).indexOf(cI)}function cF(cK,cI){for(var cJ in cK){var cL=cK[cJ];if(!cn(cL,"-")&&cp[cL]!==cj){return cI=="pfx"?cL:true}}return false}function cx(cJ,cM,cL){for(var cI in cJ){var cK=cM[cJ[cI]];if(cK!==cj){if(cL===false){return cJ[cI]}if(cl(cK,"function")){return cK.bind(cL||cM)}return cK}}return false}function i(cM,cI,cL){var cJ=cM.charAt(0).toUpperCase()+cM.slice(1),cK=(cM+" "+cG.join(cJ+" ")+cJ).split(" ");if(cl(cI,"string")||cl(cI,"undefined")){return cF(cK,cI)}else{cK=(cM+" "+(cq).join(cJ+" ")+cJ).split(" ");return cx(cK,cI,cL)}}ck.touch=function(){var cI;if(("ontouchstart" in co)||co.DocumentTouch&&ct instanceof DocumentTouch){cI=true}else{cz(["@media (",cy.join("touch-enabled),("),cE,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(cJ){cI=cJ.offsetTop===9})}return cI};ck.csstransforms3d=function(){var cI=!!i("perspective");if(cI&&"webkitPerspective" in cD.style){cz("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(cJ,cK){cI=cJ.offsetLeft===9&&cJ.offsetHeight===3})}return cI};ck.csstransitions=function(){return i("transition")};for(var ch in ck){if(cC(ck,ch)){cc=ch.toLowerCase();cm[cc]=ck[ch]();cA.push((cm[cc]?"":"no-")+cc)}}cm.addTest=function(cJ,cK){if(typeof cJ=="object"){for(var cI in cJ){if(cC(cJ,cI)){cm.addTest(cI,cJ[cI])}}}else{cJ=cJ.toLowerCase();if(cm[cJ]!==cj){return cm}cK=typeof cK=="function"?cK():cK;if(typeof enableClasses!=="undefined"&&enableClasses){cD.className+=" "+(cK?"":"no-")+cJ}cm[cJ]=cK}return cm};cr("");cB=cg=null;cm._version=cf;cm._prefixes=cy;cm._domPrefixes=cq;cm._cssomPrefixes=cG;cm.testProp=function(cI){return cF([cI])};cm.testAllProps=i;cm.testStyles=cz;cm.prefixed=function(cK,cJ,cI){if(!cJ){return i(cK,"pfx")}else{return i(cK,cJ,cI)}};return cm})(bo,k);var bB={ok:false,is:function(){return false},request:function(){},cancel:function(){},event:"",prefix:""},h="webkit moz o ms khtml".split(" ");if(typeof k.cancelFullScreen!="undefined"){bB.ok=true}else{for(var bv=0,N=h.length;bv<N;bv++){bB.prefix=h[bv];if(typeof k[bB.prefix+"CancelFullScreen"]!="undefined"){bB.ok=true;break}}}if(bB.ok){bB.event=bB.prefix+"fullscreenchange";bB.is=function(){switch(this.prefix){case"":return k.fullScreen;case"webkit":return k.webkitIsFullScreen;default:return k[this.prefix+"FullScreen"]}};bB.request=function(i){return(this.prefix==="")?i.requestFullScreen():i[this.prefix+"RequestFullScreen"]()};bB.cancel=function(i){return(this.prefix==="")?k.cancelFullScreen():k[this.prefix+"CancelFullScreen"]()}}function a6(i){var cc="bez_"+bV.makeArray(arguments).join("_").replace(".","p");if(typeof bV.easing[cc]!=="function"){var cd=function(ck,ci){var cf=[null,null],cl=[null,null],cj=[null,null],ch=function(cm,cn){cj[cn]=3*ck[cn];cl[cn]=3*(ci[cn]-ck[cn])-cj[cn];cf[cn]=1-cj[cn]-cl[cn];return cm*(cj[cn]+cm*(cl[cn]+cm*cf[cn]))},cg=function(cm){return cj[0]+cm*(2*cl[0]+3*cf[0]*cm)},ce=function(co){var cm=co,cn=0,cp;while(++cn<14){cp=ch(cm,0)-co;if(Math.abs(cp)<0.001){break}cm-=cp/cg(cm)}return cm};return function(cm){return ch(ce(cm),1)}};bV.easing[cc]=function(cf,cg,ce,ci,ch){return ci*cd([i[0],i[1]],[i[2],i[3]])(cg/ch)+ce}}return cc}var bf=bV(bo),bw=bV(k),R,I,bT=a3.hash.replace("#","")==="quirks",ac=ap.csstransforms3d,aA=ac&&!bT,aN=ac||k.compatMode==="CSS1Compat",s=bB.ok,am=navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i),bh=!aA||am,aZ=navigator.msPointerEnabled,y="onwheel" in k.createElement("div")?"wheel":k.onmousewheel!==aP?"mousewheel":"DOMMouseScroll",b8=250,ba=300,bY=1400,bQ=5000,b9=2,L=64,bk=500,bE=333,bu="$stageFrame",b7="$navDotFrame",bl="$navThumbFrame",bF="auto",u=a6([0.1,0,0.25,1]),bK=1200,b4=1,Q={width:null,minwidth:null,maxwidth:"100%",height:null,minheight:null,maxheight:null,ratio:null,margin:b9,nav:"dots",navposition:"bottom",navwidth:null,thumbwidth:L,thumbheight:L,thumbmargin:b9,thumbborderwidth:b9,allowfullscreen:false,transition:"slide",clicktransition:null,transitionduration:ba,captions:true,startindex:0,loop:false,autoplay:false,stopautoplayontouch:true,keyboard:false,arrows:true,click:true,swipe:false,trackpad:false,shuffle:false,direction:"ltr",shadows:true,showcaption:true,navdir:"horizontal",navarrows:true,navtype:"thumbs"},p={left:true,right:true,down:true,up:true,space:false,home:false,end:false};function g(){}function bb(cd,cc,i){return Math.max(isNaN(cc)?-Infinity:cc,Math.min(isNaN(i)?Infinity:i,cd))}function bi(cc,i){return cc.match(/ma/)&&cc.match(/-?\d+(?!d)/g)[cc.match(/3d/)?(i==="vertical"?13:12):(i==="vertical"?5:4)]}function aa(cc,i){if(aA){return +bi(cc.css("transform"),i)}else{return +cc.css(i==="vertical"?"top":"left").replace("px","")}}function b2(cd,cc){var i={};if(aA){switch(cc){case"vertical":i.transform="translate3d(0, "+(cd)+"px,0)";break;case"list":break;default:i.transform="translate3d("+(cd)+"px,0,0)";break}}else{cc==="vertical"?i.top=cd:i.left=cd}return i}function b6(i){return{"transition-duration":i+"ms"}}function aV(cc,i){return isNaN(cc)?i:cc}function m(cc,i){return aV(+String(cc).replace(i||"px",""))}function K(i){return/%$/.test(i)?m(i,"%"):aP}function d(cc,i){return aV(K(cc)/100*i,m(cc))}function t(i){return(!isNaN(m(i))||!isNaN(m(i,"%")))&&i}function a8(cc,cd,ce,i){return(cc-(i||0))*(cd+(ce||0))}function by(ce,cc,cd,i){return -Math.round(ce/(cc+(cd||0))-(i||0))}function aO(cd){var cc=cd.data();if(cc.tEnd){return}var ce=cd[0],i={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",msTransition:"MSTransitionEnd",transition:"transitionend"};D(ce,i[ap.prefixed("transition")],function(cf){cc.tProp&&cf.propertyName.match(cc.tProp)&&cc.onEndFn()});cc.tEnd=true}function X(cd,cf,ce,cg){var cc,i=cd.data();if(i){i.onEndFn=function(){if(cc){return}cc=true;clearTimeout(i.tT);ce()};i.tProp=cf;clearTimeout(i.tT);i.tT=setTimeout(function(){i.onEndFn()},cg*1.5);aO(cd)}}function a1(cd,cf){var cc=cd.navdir||"horizontal";if(cd.length){var i=cd.data();if(aA){cd.css(b6(0));i.onEndFn=g;clearTimeout(i.tT)}else{cd.stop()}var ce=bj(cf,function(){return aa(cd,cc)});cd.css(b2(ce,cc));return ce}}function bj(){var cc;for(var cd=0,i=arguments.length;cd<i;cd++){cc=cd?arguments[cd]():arguments[cd];if(typeof cc==="number"){break}}return cc}function aF(cc,i){return Math.round(cc+((i-cc)/1.5))}function aU(){aU.p=aU.p||(a3.protocol==="https:"?"https://":"http://");return aU.p}function ak(cc){var i=k.createElement("a");i.href=cc;return i}function at(i,cd){if(typeof i!=="string"){return i}i=ak(i);var cf,ce;if(i.host.match(/youtube\.com/)&&i.search){cf=i.search.split("v=")[1];if(cf){var cc=cf.indexOf("&");if(cc!==-1){cf=cf.substring(0,cc)}ce="youtube"}}else{if(i.host.match(/youtube\.com|youtu\.be/)){cf=i.pathname.replace(/^\/(embed\/|v\/)?/,"").replace(/\/.*/,"");ce="youtube"}else{if(i.host.match(/vimeo\.com/)){ce="vimeo";cf=i.pathname.replace(/^\/(video\/)?/,"").replace(/\/.*/,"")}}}if((!cf||!ce)&&cd){cf=i.href;ce="custom"}return cf?{id:cf,type:ce,s:i.search.replace(/^\?/,""),p:aU()}:false}function aQ(cg,ce,cf){var cc,i,cd=cg.video;if(cd.type==="youtube"){i=aU()+"img.youtube.com/vi/"+cd.id+"/default.jpg";cc=i.replace(/\/default.jpg$/,"/hqdefault.jpg");cg.thumbsReady=true}else{if(cd.type==="vimeo"){bV.ajax({url:aU()+"vimeo.com/api/v2/video/"+cd.id+".json",dataType:"jsonp",success:function(ch){cg.thumbsReady=true;v(ce,{img:ch[0].thumbnail_large,thumb:ch[0].thumbnail_small},cg.i,cf)}})}else{cg.thumbsReady=true}}return{img:cc,thumb:i}}function v(ch,cd,cf,ci){for(var cg=0,ce=ch.length;cg<ce;cg++){var cj=ch[cg];if(cj.i===cf&&cj.thumbsReady){var cc={videoReady:true};cc[bu]=cc[bl]=cc[b7]=false;ci.splice(cg,1,bV.extend({},cj,cc,cd));break}}}function bI(cc){var ce=[];function cd(cf,ch,cn){var ci=cf.children("img").eq(0),cm=cf.attr("href"),ck=cf.attr("src"),cl=ci.attr("src"),cj=ch.video,cg=cn?at(cm,cj===true):false;if(cg){cm=false}else{cg=cj}i(cf,ci,bV.extend(ch,{video:cg,img:ch.img||cm||ck||cl,thumb:ch.thumb||cl||ck||cm}))}function i(cg,cj,ck){var ci=ck.thumb&&ck.img!==ck.thumb,ch=m(ck.width||cg.attr("width")),cf=m(ck.height||cg.attr("height"));bV.extend(ck,{width:ch,height:cf,thumbratio:bm(ck.thumbratio||(m(ck.thumbwidth||(cj&&cj.attr("width"))||ci||ch)/m(ck.thumbheight||(cj&&cj.attr("height"))||ci||cf)))})}cc.children().each(function(){var cf=bV(this),cg=bA(bV.extend(cf.data(),{id:cf.attr("id")}));if(cf.is("a, img")){cd(cf,cg,true)}else{if(!cf.is(":empty")){i(cf,null,bV.extend(cg,{html:this,_html:cf.html()}))}else{return}}ce.push(cg)});return ce}function W(i){return i.offsetWidth===0&&i.offsetHeight===0}function be(i){return !bV.contains(k.documentElement,i)}function bX(cf,cd,ce,cc){if(!bX.i){bX.i=1;bX.ii=[true]}cc=cc||bX.i;if(typeof bX.ii[cc]==="undefined"){bX.ii[cc]=true}if(cf()){cd()}else{bX.ii[cc]&&setTimeout(function(){bX.ii[cc]&&bX(cf,cd,ce,cc)},ce||100)}return bX.i++}bX.stop=function(cc){bX.ii[cc]=false};function V(ce,cd){var cc=ce.data(),cg=cc.measures;if(cg&&(!cc.l||cc.l.W!==cg.width||cc.l.H!==cg.height||cc.l.r!==cg.ratio||cc.l.w!==cd.w||cc.l.h!==cd.h)){var i=bb(cd.h,0,cg.height),cf=i*cg.ratio;aS.setRatio(ce,cf,i);cc.l={W:cg.width,H:cg.height,r:cg.ratio,w:cd.w,h:cd.h}}return true}function an(i,cd){var cc=i[0];if(cc.styleSheet){cc.styleSheet.cssText=cd}else{i.html(cd)}}function bp(ce,cd,i,cc){return cd===i?false:cc==="vertical"?(ce<=cd?"top":ce>=i?"bottom":"top bottom"):(ce<=cd?"left":ce>=i?"right":"left right")}function z(cc,cd,i){i=i||{};cc.each(function(){var cg=bV(this),cf=cg.data(),ce;if(cf.clickOn){return}cf.clickOn=true;bV.extend(aI(cg,{onStart:function(ch){ce=ch;(i.onStart||g).call(this,ch)},onMove:i.onMove||g,onTouchEnd:i.onTouchEnd||g,onEnd:function(ch){if(ch.moved){return}cd.call(this,ce)}}),{noMove:true})})}function ab(i,cc){return'<div class="'+i+'">'+(cc||"")+"</div>"}function aT(i){return"."+i}function q(i){var cc='<iframe src="'+i.p+i.type+".com/embed/"+i.id+'" frameborder="0" allowfullscreen></iframe>';return cc}function aC(cf){var cc=cf.length;while(cc){var ce=Math.floor(Math.random()*cc--);var cd=cf[cc];cf[cc]=cf[ce];cf[ce]=cd}return cf}function bG(i){return Object.prototype.toString.call(i)=="[object Array]"&&bV.map(i,function(cc){return bV.extend({},cc)})}function bU(i,cd,cc){i.scrollLeft(cd||0).scrollTop(cc||0)}function bA(i){if(i){var cc={};bV.each(i,function(cd,ce){cc[cd.toLowerCase()]=ce});return cc}}function bm(i){if(!i){return}var cc=+i;if(!isNaN(cc)){return cc}else{cc=i.split("/");return +cc[0]/+cc[1]||aP}}function D(cd,ce,cc,i){if(!ce){return}cd.addEventListener?cd.addEventListener(ce,cc,!!i):cd.attachEvent("on"+ce,cc)}function a5(i,cc){if(i>cc.max){i=cc.max}else{if(i<cc.min){i=cc.min}}return i}function aD(i,ck,ch,cf,ce,cd,cc){var cg,cj,ci;if(cc==="horizontal"){cj=i.thumbwidth;ci=cd.width()}else{cj=i.thumbheight;ci=cd.height()}if((cj+i.margin)*(ch+1)>=(ci-cf)){if(cc==="horizontal"){cg=-ce.position().left}else{cg=-ce.position().top}}else{if((cj+i.margin)*(ch)<=Math.abs(cf)){if(cc==="horizontal"){cg=-ce.position().left+ci-(cj+i.margin)}else{cg=-ce.position().top+ci-(cj+i.margin)}}else{cg=cf}}cg=a5(cg,ck);return cg||0}function aj(i){return !!i.getAttribute("disabled")}function ad(cc,i){if(i){return{disabled:cc}}else{return{tabindex:cc*-1+"",disabled:cc}}}function a(cc,i){D(cc,"keyup",function(cd){aj(cc)||cd.keyCode==13&&i.call(cc,cd)})}function bL(cc,i){D(cc,"focus",cc.onfocusin=function(cd){i.call(cc,cd)},true)}function O(cc,i){cc.preventDefault?cc.preventDefault():(cc.returnValue=false);i&&cc.stopPropagation&&cc.stopPropagation()}function aE(cd,cc){var i=/iP(ad|hone|od)/i.test(bo.navigator.userAgent);if(i&&cc==="touchend"){cd.on("touchend",function(ce){bw.trigger("mouseup",ce)})}cd.on(cc,function(ce){O(ce,true);return false})}function ay(i){return i?">":"<"}var aS=(function(){function cd(ch,ce,cg){var cf=ce/cg;if(cf<=1){ch.parent().removeClass(au);ch.parent().addClass(aY)}else{ch.parent().removeClass(aY);ch.parent().addClass(au)}}function i(cf,cg,ch){var ce=ch;if(!cf.attr(ce)&&cf.attr(ce)!==aP){cf.attr(ce,cg)}if(cf.find("["+ce+"]").length){cf.find("["+ce+"]").each(function(){bV(this).attr(ce,cg)})}}function cc(cf,ce,ci){var cg=false,ch;cf.showCaption===ci||cf.showCaption===true?ch=true:ch=false;if(!ce){return false}if(cf.caption&&ch){cg=true}return cg}return{setRatio:cd,setThumbAttr:i,isExpectedCaption:cc}}(aS||{},jQuery));function A(ce,cd){var cc=ce.data(),i=Math.round(cd.pos),cf=function(){if(cc&&cc.sliding){cc.sliding=false}(cd.onEnd||g)()};if(typeof cd.overPos!=="undefined"&&cd.overPos!==cd.pos){i=cd.overPos}var cg=bV.extend(b2(i,cd.direction),cd.width&&{width:cd.width},cd.height&&{height:cd.height});if(cc&&cc.sliding){cc.sliding=true}if(aA){ce.css(bV.extend(b6(cd.time),cg));if(cd.time>10){X(ce,"transform",cf,cd.time)}else{cf()}}else{ce.stop().animate(cg,cd.time,u,cf)}}function aq(ck,cj,cc,cm,ce,i){var ch=typeof i!=="undefined";if(!ch){ce.push(arguments);Array.prototype.push.call(arguments,ce.length);if(ce.length>1){return}}ck=ck||bV(ck);cj=cj||bV(cj);var ci=ck[0],cg=cj[0],cf=cm.method==="crossfade",cl=function(){if(!cl.done){cl.done=true;var cn=(ch||ce.shift())&&ce.shift();cn&&aq.apply(this,cn);(cm.onEnd||g)(!!cn)}},cd=cm.time/(i||1);cc.removeClass(n+" "+al);ck.stop().addClass(n);cj.stop().addClass(al);cf&&cg&&ck.fadeTo(0,0);ck.fadeTo(cf?cd:0,1,cf&&cl);cj.fadeTo(cd,0,cl);(ci&&cf)||cg||cl()}var G,b5,e,j,bD;function bn(i){var cc=(i.touches||[])[0]||i;i._x=cc.pageX||cc.originalEvent.pageX;i._y=cc.clientY||cc.originalEvent.clientY;i._now=bV.now()}function aI(cr,cg){var cc=cr[0],cj={},i,cl,cf,cn,cs,cd,ce,co,ch;function cq(ct){cf=bV(ct.target);cj.checked=cd=ce=ch=false;if(i||cj.flow||(ct.touches&&ct.touches.length>1)||ct.which>1||(G&&G.type!==ct.type&&e)||(cd=cg.select&&cf.is(cg.select,cc))){return cd}cs=ct.type==="touchstart";ce=cf.is("a, a *",cc);cn=cj.control;co=(cj.noMove||cj.noSwipe||cn)?16:!cj.snap?4:0;bn(ct);cl=G=ct;b5=ct.type.replace(/down|start/,"move").replace(/Down/,"Move");(cg.onStart||g).call(cc,ct,{control:cn,$target:cf});i=cj.flow=true;if(!cs||cj.go){O(ct)}}function ck(cx){if((cx.touches&&cx.touches.length>1)||(aZ&&!cx.isPrimary)||b5!==cx.type||!i){i&&ci();(cg.onTouchEnd||g)();return}bn(cx);var cy=Math.abs(cx._x-cl._x),cu=Math.abs(cx._y-cl._y),cw=cy-cu,cv=(cj.go||cj.x||cw>=0)&&!cj.noSwipe,ct=cw<0;if(cs&&!cj.checked){if(i=cv){O(cx)}}else{O(cx);(cg.onMove||g).call(cc,cx,{touch:cs})}if(!ch&&Math.sqrt(Math.pow(cy,2)+Math.pow(cu,2))>co){ch=true}cj.checked=cj.checked||cv||ct}function ci(cu){(cg.onTouchEnd||g)();var ct=i;cj.control=i=false;if(ct){cj.flow=false}if(!ct||(ce&&!cj.checked)){return}cu&&O(cu);e=true;clearTimeout(j);j=setTimeout(function(){e=false},1000);(cg.onEnd||g).call(cc,{moved:ch,$target:cf,control:cn,touch:cs,startEvent:cl,aborted:!cu||cu.type==="MSPointerCancel"})}function cm(){if(cj.flow){return}cj.flow=true}function cp(){if(!cj.flow){return}cj.flow=false}if(aZ){D(cc,"MSPointerDown",cq);D(k,"MSPointerMove",ck);D(k,"MSPointerCancel",ci);D(k,"MSPointerUp",ci)}else{D(cc,"touchstart",cq);D(cc,"touchmove",ck);D(cc,"touchend",ci);D(k,"touchstart",cm);D(k,"touchend",cp);D(k,"touchcancel",cp);bf.on("scroll",cp);cr.on("mousedown pointerdown",cq);bw.on("mousemove pointermove",ck).on("mouseup pointerup",ci)}if(ap.touch){bD="a"}else{bD="div"}cr.on("click",bD,function(ct){cj.checked&&O(ct)});return cj}function ao(cz,cd){var cc=cz[0],ce=cz.data(),cm={},cw,cf,cx,cj,ch,cy,co,cg,cr,ct,cp,cq,i,cv,ci,cn;function cs(cA,cB){cn=true;cw=cf=(cq==="vertical")?cA._y:cA._x;co=cA._now;cy=[[co,cw]];cx=cj=cm.noMove||cB?0:a1(cz,(cd.getPos||g)());(cd.onStart||g).call(cc,cA)}function cu(cB,cA){cr=cm.min;ct=cm.max;cp=cm.snap,cq=cm.direction||"horizontal",cz.navdir=cq;i=cB.altKey;cn=ci=false;cv=cA.control;if(!cv&&!ce.sliding){cs(cB)}}function cl(cB,cA){if(!cm.noSwipe){if(!cn){cs(cB)}cf=(cq==="vertical")?cB._y:cB._x;cy.push([cB._now,cf]);cj=cx-(cw-cf);ch=bp(cj,cr,ct,cq);if(cj<=cr){cj=aF(cj,cr)}else{if(cj>=ct){cj=aF(cj,ct)}}if(!cm.noMove){cz.css(b2(cj,cq));if(!ci){ci=true;cA.touch||aZ||cz.addClass(bR)}(cd.onMove||g).call(cc,cB,{pos:cj,edge:ch})}}}function ck(cJ){if(cm.noSwipe&&cJ.moved){return}if(!cn){cs(cJ.startEvent,true)}cJ.touch||aZ||cz.removeClass(bR);cg=bV.now();var cG=cg-b8,cK,cP,cQ,cS=null,cA,cE,cN,cD,cF,cI=ba,cO,cH=cd.friction;for(var cC=cy.length-1;cC>=0;cC--){cK=cy[cC][0];cP=Math.abs(cK-cG);if(cS===null||cP<cQ){cS=cK;cA=cy[cC][1]}else{if(cS===cG||cP>cQ){break}}cQ=cP}cD=bb(cj,cr,ct);var cT=cA-cf,cR=cT>=0,cL=cg-cS,cB=cL>b8,cM=!cB&&cj!==cx&&cD===cj;if(cp){cD=bb(Math[cM?(cR?"floor":"ceil"):"round"](cj/cp)*cp,cr,ct);cr=ct=cD}if(cM&&(cp||cD===cj)){cO=-(cT/cL);cI*=bb(Math.abs(cO),cd.timeLow,cd.timeHigh);cE=Math.round(cj+cO*cI/cH);if(!cp){cD=cE}if(!cR&&cE>ct||cR&&cE<cr){cN=cR?cr:ct;cF=cE-cN;if(!cp){cD=cN}cF=bb(cD+cF*0.03,cN-50,cN+50);cI=Math.abs((cj-cF)/(cO/cH))}}cI*=i?10:1;(cd.onEnd||g).call(cc,bV.extend(cJ,{moved:cJ.moved||cB&&cp,pos:cj,newPos:cD,overPos:cF,time:cI,dir:cq}))}cm=bV.extend(aI(cd.$wrap,bV.extend({},cd,{onStart:cu,onMove:cl,onEnd:ck})),cm);return cm}function o(ce,cd){var cg=ce[0],ch,cf,i,cc={prevent:{}};D(cg,y,function(co){var cl=co.wheelDeltaY||-1*co.deltaY||0,cn=co.wheelDeltaX||-1*co.deltaX||0,ck=Math.abs(cn)&&!Math.abs(cl),cm=ay(cn<0),cp=cf===cm,ci=bV.now(),cj=ci-i<b8;cf=cm;i=ci;if(!ck||!cc.ok||cc.prevent[cm]&&!ch){return}else{O(co,true);if(ch&&cp&&cj){return}}if(cd.shift){ch=true;clearTimeout(cc.t);cc.t=setTimeout(function(){ch=false},bY)}(cd.onEnd||g)(co,cd.shift?cm:cn)});return cc}jQuery.Fotorama=function(d6,c3){R=bV("html");I=bV("body");var cg=this,cC=bV.now(),cQ=ag+cC,eu=d6[0],dP,cY=1,cR=d6.data(),c1,de=bV("<style></style>"),c9=bV(ab(bs)),dk=d6.find(aT(ae)),cf=dk.find(aT(a7)),dY=cf[0],cl=d6.find(aT(B)),c8=bV(),dW=d6.find(aT(bc)),da=d6.find(aT(r)),cU=d6.find(aT(aK)),dU=d6.find(aT(bq)),dO=dU.find(aT(bO)),cF=dO.find(aT(aH)),dA,cB=bV(),cW=bV(),dS=cl.data(),cX=cF.data(),c7=d6.find(aT(cb)),eg=d6.find(aT(b0)),dX=d6.find(aT(H)),dM=d6.find(aT(aJ)),dD=dM[0],cH=bV(ab(T)),dt=d6.find(aT(w)),d1=dt[0],eb=d6.find(aT(ca)),dg,eo=false,dF,ea,c2,ed,dw,d4,cN,cK,dx,dj,cq,c0,d8,c4,d2,cv,ch,ej,ds,cu,ec,dH,dE,d0={},en={},dG,d5={},cG={},dy={},ef={},cs,cT,ee,cj,el,cd={},er={},dZ,c6,dz,dr,d3=0,cI=[];dk[bu]=bV('<div class="'+x+'"></div>');dk[bl]=bV(bV.Fotorama.jst.thumb());dk[b7]=bV(bV.Fotorama.jst.dots());cd[bu]=[];cd[bl]=[];cd[b7]=[];er[bu]={};dk.addClass(aA?aX:ah);cR.fotorama=this;function ep(){bV.each(dP,function(ey,eA){if(!eA.i){eA.i=cY++;var ez=at(eA.video,true);if(ez){var ex={};eA.video=ez;if(!eA.img&&!eA.thumb){ex=aQ(eA,dP,cg)}else{eA.thumbsReady=true}v(dP,{img:ex.img,thumb:ex.thumb},eA.i,cg)}}})}function df(ex){return dE[ex]}function i(){if(cf!==aP){if(c3.navdir=="vertical"){var ex=c3.thumbwidth+c3.thumbmargin;cf.css("left",ex);da.css("right",ex);dM.css("right",ex);dk.css("width",dk.css("width")+ex);cl.css("max-width",dk.width()-ex)}else{cf.css("left","");da.css("right","");dM.css("right","");dk.css("width",dk.css("width")+ex);cl.css("max-width","")}}}function ek(eB){var eC="keydown."+ag,eD=ag+cC,ex="keydown."+eD,eA="keyup."+eD,ey="resize."+eD+" orientationchange."+eD,ez;if(eB){bw.on(ex,function(eG){var eF,eE;if(dg&&eG.keyCode===27){eF=true;cO(dg,true,true)}else{if(cg.fullScreen||(c3.keyboard&&!cg.index)){if(eG.keyCode===27){eF=true;cg.cancelFullScreen()}else{if((eG.shiftKey&&eG.keyCode===32&&df("space"))||(!eG.altKey&&!eG.metaKey&&eG.keyCode===37&&df("left"))||(eG.keyCode===38&&df("up")&&bV(":focus").attr("data-gallery-role"))){cg.longPress.progress();eE="<"}else{if((eG.keyCode===32&&df("space"))||(!eG.altKey&&!eG.metaKey&&eG.keyCode===39&&df("right"))||(eG.keyCode===40&&df("down")&&bV(":focus").attr("data-gallery-role"))){cg.longPress.progress();eE=">"}else{if(eG.keyCode===36&&df("home")){cg.longPress.progress();eE="<<"}else{if(eG.keyCode===35&&df("end")){cg.longPress.progress();eE=">>"}}}}}}}(eF||eE)&&O(eG);ez={index:eE,slow:eG.altKey,user:true};eE&&(cg.longPress.inProgress?cg.showWhileLongPress(ez):cg.show(ez))});if(eB){bw.on(eA,function(eE){if(cg.longPress.inProgress){cg.showEndLongPress({user:true})}cg.longPress.reset()})}if(!cg.index){bw.off(eC).on(eC,"textarea, input, select",function(eE){!I.hasClass(bH)&&eE.stopPropagation()})}bf.on(ey,cg.resize)}else{bw.off(ex);bf.off(ey)}}function dd(ex){if(ex===dd.f){return}if(ex){d6.addClass(ag+" "+cQ).before(c9).before(de);C(cg)}else{c9.detach();de.detach();d6.html(cR.urtext).removeClass(cQ);av(cg)}ek(ex);dd.f=ex}function dn(){dP=cg.data=dP||bG(c3.data)||bI(d6);c1=cg.size=dP.length;eq.ok&&c3.shuffle&&aC(dP);ep();eo=cn(eo);c1&&dd(true)}function em(){var ex=c1<2||dg;d5.noMove=ex||cv;d5.noSwipe=ex||!c3.swipe;!cu&&cl.toggleClass(aB,!c3.click&&!d5.noMove&&!d5.noSwipe);aZ&&dk.toggleClass(U,!d5.noSwipe)}function dq(ex){if(ex===true){ex=""}c3.autoplay=Math.max(+ex||bQ,ds*1.5)}function db(ex){if(ex.navarrows&&ex.nav==="thumbs"){eg.show();dX.show()}else{eg.hide();dX.hide()}}function ck(ex,ey){return Math.floor(dk.width()/(ey.thumbwidth+ey.thumbmargin))}function dQ(){if(!c3.nav||c3.nav==="dots"){c3.navdir="horizontal"}cg.options=c3=bA(c3);b4=ck(dk,c3);cv=(c3.transition==="crossfade"||c3.transition==="dissolve");dj=c3.loop&&(c1>2||(cv&&(!cu||cu!=="slide")));ds=+c3.transitionduration||ba;dH=c3.direction==="rtl";dE=bV.extend({},c3.keyboard&&p,c3.keyboard);db(c3);var ey={add:[],remove:[]};function ex(ez,eA){ey[ez?"add":"remove"].push(eA)}if(c1>1){cq=c3.nav;d8=c3.navposition==="top";ey.remove.push(a9);cU.toggle(!!c3.arrows)}else{cq=false;cU.hide()}dh();cJ();ev();if(c3.autoplay){dq(c3.autoplay)}ch=m(c3.thumbwidth)||L;ej=m(c3.thumbheight)||L;cG.ok=ef.ok=c3.trackpad&&!bh;em();dL(c3,[en]);c0=cq==="thumbs";if(dU.filter(":hidden")&&!!cq){dU.show()}if(c0){dl(c1,"navThumb");dA=cW;dr=bl;an(de,bV.Fotorama.jst.style({w:ch,h:ej,b:c3.thumbborderwidth,m:c3.thumbmargin,s:cC,q:!aN}));dO.addClass(ai).removeClass(bW)}else{if(cq==="dots"){dl(c1,"navDot");dA=cB;dr=b7;dO.addClass(bW).removeClass(ai)}else{dU.hide();cq=false;dO.removeClass(ai+" "+bW)}}if(cq){if(d8){dU.insertBefore(cf)}else{dU.insertAfter(cf)}cz.nav=false;cz(dA,cF,"nav")}c4=c3.allowfullscreen;if(c4){dM.prependTo(cf);d2=s&&c4==="native";aE(dM,"touchend")}else{dM.detach();d2=false}ex(cv,ar);ex(!cv,aw);ex(!c3.captions,bN);ex(dH,a0);ex(c3.arrows,f);ec=c3.shadows&&!bh;ex(!ec,aM);dk.addClass(ey.add.join(" ")).removeClass(ey.remove.join(" "));d0=bV.extend({},c3);i()}function cZ(ex){return ex<0?(c1+(ex%c1))%c1:ex>=c1?ex%c1:ex}function cn(ex){return bb(ex,0,c1-1)}function du(ex){return dj?cZ(ex):cn(ex)}function dB(ex){return ex>0||dj?ex-1:false}function ci(ex){return ex<c1-1||dj?ex+1:false}function d9(){d5.min=dj?-Infinity:-a8(c1-1,en.w,c3.margin,c2);d5.max=dj?Infinity:-a8(0,en.w,c3.margin,c2);d5.snap=en.w+c3.margin}function c5(){var ex=(c3.navdir==="vertical");var ez=ex?cF.height():cF.width();var ey=ex?en.h:en.nw;dy.min=Math.min(0,ey-ez);dy.max=0;dy.direction=c3.navdir;cF.toggleClass(aB,!(dy.noMove=dy.min===dy.max))}function dm(ey,eA,ez){if(typeof ey==="number"){ey=new Array(ey);var ex=true}return bV.each(ey,function(eD,eB){if(ex){eB=eD}if(typeof eB==="number"){var eF=dP[cZ(eB)];if(eF){var eC="$"+eA+"Frame",eE=eF[eC];ez.call(this,eD,eB,eF,eE,eC,eE&&eE.data())}}})}function cc(eA,ex,ez,ey){if(!dG||(dG==="*"&&ey===dx)){eA=t(c3.width)||t(eA)||bk;ex=t(c3.height)||t(ex)||bE;cg.resize({width:eA,ratio:c3.ratio||ez||eA/ex},0,ey!==dx&&"*")}}function cx(ex,ey,eA,ez){dm(ex,ey,function(eM,eE,eD,eC,eR,eB){if(!eC){return}var eN=cg.fullScreen&&!eB.$full&&ey==="stage";if(eB.$img&&!ez&&!eN){return}var eS=new Image(),eG=bV(eS),eO=eG.data();eB[eN?"$full":"$img"]=eG;var eJ=ey==="stage"?(eN?"full":"img"):"thumb",eF=eD[eJ],eP=eN?eD.img:eD[ey==="stage"?"thumb":"img"];if(ey==="navThumb"){eC=eB.$wrap}function eH(eT){var eU=cZ(eE);dc(eT,{index:eU,src:eF,frame:dP[eU]})}function eK(){eG.remove();bV.Fotorama.cache[eF]="error";if((!eD.html||ey!=="stage")&&eP&&eP!==eF){eD[eJ]=eF=eP;eB.$full=null;cx([eE],ey,eA,true)}else{if(eF&&!eD.html&&!eN){eC.trigger("f:error").removeClass(bM).addClass(bP);eH("error")}else{if(ey==="stage"){eC.trigger("f:load").removeClass(bM+" "+bP).addClass(c);eH("load");cc()}}eB.state="error";if(c1>1&&dP[eE]===eD&&!eD.html&&!eD.deleted&&!eD.video&&!eN){eD.deleted=true;cg.splice(eE,1)}}}function eL(){bV.Fotorama.measures[eF]=eO.measures=bV.Fotorama.measures[eF]||{width:eS.width,height:eS.height,ratio:eS.width/eS.height};cc(eO.measures.width,eO.measures.height,eO.measures.ratio,eE);eG.off("load error").addClass(""+(eN?Y:J)).attr("aria-hidden","false").prependTo(eC);if(eC.hasClass(x)&&!eC.hasClass(af)){eC.attr("href",eG.attr("src"))}V(eG,(bV.isFunction(eA)?eA():eA)||en);bV.Fotorama.cache[eF]=eB.state="loaded";setTimeout(function(){eC.trigger("f:load").removeClass(bM+" "+bP).addClass(c+" "+(eN?b3:bg));if(ey==="stage"){eH("load")}else{if(eD.thumbratio===bF||!eD.thumbratio&&c3.thumbratio===bF){eD.thumbratio=eO.measures.ratio;dV()}}},0)}if(!eF){eK();return}function eI(){var eT=10;bX(function(){return !c6||!eT--&&!bh},function(){eL()})}if(!bV.Fotorama.cache[eF]){bV.Fotorama.cache[eF]="*";eG.on("load",eI).on("error",eK)}else{(function eQ(){if(bV.Fotorama.cache[eF]==="error"){eK()}else{if(bV.Fotorama.cache[eF]==="loaded"){setTimeout(eI,0)}else{setTimeout(eQ,100)}}})()}eB.state="";eS.src=eF;if(eB.data.caption){eS.alt=eB.data.caption||""}if(eB.data.full){bV(eS).data("original",eB.data.full)}if(aS.isExpectedCaption(eD,c3.showcaption)){bV(eS).attr("aria-labelledby",eD.labelledby)}})}function cy(){var ex=dF[bu];if(ex&&!ex.data().state){eb.addClass(Z);ex.on("f:load f:error",function(){ex.off("f:load f:error");eb.removeClass(Z)})}}function cL(ex){a(ex,dJ);bL(ex,function(){setTimeout(function(){bU(dO)},0);dT({time:ds,guessIndex:bV(this).data().eq,minMax:dy})})}function dl(ex,ey){dm(ex,ey,function(eB,ez,eG,eD,eA,eC){if(eD){return}eD=eG[eA]=dk[eA].clone();eC=eD.data();eC.data=eG;var eF=eD[0],eE="labelledby"+bV.now();if(ey==="stage"){if(eG.html){bV('<div class="'+bd+'"></div>').append(eG._html?bV(eG.html).removeAttr("id").html(eG._html):eG.html).appendTo(eD)}if(eG.id){eE=eG.id||eE}eG.labelledby=eE;if(aS.isExpectedCaption(eG,c3.showcaption)){bV(bV.Fotorama.jst.frameCaption({caption:eG.caption,labelledby:eE})).appendTo(eD)}eG.video&&eD.addClass(l).append(cH.clone());bL(eF,function(){setTimeout(function(){bU(cf)},0);cm({index:eC.eq,user:true})});c8=c8.add(eD)}else{if(ey==="navDot"){cL(eF);cB=cB.add(eD)}else{if(ey==="navThumb"){cL(eF);eC.$wrap=eD.children(":first");cW=cW.add(eD);if(eG.video){eC.$wrap.append(cH.clone())}}}}})}function cM(ey,ex){return ey&&ey.length&&V(ey,ex)}function di(ex){dm(ex,"stage",function(eB,ez,eE,eD,eA,eC){if(!eD){return}var ey=cZ(ez);eC.eq=ey;er[bu][ey]=eD.css(bV.extend({left:cv?0:a8(ez,en.w,c3.margin,c2)},cv&&b6(0)));if(be(eD[0])){eD.appendTo(cl);cO(eE.$video)}cM(eC.$img,en);cM(eC.$full,en);if(eD.hasClass(x)&&!(eD.attr("aria-hidden")==="false"&&eD.hasClass(a4))){eD.attr("aria-hidden","true")}})}function dp(eB,ex){var ey,ez,eA;if(cq!=="thumbs"||isNaN(eB)){return}ey=-eB;ez=-eB+en.nw;if(c3.navdir==="vertical"){eB=eB-c3.thumbheight;ez=-eB+en.h}cW.each(function(){var eH=bV(this),eD=eH.data(),eC=eD.eq,eG=function(){return{h:ej,w:eD.w}},eF=eG(),eE=c3.navdir==="vertical"?eD.t>ez:eD.l>ez;eF.w=eD.w;if(eD.l+eD.w<ey||eE||cM(eD.$img,eF)){return}ex&&cx([eC],"navThumb",eG)})}function cz(ex,eC,ey){if(!cz[ey]){var eB=ey==="nav"&&c0,eA=0,ez=0;eC.append(ex.filter(function(){var eH,eG=bV(this),eE=eG.data();for(var eF=0,eD=dP.length;eF<eD;eF++){if(eE.data===dP[eF]){eH=true;eE.eq=eF;break}}return eH||eG.remove()&&false}).sort(function(eE,eD){return bV(eE).data().eq-bV(eD).data().eq}).each(function(){var eE=bV(this),eD=eE.data();aS.setThumbAttr(eE,eD.data.caption,"aria-label")}).each(function(){if(!eB){return}var eF=bV(this),eE=eF.data(),eG=Math.round(ej*eE.data.thumbratio)||ch,eD=Math.round(ch/eE.data.thumbratio)||ej;eE.t=ez;eE.h=eD;eE.l=eA;eE.w=eG;eF.css({width:eG});ez+=eD+c3.thumbmargin;eA+=eG+c3.thumbmargin}));cz[ey]=true}}function eh(ex){return ex-d3>en.w/3}function cE(ex){return !dj&&(!(eo+ex)||!(eo-c1+ex))&&!dg}function dh(){var ey=cE(0),ex=cE(1);dW.toggleClass(F,ey).attr(ad(ey,false));da.toggleClass(F,ex).attr(ad(ex,false))}function ev(){var ex=false,ey=false;if(c3.navtype==="thumbs"&&!c3.loop){(eo==0)?ex=true:ex=false;(eo==c3.data.length-1)?ey=true:ey=false}if(c3.navtype==="slides"){var ez=aa(cF,c3.navdir);ez>=dy.max?ex=true:ex=false;ez<=dy.min?ey=true:ey=false}eg.toggleClass(F,ex).attr(ad(ex,true));dX.toggleClass(F,ey).attr(ad(ey,true))}function cJ(){if(cG.ok){cG.prevent={"<":cE(0),">":cE(1)}}}function dI(eD){var eA=eD.data(),eC,eB,ez,ex;if(c0){eC=eA.l;eB=eA.t;ez=eA.w;ex=eA.h}else{eC=eD.position().left;ez=eD.width()}var ey={c:eC+ez/2,min:-eC+c3.thumbmargin*10,max:-eC+en.w-ez-c3.thumbmargin*10};var eE={c:eB+ex/2,min:-eB+c3.thumbmargin*10,max:-eB+en.h-ex-c3.thumbmargin*10};return c3.navdir==="vertical"?eE:ey}function d7(ey){var ex=dF[dr].data();A(c7,{time:ey*1.2,pos:(c3.navdir==="vertical"?ex.t:ex.l),width:ex.w,height:ex.h,direction:c3.navdir})}function dT(eH){var eB=dP[eH.guessIndex][dr],ez=c3.navtype;var eD,ex,eA,eG,eC,ey,eE,eF;if(eB){if(ez==="thumbs"){eD=dy.min!==dy.max;eA=eH.minMax||eD&&dI(dF[dr]);eG=eD&&(eH.keep&&dT.t?dT.l:bb((eH.coo||en.nw/2)-dI(eB).c,eA.min,eA.max));eC=eD&&(eH.keep&&dT.l?dT.l:bb((eH.coo||en.nw/2)-dI(eB).c,eA.min,eA.max));ey=(c3.navdir==="vertical"?eG:eC);eE=eD&&bb(ey,dy.min,dy.max)||0;ex=eH.time*1.1;A(cF,{time:ex,pos:eE,direction:c3.navdir,onEnd:function(){dp(eE,true);ev()}});co(dO,bp(eE,dy.min,dy.max,c3.navdir));dT.l=ey}else{eF=aa(cF,c3.navdir);ex=eH.time*1.11;eE=aD(c3,dy,eH.guessIndex,eF,eB,dU,c3.navdir);A(cF,{time:ex,pos:eE,direction:c3.navdir,onEnd:function(){dp(eE,true);ev()}});co(dO,bp(eE,dy.min,dy.max,c3.navdir))}}}function cS(){dN(dr);cd[dr].push(dF[dr].addClass(a4).attr("data-active",true))}function dN(ey){var ex=cd[ey];while(ex.length){ex.shift().removeClass(a4).attr("data-active",false)}}function ce(ey){var ex=er[ey];bV.each(ea,function(eA,ez){delete ex[cZ(ez)]});bV.each(ex,function(ez,eA){delete ex[ez];eA.detach()})}function dC(ey){c2=ed=eo;var ex=dF[bu];if(ex){dN(bu);cd[bu].push(ex.addClass(a4).attr("data-active",true));if(ex.hasClass(x)){ex.attr("aria-hidden","false")}ey||cg.showStage.onEnd(true);a1(cl,0,true);ce(bu);di(ea);d9();c5();a(cl[0],function(){if(!d6.hasClass(M)){cg.requestFullScreen();dM.focus()}})}}function dL(ey,ex){if(!ey){return}bV.each(ex,function(ez,eA){if(!eA){return}bV.extend(eA,{width:ey.width||eA.width,height:ey.height,minwidth:ey.minwidth,maxwidth:ey.maxwidth,minheight:ey.minheight,maxheight:ey.maxheight,ratio:bm(ey.ratio)})})}function dc(ey,ex){d6.trigger(ag+":"+ey,[cg,ex])}function dR(){clearTimeout(cr.t);c6=1;if(c3.stopautoplayontouch){cg.stopAutoplay()}else{cj=true}}function cr(){if(!c6){return}if(!c3.stopautoplayontouch){cw();es()}cr.t=setTimeout(function(){c6=0},ba+b8)}function cw(){cj=!!(dg||el)}function es(){clearTimeout(es.t);bX.stop(es.w);if(!c3.autoplay||cj){if(cg.autoplay){cg.autoplay=false;dc("stopautoplay")}return}if(!cg.autoplay){cg.autoplay=true;dc("startautoplay")}var ey=eo;var ex=dF[bu].data();es.w=bX(function(){return ex.state||ey!==eo},function(){es.t=setTimeout(function(){if(cj||ey!==eo){return}var ez=cK,eA=dP[ez][bu].data();es.w=bX(function(){return eA.state||ez!==cK},function(){if(cj||ez!==cK){return}cg.show(dj?ay(!dH):cK)})},c3.autoplay)})}cg.startAutoplay=function(ex){if(cg.autoplay){return this}cj=el=false;dq(ex||c3.autoplay);es();return this};cg.stopAutoplay=function(){if(cg.autoplay){cj=el=true;es()}return this};cg.showSlide=function(ez){var eA=aa(cF,c3.navdir),eC,eB=500*1.1,ey=c3.navdir==="horizontal"?c3.thumbwidth:c3.thumbheight,ex=function(){ev()};if(ez==="next"){eC=eA-(ey+c3.margin)*b4}if(ez==="prev"){eC=eA+(ey+c3.margin)*b4}eC=a5(eC,dy);dp(eC,true);A(cF,{time:eB,pos:eC,direction:c3.navdir,onEnd:ex})};cg.showWhileLongPress=function(eA){if(cg.longPress.singlePressInProgress){return}var ez=dK(eA);ew(ez);var eB=cA(eA)/50;var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showNav(ex,eA,eB);return this};cg.showEndLongPress=function(eA){if(cg.longPress.singlePressInProgress){return}var ez=dK(eA);ew(ez);var eB=cA(eA)/50;var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showStage(ex,eA,eB);ee=typeof dw!=="undefined"&&dw!==eo;dw=eo;return this};function dK(ey){var ex;if(typeof ey!=="object"){ex=ey;ey={}}else{ex=ey.index}ex=ex===">"?ed+1:ex==="<"?ed-1:ex==="<<"?0:ex===">>"?c1-1:ex;ex=isNaN(ex)?aP:ex;ex=typeof ex==="undefined"?eo||0:ex;return ex}function ew(ex){cg.activeIndex=eo=du(ex);d4=dB(eo);cN=ci(eo);cK=cZ(eo+(dH?-1:1));ea=[eo,d4,cN];ed=dj?ex:eo}function cA(ey){var ex=Math.abs(dw-ed),ez=bj(ey.time,function(){return Math.min(ds*(1+(ex-1)/12),ds*2)});if(ey.slow){ez*=10}return ez}cg.showStage=function(ey,eA,eD){cO(dg,dF.i!==dP[cZ(c2)].i);dl(ea,"stage");di(bh?[ed]:[ed,dB(ed),ci(ed)]);cD("go",true);ey||dc("show",{user:eA.user,time:eD});cj=true;var eC=eA.overPos;var ez=cg.showStage.onEnd=function(eE){if(ez.ok){return}ez.ok=true;eE||dC(true);if(!ey){dc("showend",{user:eA.user})}if(!eE&&cu&&cu!==c3.transition){cg.setOptions({transition:cu});cu=false;return}cy();cx(ea,"stage");cD("go",false);cJ();ei();cw();es();if(cg.fullScreen){dF[bu].find("."+Y).attr("aria-hidden",false);dF[bu].find("."+J).attr("aria-hidden",true)}else{dF[bu].find("."+Y).attr("aria-hidden",true);dF[bu].find("."+J).attr("aria-hidden",false)}};if(!cv){A(cl,{pos:-a8(ed,en.w,c3.margin,c2),overPos:eC,time:eD,onEnd:ez})}else{var ex=dF[bu],eB=dP[dw]&&eo!==dw?dP[dw][bu]:null;aq(ex,eB,c8,{time:eD,method:c3.transition,onEnd:ez},cI)}dh()};cg.showNav=function(ey,ez,eA){ev();if(cq){cS();var ex=cn(eo+bb(ed-dw,-1,1));dT({time:eA,coo:ex!==eo&&ez.coo,guessIndex:typeof ez.coo!=="undefined"?ex:eo,keep:ey});if(c0){d7(eA)}}};cg.show=function(eA){cg.longPress.singlePressInProgress=true;var ez=dK(eA);ew(ez);var eB=cA(eA);var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showStage(ex,eA,eB);cg.showNav(ex,eA,eB);ee=typeof dw!=="undefined"&&dw!==eo;dw=eo;cg.longPress.singlePressInProgress=false;return this};cg.requestFullScreen=function(){if(c4&&!cg.fullScreen){var ex=bV((cg.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container");if(ex){return}cs=bf.scrollTop();cT=bf.scrollLeft();bU(bf);cD("x",true);dZ=bV.extend({},en);d6.addClass(M).appendTo(I.addClass(bH));R.addClass(bH);cO(dg,true,true);cg.fullScreen=true;if(d2){bB.request(eu)}cg.resize();cx(ea,"stage");cy();dc("fullscreenenter");if(!("ontouchstart" in bo)){dM.focus()}}return this};function cP(){if(cg.fullScreen){cg.fullScreen=false;if(s){bB.cancel(eu)}I.removeClass(bH);R.removeClass(bH);d6.removeClass(M).insertAfter(c9);en=bV.extend({},dZ);cO(dg,true,true);cD("x",false);cg.resize();cx(ea,"stage");bU(bf,cT,cs);dc("fullscreenexit")}}cg.cancelFullScreen=function(){if(d2&&bB.is()){bB.cancel(k)}else{cP()}return this};cg.toggleFullScreen=function(){return cg[(cg.fullScreen?"cancel":"request")+"FullScreen"]()};cg.resize=function(ez){if(!dP){return this}var eC=arguments[1]||0,ey=arguments[2];b4=ck(dk,c3);dL(!cg.fullScreen?bA(ez):{width:bV(bo).width(),maxwidth:null,minwidth:null,height:bV(bo).height(),maxheight:null,minheight:null},[en,ey||cg.fullScreen||c3]);var eB=en.width,ex=en.height,eA=en.ratio,eD=bf.height()-(cq?dO.height():0);if(t(eB)){dk.css({width:""});dk.css({height:""});cf.css({width:""});cf.css({height:""});cl.css({width:""});cl.css({height:""});dO.css({width:""});dO.css({height:""});dk.css({minWidth:en.minwidth||0,maxWidth:en.maxwidth||bK});if(cq==="dots"){dU.hide()}eB=en.W=en.w=dk.width();en.nw=cq&&d(c3.navwidth,eB)||eB;cl.css({width:en.w,marginLeft:(en.W-en.w)/2});ex=d(ex,eD);ex=ex||(eA&&eB/eA);if(ex){eB=Math.round(eB);ex=en.h=Math.round(bb(ex,d(en.minheight,eD),d(en.maxheight,eD)));cf.css({width:eB,height:ex});if(c3.navdir==="vertical"&&!cg.fullscreen){dO.width(c3.thumbwidth+c3.thumbmargin*2)}if(c3.navdir==="horizontal"&&!cg.fullscreen){dO.height(c3.thumbheight+c3.thumbmargin*2)}if(cq==="dots"){dO.width(eB).height("auto");dU.show()}if(c3.navdir==="vertical"&&cg.fullScreen){cf.css("height",bf.height())}if(c3.navdir==="horizontal"&&cg.fullScreen){cf.css("height",bf.height()-dO.height())}if(cq){switch(c3.navdir){case"vertical":dU.removeClass(bZ);dU.removeClass(ax);dU.addClass(b);dO.stop().animate({height:en.h,width:c3.thumbwidth},eC);break;case"list":dU.removeClass(b);dU.removeClass(bZ);dU.addClass(ax);break;default:dU.removeClass(b);dU.removeClass(ax);dU.addClass(bZ);dO.stop().animate({width:en.nw},eC);break}dC();dT({guessIndex:eo,time:eC,keep:true});if(c0&&cz.nav){d7(eC)}}dG=ey||true;eq.ok=true;eq()}}d3=cf.offset().left;i();return this};cg.setOptions=function(ex){bV.extend(c3,ex);dV();return this};cg.shuffle=function(){dP&&aC(dP)&&dV();return this};function co(ex,ey){if(ec){ex.removeClass(S+" "+aL);ex.removeClass(a2+" "+aR);ey&&!dg&&ex.addClass(ey.replace(/^|\s/g," "+bz+"--"))}}cg.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){if(!this.inProgress){this.count++;this.inProgress=this.count>this.threshold}},end:function(){if(this.inProgress){this.isEnded=true}},reset:function(){this.count=0;this.inProgress=false;this.isEnded=false}};cg.destroy=function(){cg.cancelFullScreen();cg.stopAutoplay();dP=cg.data=null;dd();ea=[];ce(bu);dV.ok=false;return this};cg.playVideo=function(){var ez=dF,ex=ez.video,ey=eo;if(typeof ex==="object"&&ez.videoReady){d2&&cg.fullScreen&&cg.cancelFullScreen();bX(function(){return !bB.is()||ey!==eo},function(){if(ey===eo){ez.$video=ez.$video||bV(ab(bJ)).append(q(ex));ez.$video.appendTo(ez[bu]);dk.addClass(bt);dg=ez.$video;em();cU.blur();dM.blur();dc("loadvideo")}})}return this};cg.stopVideo=function(){cO(dg,true,true);return this};cg.spliceByIndex=function(ex,ey){ey.i=ex+1;ey.img&&bV.ajax({url:ey.img,type:"HEAD",success:function(){dP.splice(ex,1,ey);dV()}})};function cO(ex,ez,ey){if(ez){dk.removeClass(bt);dg=false;em()}if(ex&&ex!==dg){ex.remove();dc("unloadvideo")}if(ey){cw();es()}}function cp(ex){dk.toggleClass(P,ex)}function ei(ez){if(d5.flow){return}var ex=ez?ez.pageX:ei.x,ey=ex&&!cE(eh(ex))&&c3.click;if(ei.p!==ey&&cf.toggleClass(bC,ey)){ei.p=ey;ei.x=ex}}cf.on("mousemove",ei);function cm(ex){clearTimeout(cm.t);if(c3.clicktransition&&c3.clicktransition!==c3.transition){setTimeout(function(){var ey=c3.transition;cg.setOptions({transition:c3.clicktransition});cu=ey;cm.t=setTimeout(function(){cg.show(ex)},10)},0)}else{cg.show(ex)}}function ct(eA,ey){var ez=eA.target,ex=bV(ez);if(ex.hasClass(T)){cg.playVideo()}else{if(ez===dD){cg.toggleFullScreen()}else{if(dg){ez===d1&&cO(dg,true,true)}else{if(!d6.hasClass(M)){cg.requestFullScreen()}}}}O(eA,true)}function cD(ex,ey){d5[ex]=dy[ex]=ey}d5=ao(cl,{onStart:dR,onMove:function(ey,ex){co(cf,ex.edge)},onTouchEnd:cr,onEnd:function(ex){var ez;co(cf);ez=(aZ&&!dz||ex.touch)&&c3.arrows;if((ex.moved||(ez&&ex.pos!==ex.newPos&&!ex.control))&&ex.$target[0]!==dM[0]){var ey=by(ex.newPos,en.w,c3.margin,c2);cg.show({index:ey,time:cv?ds:ex.time,overPos:ex.overPos,user:true})}else{if(!ex.aborted&&!ex.control){ct(ex.startEvent,ez)}}},timeLow:1,timeHigh:1,friction:2,select:"."+a9+", ."+a9+" *",$wrap:cf,direction:"horizontal"});dy=ao(cF,{onStart:dR,onMove:function(ey,ex){co(dO,ex.edge)},onTouchEnd:cr,onEnd:function(ex){function ey(){dT.l=ex.newPos;cw();es();dp(ex.newPos,true);ev()}if(!ex.moved){var ez=ex.$target.closest("."+aG,cF)[0];ez&&dJ.call(ez,ex.startEvent)}else{if(ex.pos!==ex.newPos){cj=true;A(cF,{time:ex.time,pos:ex.newPos,overPos:ex.overPos,direction:c3.navdir,onEnd:ey});dp(ex.newPos);ec&&co(dO,bp(ex.newPos,dy.min,dy.max,ex.dir))}else{ey()}}},timeLow:0.5,timeHigh:2,friction:5,$wrap:dO,direction:c3.navdir});cG=o(cf,{shift:true,onEnd:function(ey,ex){dR();cr();cg.show({index:ex,slow:ey.altKey})}});ef=o(dO,{onEnd:function(ez,ey){dR();cr();var ex=a1(cF)+ey*0.25;cF.css(b2(bb(ex,dy.min,dy.max),c3.navdir));ec&&co(dO,bp(ex,dy.min,dy.max,c3.navdir));ef.prevent={"<":ex>=dy.max,">":ex<=dy.min};clearTimeout(ef.t);ef.t=setTimeout(function(){dT.l=ex;dp(ex,true)},b8);dp(ex)}});dk.hover(function(){setTimeout(function(){if(c6){return}cp(!(dz=true))},0)},function(){if(!dz){return}cp(!(dz=false))});function dJ(ey){var ex=bV(this).data().eq;if(c3.navtype==="thumbs"){cm({index:ex,slow:ey.altKey,user:true,coo:ey._x-dO.offset().left})}else{cm({index:ex,slow:ey.altKey,user:true})}}function et(ex){cm({index:cU.index(this)?">":"<",slow:ex.altKey,user:true})}z(cU,function(ex){O(ex);et.call(this,ex)},{onStart:function(){dR();d5.control=true},onTouchEnd:cr});z(eg,function(ex){O(ex);if(c3.navtype==="thumbs"){cg.show("<")}else{cg.showSlide("prev")}});z(dX,function(ex){O(ex);if(c3.navtype==="thumbs"){cg.show(">")}else{cg.showSlide("next")}});function dv(ex){bL(ex,function(){setTimeout(function(){bU(cf)},0);cp(false)})}cU.each(function(){a(this,function(ex){et.call(this,ex)});dv(this)});a(dD,function(){if(d6.hasClass(M)){cg.cancelFullScreen();cl.focus()}else{cg.requestFullScreen();dM.focus()}});dv(dD);function dV(){dn();dQ();if(!dV.i){dV.i=true;var ex=c3.startindex;eo=c2=ed=dw=dx=du(ex)||0}if(c1){if(cV()){return}if(dg){cO(dg,true)}ea=[];ce(bu);dV.ok=true;cg.show({index:eo,time:0});cg.resize()}else{cg.destroy()}}function cV(){if(!cV.f===dH){cV.f=dH;eo=c1-1-eo;cg.reverse();return true}}bV.each("load push pop shift unshift reverse sort splice".split(" "),function(ex,ey){cg[ey]=function(){dP=dP||[];if(ey!=="load"){Array.prototype[ey].apply(dP,arguments)}else{if(arguments[0]&&typeof arguments[0]==="object"&&arguments[0].length){dP=bG(arguments[0])}}dV();return cg}});function eq(){if(eq.ok){eq.ok=false;dc("ready")}}dV()};bV.fn.fotorama=function(i){return this.each(function(){var ce=this,cd=bV(this),cc=cd.data(),cf=cc.fotorama;if(!cf){bX(function(){return !W(ce)},function(){cc.urtext=cd.html();new bV.Fotorama(cd,bV.extend({},Q,bo.fotoramaDefaults,i,cc))})}else{cf.setOptions(i,true)}})};bV.Fotorama.instances=[];function b1(){bV.each(bV.Fotorama.instances,function(cc,i){i.index=cc})}function C(i){bV.Fotorama.instances.push(i);b1()}function av(i){bV.Fotorama.instances.splice(i.index,1);b1()}bV.Fotorama.cache={};bV.Fotorama.measures={};bV=bV||{};bV.Fotorama=bV.Fotorama||{};bV.Fotorama.jst=bV.Fotorama.jst||{};bV.Fotorama.jst.dots=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__nav__frame fotorama__nav__frame--dot" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__dot"></div>\r\n</div>';return ce};bV.Fotorama.jst.frameCaption=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__caption" aria-hidden="true">\r\n <div class="fotorama__caption__wrap" id="'+((i=(cc.labelledby))==null?"":i)+'">'+((i=(cc.caption))==null?"":i)+"</div>\r\n</div>\r\n";return ce};bV.Fotorama.jst.style=function(cc){var i,ce="",cd=bx.escape;ce+=".fotorama"+((i=(cc.s))==null?"":i)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+((i=(cc.m))==null?"":i)+"px;\r\nheight:"+((i=(cc.h))==null?"":i)+"px}\r\n.fotorama"+((i=(cc.s))==null?"":i)+" .fotorama__thumb-border{\r\nheight:"+((i=(cc.h))==null?"":i)+"px;\r\nborder-width:"+((i=(cc.b))==null?"":i)+"px;\r\nmargin-top:"+((i=(cc.m))==null?"":i)+"px}";return ce};bV.Fotorama.jst.thumb=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__nav__frame fotorama__nav__frame--thumb" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__thumb">\r\n </div>\r\n</div>';return ce}})(window,document,location,typeof jQuery!=="undefined"&&jQuery); From 04da4fc00c19ed6cec3f7fe41fc64276ecce031e Mon Sep 17 00:00:00 2001 From: Miguel Balparda <miguel.balparda@moozo.com.ar> Date: Tue, 2 Oct 2018 12:45:18 -0300 Subject: [PATCH 109/240] Added required fields to templates --- .github/ISSUE_TEMPLATE.md | 9 +++++---- .github/ISSUE_TEMPLATE/bug_report.md | 9 +++++---- .github/ISSUE_TEMPLATE/developer-experience-issue.md | 5 +++-- .github/ISSUE_TEMPLATE/feature_request.md | 5 +++-- .github/PULL_REQUEST_TEMPLATE.md | 9 +++++---- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 12ad4e452b1c7..ce3f059a46ff6 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -5,11 +5,12 @@ - Information on your environment, - Steps to reproduce, - Expected and actual results, + Fields marked with (*) are required. Please don't remove the template. Please also have a look at our guidelines article before adding a new issue https://github.com/magento/magento2/wiki/Issue-reporting-guidelines --> -### Preconditions +### Preconditions (*) <!--- Please provide as detailed information about your environment as possible. For example Magento version, tag, HEAD, PHP & MySQL version, etc.. @@ -17,7 +18,7 @@ 1. 2. -### Steps to reproduce +### Steps to reproduce (*) <!--- It is important to provide a set of clear steps to reproduce this bug. If relevant please include code samples @@ -26,10 +27,10 @@ 2. 3. -### Expected result +### Expected result (*) <!--- Tell us what should happen --> 1. [Screenshots, logs or description] -### Actual result +### Actual result (*) <!--- Tell us what happens instead --> 1. [Screenshots, logs or description] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 17aa66c919eb5..33a6ef02ace11 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,28 +6,29 @@ about: Technical issue with the Magento 2 core components <!--- Please review our guidelines before adding a new issue: https://github.com/magento/magento2/wiki/Issue-reporting-guidelines +Fields marked with (*) are required. Please don't remove the template. --> -### Preconditions +### Preconditions (*) <!--- Provide the exact Magento version (example: 2.2.5) and any important information on the environment where bug is reproducible. --> 1. 2. -### Steps to reproduce +### Steps to reproduce (*) <!--- Important: Provide a set of clear steps to reproduce this bug. We can not provide support without clear instructions on how to reproduce. --> 1. 2. -### Expected result +### Expected result (*) <!--- Tell us what do you expect to happen. --> 1. [Screenshots, logs or description] 2. -### Actual result +### Actual result (*) <!--- Tell us what happened instead. Include error messages and issues. --> 1. [Screenshots, logs or description] 2. diff --git a/.github/ISSUE_TEMPLATE/developer-experience-issue.md b/.github/ISSUE_TEMPLATE/developer-experience-issue.md index a66b0c62ef8e2..423d4818fb31c 100644 --- a/.github/ISSUE_TEMPLATE/developer-experience-issue.md +++ b/.github/ISSUE_TEMPLATE/developer-experience-issue.md @@ -6,12 +6,13 @@ about: Issues related to customization, extensibility, modularity <!--- Please review our guidelines before adding a new issue: https://github.com/magento/magento2/wiki/Issue-reporting-guidelines +Fields marked with (*) are required. Please don't remove the template. --> -### Summary +### Summary (*) <!--- Describe the issue you are experiencing. Include general information, error messages, environments, and so on. --> -### Examples +### Examples (*) <!--- Provide code examples or a patch with a test (recommended) to clearly indicate the problem. --> ### Proposed solution diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index de85da43b70fa..d6c816c2ee2be 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -6,12 +6,13 @@ about: Please consider reporting directly to https://github.com/magento/communit <!--- Important: This repository is intended only for Magento 2 Technical Issues. Enter Feature Requests at https://github.com/magento/community-features. Project stakeholders monitor and manage requests. Feature requests entered using this form may be moved to the forum. +Fields marked with (*) are required. Please don't remove the template. --> -### Description +### Description (*) <!--- Describe the feature you would like to add. --> -### Expected behavior +### Expected behavior (*) <!--- What is the expected behavior of this feature? How is it going to work? --> ### Benefits diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5b0b9d74e453b..f191bd9aaba67 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,12 +3,13 @@ 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, + - Manual testing scenarios + Fields marked with (*) are required. Please don't remove the template. --> <!--- Please provide a general summary of the Pull Request in the Title above --> -### Description +### Description (*) <!--- 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. @@ -22,7 +23,7 @@ 1. magento/magento2#<issue_number>: Issue title 2. ... -### Manual testing scenarios +### Manual testing scenarios (*) <!--- 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. @@ -30,7 +31,7 @@ 1. ... 2. ... -### Contribution checklist +### Contribution checklist (*) - [ ] Pull request has a meaningful description of its purpose - [ ] All commits are accompanied by meaningful commit messages - [ ] All new or changed code is covered with unit/integration tests (if applicable) From f3782493df9d729926040e2458b241aeab3cb09c Mon Sep 17 00:00:00 2001 From: Miguel Balparda <miguel.balparda@moozo.com.ar> Date: Tue, 2 Oct 2018 12:51:08 -0300 Subject: [PATCH 110/240] Update ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index ce3f059a46ff6..2b1720ccaabae 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -5,7 +5,7 @@ - Information on your environment, - Steps to reproduce, - Expected and actual results, - Fields marked with (*) are required. Please don't remove the template. + Fields marked with (*) are required. Please don't remove the template. Please also have a look at our guidelines article before adding a new issue https://github.com/magento/magento2/wiki/Issue-reporting-guidelines --> From 4815246dbba5c2c082c7ec35d517c7b295ee2e0d Mon Sep 17 00:00:00 2001 From: Miguel Balparda <miguel.balparda@moozo.com.ar> Date: Tue, 2 Oct 2018 12:52:02 -0300 Subject: [PATCH 111/240] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index d6c816c2ee2be..f64185773cab4 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -5,8 +5,7 @@ about: Please consider reporting directly to https://github.com/magento/communit --- <!--- -Important: This repository is intended only for Magento 2 Technical Issues. Enter Feature Requests at https://github.com/magento/community-features. Project stakeholders monitor and manage requests. Feature requests entered using this form may be moved to the forum. -Fields marked with (*) are required. Please don't remove the template. +Important: This repository is intended only for Magento 2 Technical Issues. Enter Feature Requests at https://github.com/magento/community-features. Project stakeholders monitor and manage requests. Feature requests entered using this form may be moved to the forum. Fields marked with (*) are required. Please don't remove the template. --> ### Description (*) From a10f0749d9be91695cf3f3558f60744a23d9e12c Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Tue, 16 Oct 2018 14:21:53 +0530 Subject: [PATCH 112/240] [Backport] Fix SKU limit in import new products --- .../Magento/CatalogImportExport/Model/Import/Product.php | 5 ----- .../CatalogImportExport/Model/Import/Product/Validator.php | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 7c73f7f7dba2e..19e33ee267da7 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -159,11 +159,6 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ const URL_KEY = 'url_key'; - /** - * SKU max length - */ - const DB_MAX_SKU_LENGTH = 65; - /** * Attribute cache * diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 818f28aa5f27a..40cf70b8f4e73 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -7,6 +7,7 @@ use Magento\CatalogImportExport\Model\Import\Product; use Magento\Framework\Validator\AbstractValidator; +use Magento\Catalog\Model\Product\Attribute\Backend\Sku; /** * Class Validator @@ -70,7 +71,7 @@ protected function textValidation($attrCode, $type) if ($type == 'text') { $valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH; } else if ($attrCode == Product::COL_SKU) { - $valid = $this->string->strlen($val) < Product::DB_MAX_SKU_LENGTH; + $valid = $this->string->strlen($val) < SKU::SKU_MAX_LENGTH; } else { $valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH; } From 26e4decae625b1df1b67cb5c29f2535d837fc214 Mon Sep 17 00:00:00 2001 From: Janak <janak@krishtechnolabs.com> Date: Tue, 16 Oct 2018 14:26:48 +0530 Subject: [PATCH 113/240] Fix customer unsubscribed issue --- app/code/Magento/Newsletter/Model/Subscriber.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index b916f20dbe770..5e0d5448f11ca 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -594,6 +594,8 @@ protected function _updateCustomerSubscription($customerId, $subscribe) } elseif (($this->getStatus() == self::STATUS_UNCONFIRMED) && ($customerData->getConfirmation() === null)) { $status = self::STATUS_SUBSCRIBED; $sendInformationEmail = true; + } elseif (($this->getStatus() == self::STATUS_NOT_ACTIVE) && ($customerData->getConfirmation() === null)) { + $status = self::STATUS_NOT_ACTIVE; } else { $status = self::STATUS_UNSUBSCRIBED; } From ddb7fb6c2045a164a88da9cecbf56a614386b2ac Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Tue, 16 Oct 2018 14:02:08 +0300 Subject: [PATCH 114/240] MAGETWO-90929: Billing Address in Braintree PayPal response is absent --- .../Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php | 1 + 1 file changed, 1 insertion(+) 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 c1ecdbd555e16..7475d81a56142 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 @@ -293,6 +293,7 @@ private function getQuoteMock(): MockObject [ 'getIsVirtual', 'getPayment', + 'getExtensionAttributes', 'setMayEditShippingAddress', 'setMayEditShippingMethod', 'collectTotals', From 382e550fe3161fb667f4673d53066d4918aedb74 Mon Sep 17 00:00:00 2001 From: mahesh <mahesh721@webkul.com> Date: Tue, 16 Oct 2018 18:52:20 +0530 Subject: [PATCH 115/240] issue #18617 in v2.2.6 --- app/code/Magento/Weee/Model/Sales/Pdf/Weee.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/code/Magento/Weee/Model/Sales/Pdf/Weee.php b/app/code/Magento/Weee/Model/Sales/Pdf/Weee.php index fa71e81281763..62019426feb37 100644 --- a/app/code/Magento/Weee/Model/Sales/Pdf/Weee.php +++ b/app/code/Magento/Weee/Model/Sales/Pdf/Weee.php @@ -70,4 +70,17 @@ public function getTotalsForDisplay() return $totals; } + + /** + * Check if we can display Weee total information in PDF + * + * @return bool + */ + public function canDisplay() + { + $items = $this->getSource()->getAllItems(); + $store = $this->getSource()->getStore(); + $amount = $this->_weeeData->getTotalAmounts($items, $store); + return $this->getDisplayZero() === 'true' || $amount != 0; + } } From a10c64e6e05981bb61e015f94d71d72c19c82594 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Tue, 16 Oct 2018 07:45:29 -0700 Subject: [PATCH 116/240] MAGETWO-95173: Sorting by price for Configurable with Catalog Rule applied --- .../AdminProductAttributeActionGroup.xml | 12 ++++----- .../StorefrontCategoryActionGroup.xml | 1 - .../AdminCreateProductAttributeSection.xml | 2 +- .../AdminProductAttributeGridSection.xml | 2 +- .../Section/StorefrontCategoryMainSection.xml | 2 +- .../CatalogPriceRuleActionGroup.xml | 8 +++--- .../Test/Mftf/Data/CatalogRuleData.xml | 2 +- ...AdminCatalogPriceRuleConditionsSection.xml | 8 +++--- .../AdminCatalogPriceRuleGridSection.xml | 2 +- .../Section/AdminCatalogPriceRuleSection.xml | 1 - ...ConfigurableWithCatalogRuleAppliedTest.xml | 26 +++++++++---------- 11 files changed, 29 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 8e5cc3141b0d4..4cf014400d403 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -9,22 +9,20 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="navigateToEditProductAttribute"> <arguments> - <argument name="ProductAttribute" type="string"/> + <argument name="attributeLabel" type="string"/> </arguments> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.gridFilterFrontEndLabel}}" userInput="{{ProductAttribute}}" stepKey="navigateToAttributeEditPage1" /> + <fillField selector="{{AdminProductAttributeGridSection.gridFilterFrontEndLabel}}" userInput="{{attributeLabel}}" stepKey="navigateToAttributeEditPage1" /> <click selector="{{AdminProductAttributeGridSection.search}}" stepKey="navigateToAttributeEditPage2" /> - <waitForPageLoad stepKey="waitForPageLoad" /> <click selector="{{AdminProductAttributeGridSection.firstRow}}" stepKey="navigateToAttributeEditPage3" /> - <waitForPageLoad stepKey="waitForPageLoad2" /> </actionGroup> <actionGroup name="changeUseForPromoRuleConditionsProductAttribute"> <arguments> - <argument name="option" type="string"/> + <argument name="useForRule" type="string" defaultValue="No"/> </arguments> - <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStoreFrontPropertiesTab"/> + <click selector="{{StorefrontPropertiesSection.storefrontPropertiesTab}}" stepKey="clickStoreFrontPropertiesTab"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="{{option}}" stepKey="changeOption"/> + <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="{{useForRule}}" stepKey="changeOption"/> <click selector="{{AttributePropertiesSection.save}}" stepKey="saveAttribute"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the product attribute." stepKey="successMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index 806f8122cf687..8f0df2fc899a9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -46,6 +46,5 @@ </arguments> <!-- Go to storefront category page --> <amOnPage url="{{StorefrontCategoryPage.url(category)}}?product_list_mode={{mode}}&product_list_order={{sortBy}}&product_list_dir={{sort}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index c151f2b67a8b2..7087959259908 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -13,7 +13,7 @@ <element name="Save" type="button" selector="#save"/> </section> <section name="StorefrontPropertiesSection"> - <element name="StoreFrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> + <element name="storefrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index bb944270a10a9..1e7bca367c063 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -14,7 +14,7 @@ <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]"/> + <element name="firstRow" type="button" selector="//*[@id='attributeGrid_table']/tbody/tr[1]" timeout="30"/> <element name="attributeCodeFilterInput" type="input" selector=".admin__data-grid-filters input[name='attribute_code']"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index 7cdb1e82ed54e..1f1a4ce9133e7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -21,6 +21,6 @@ <element name="productsList" type="text" selector="//ol[@class='products list items product-items']"/> <element name="categoryPageProductImagePlaceholderSmall" type="text" selector=".products-grid img[src*='placeholder/small_image.jpg']"/> <element name="categoryPageProductImage" type="text" selector=".products-grid img[src*='/{{var1}}']" parameterized="true"/> - <element name="lineProductName" type="text" selector=".products.list.items.product-items li:nth-of-type({{line}}) .product-item-link" timeout="30" parameterized="true"/> + <element name="categoryPageProductName" type="text" selector=".products.list.items.product-items li:nth-of-type({{line}}) .product-item-link" timeout="30" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index d6bd37910cd83..72e082392059e 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -59,14 +59,12 @@ <!--Add Catalog Rule Condition With product SKU--> <actionGroup name="newCatalogPriceRuleByUIWithConditionIsSKU" extends="CreateCatalogPriceRule"> <arguments> - <argument name="productSku"/> + <argument name="productSku" type="string"/> </arguments> <click selector="{{AdminCatalogPriceRuleSection.conditionsTab}}" after="discardSubsequentRules" stepKey="openConditionsTab"/> - <waitForPageLoad after="openConditionsTab" stepKey="waitForConditionTabOpened"/> - <click selector="{{AdminCatalogPriceRuleConditionsSection.newCondition}}" after="waitForConditionTabOpened" stepKey="addNewCondition"/> + <click selector="{{AdminCatalogPriceRuleConditionsSection.newCondition}}" after="openConditionsTab" stepKey="addNewCondition"/> <selectOption selector="{{AdminCatalogPriceRuleConditionsSection.conditionSelect('1')}}" userInput="Magento\CatalogRule\Model\Rule\Condition\Product|sku" after="addNewCondition" stepKey="selectTypeCondition"/> - <waitForPageLoad after="selectTypeCondition" stepKey="waitForConditionChosed"/> - <click selector="{{AdminCatalogPriceRuleConditionsSection.targetEllipsis('1')}}" after="waitForConditionChosed" stepKey="clickEllipsis"/> + <click selector="{{AdminCatalogPriceRuleConditionsSection.targetEllipsis('1')}}" after="selectTypeCondition" stepKey="clickEllipsis"/> <fillField selector="{{AdminCatalogPriceRuleConditionsSection.targetInput('1', '1')}}" userInput="{{productSku}}" after="clickEllipsis" stepKey="fillProductSku"/> <click selector="{{AdminCatalogPriceRuleConditionsSection.applyButton('1', '1')}}" after="fillProductSku" stepKey="clickApply"/> </actionGroup> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml index 7e2dbcd53f922..534a2c8cc6de2 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml @@ -21,7 +21,7 @@ <data key="simple_action">by_percent</data> <data key="discount_amount">10</data> </entity> - <entity name="CatalogRuleByPercentWith96Amount" type="catalogRule"> + <entity name="CatalogRule96PercentDiscount" type="catalogRule"> <data key="name" unique="suffix">CatalogPriceRule</data> <data key="description">Catalog Price Rule Description</data> <data key="is_active">1</data> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml index e7450663464c2..f522fdaa03f89 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleConditionsSection.xml @@ -9,10 +9,10 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogPriceRuleConditionsSection"> - <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child"/> - <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true"/> - <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> + <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child" timeout="30"/> + <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true" timeout="30"/> + <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true" timeout="30"/> <element name="targetInput" type="input" selector="input#conditions__{{var1}}--{{var2}}__value" parameterized="true"/> - <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true"/> + <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml index ad90a749bff45..5224147c51804 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml @@ -10,6 +10,6 @@ <section name="AdminCatalogPriceRuleGridSection"> <element name="filterByRuleName" type="input" selector="#promo_catalog_grid_filter_name"/> <element name="attribute" type="text" selector="//td[contains(text(), '{{arg}}')]" parameterized="true"/> - <element name="applyRulesButton" type="button" selector="#apply_rules"/> + <element name="applyRulesButton" type="button" selector="#apply_rules" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml index 71c64987eb280..bbf5ed6053cd2 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleSection.xml @@ -11,7 +11,6 @@ <section name="AdminCatalogPriceRuleSection"> <element name="saveAndApply" type="button" selector="#save_and_apply" timeout="30"/> <element name="saveAndContinue" type="button" selector="#save_and_continue" timeout="30"/> - <element name="save" type="button" selector="#save" timeout="30"/> <element name="ruleName" type="input" selector="[name='name']"/> <element name="description" type="textarea" selector="[name='description']"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml index 8e868300efbf2..dee6f9284a49f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml @@ -89,10 +89,10 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--SKU Product Attribute is enabled for Promo Rule Conditions--> <actionGroup ref="navigateToEditProductAttribute" stepKey="navigateToSkuProductAttribute"> - <argument name="ProductAttribute" value="sku"/> + <argument name="attributeLabel" value="sku"/> </actionGroup> <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToYes"> - <argument name="option" value="Yes"/> + <argument name="useForRule" value="Yes"/> </actionGroup> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> @@ -110,11 +110,9 @@ <!--SKU Product Attribute is disable for Promo Rule Conditions--> <actionGroup ref="navigateToEditProductAttribute" stepKey="navigateToSkuProductAttribute"> - <argument name="ProductAttribute" value="sku"/> - </actionGroup> - <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToNo"> - <argument name="option" value="No"/> + <argument name="attributeLabel" value="sku"/> </actionGroup> + <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToNo"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> @@ -125,13 +123,13 @@ <argument name="sortBy" value="price"/> <argument name="sort" value="desc"/> </actionGroup> - <see selector="{{StorefrontCategoryMainSection.lineProductName('1')}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProduct"/> - <see selector="{{StorefrontCategoryMainSection.lineProductName('2')}}" userInput="$$createSimpleProduct2.name$$" stepKey="seeSimpleProductTwo"/> - <see selector="{{StorefrontCategoryMainSection.lineProductName('3')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct"/> + <see selector="{{StorefrontCategoryMainSection.categoryPageProductName('1')}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProduct"/> + <see selector="{{StorefrontCategoryMainSection.categoryPageProductName('2')}}" userInput="$$createSimpleProduct2.name$$" stepKey="seeSimpleProductTwo"/> + <see selector="{{StorefrontCategoryMainSection.categoryPageProductName('3')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct"/> <!--Create and apply catalog price rule--> <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsSKU" stepKey="createCatalogPriceRule"> - <argument name="catalogRule" value="CatalogRuleByPercentWith96Amount" /> + <argument name="catalogRule" value="CatalogRule96PercentDiscount" /> <argument name="productSku" value="$$createConfigChildProduct3.sku$$" /> </actionGroup> <click selector="{{AdminCatalogPriceRuleGridSection.applyRulesButton}}" stepKey="clickApplyRules"/> @@ -146,13 +144,13 @@ <argument name="sortBy" value="price"/> <argument name="sort" value="desc"/> </actionGroup> - <see selector="{{StorefrontCategoryMainSection.lineProductName('1')}}" userInput="$$createSimpleProduct2.name$$" stepKey="seeSimpleProductTwo2"/> - <see selector="{{StorefrontCategoryMainSection.lineProductName('2')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct2"/> - <see selector="{{StorefrontCategoryMainSection.lineProductName('3')}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProduct2"/> + <see selector="{{StorefrontCategoryMainSection.categoryPageProductName('1')}}" userInput="$$createSimpleProduct2.name$$" stepKey="seeSimpleProductTwo2"/> + <see selector="{{StorefrontCategoryMainSection.categoryPageProductName('2')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct2"/> + <see selector="{{StorefrontCategoryMainSection.categoryPageProductName('3')}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProduct2"/> <!-- Delete the rule --> <actionGroup ref="RemoveCatalogPriceRule" stepKey="deletePriceRule"> - <argument name="ruleName" value="CatalogRuleByPercentWith96Amount.name" /> + <argument name="ruleName" value="CatalogRule96PercentDiscount.name" /> </actionGroup> </test> </tests> From f7d8edd9cdc0dce6f51e030277a15ab33546b8df Mon Sep 17 00:00:00 2001 From: BezV8 <berwyn@v8media.co.uk> Date: Thu, 11 Oct 2018 09:12:58 +0100 Subject: [PATCH 117/240] Fix typo on product details causing Validation error. Fixes a typo on the aria-labeleledby attribute on the product page causing a W3C Validation error. --- .../Catalog/view/frontend/templates/product/view/details.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml index b1af46b80552d..038bea86e7d4e 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml @@ -21,7 +21,7 @@ $label = $block->getChildData($alias, 'title'); ?> <div class="data item title" - aria-labeledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title" + aria-labelledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title" data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>"> <a class="data switch" tabindex="-1" From af1e20e915690c02de352bccc7134d6211059ab9 Mon Sep 17 00:00:00 2001 From: BezV8 <berwyn@v8media.co.uk> Date: Thu, 11 Oct 2018 15:18:14 +0100 Subject: [PATCH 118/240] Fix validation errors on cart page. --- .../Magento/Checkout/view/frontend/templates/cart/form.phtml | 2 +- .../Checkout/view/frontend/templates/cart/item/default.phtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml index 71b1392d5391f..e762a89dc1580 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml @@ -26,7 +26,7 @@ class="cart items data table" data-mage-init='{"shoppingCart":{"emptyCartButton": "action.clear", "updateCartActionContainer": "#update_cart_action_container"}}'> - <caption role="heading" aria-level="2" class="table-caption"><?= /* @escapeNotVerified */ __('Shopping Cart Items') ?></caption> + <caption class="table-caption"><?= /* @escapeNotVerified */ __('Shopping Cart Items') ?></caption> <thead> <tr> <th class="col item" scope="col"><span><?= /* @escapeNotVerified */ __('Item') ?></span></th> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml index 0567c61f0db60..c96df9cdd3195 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml @@ -111,7 +111,7 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima </td> </tr> <tr class="item-actions"> - <td colspan="100"> + <td colspan="4"> <div class="actions-toolbar"> <?= /* @escapeNotVerified */ $block->getActions($_item) ?> </div> From e6dedb70cdd9c218a4c45709fed42129e369d886 Mon Sep 17 00:00:00 2001 From: peterjaap <peterjaap@elgentos.nl> Date: Thu, 11 Oct 2018 13:38:29 +0200 Subject: [PATCH 119/240] Change expected Exception from \Magento\NewRelicReporting\Model\Exception to \Exception to fix docblock --- app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php index ec21e06976b8b..9882a1ce9b0b8 100644 --- a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php +++ b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php @@ -31,7 +31,7 @@ public function addCustomParameter($param, $value) /** * Wrapper for 'newrelic_notice_error' function * - * @param Exception $exception + * @param \Exception $exception * @return void */ public function reportError($exception) From c2a2a100e1df8eabd3e44fc1e9199cef43e78c5a Mon Sep 17 00:00:00 2001 From: peterjaap <peterjaap@elgentos.nl> Date: Thu, 11 Oct 2018 13:40:38 +0200 Subject: [PATCH 120/240] Create empty modelData array to avoid undefined var error --- .../Magento/NewRelicReporting/Model/Cron/ReportModulesInfo.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/NewRelicReporting/Model/Cron/ReportModulesInfo.php b/app/code/Magento/NewRelicReporting/Model/Cron/ReportModulesInfo.php index 9cdc90bc46b2a..78c485c5bb6f5 100644 --- a/app/code/Magento/NewRelicReporting/Model/Cron/ReportModulesInfo.php +++ b/app/code/Magento/NewRelicReporting/Model/Cron/ReportModulesInfo.php @@ -64,6 +64,7 @@ public function report() $moduleData = $this->collect->getModuleData(); if (count($moduleData['changes']) > 0) { foreach ($moduleData['changes'] as $change) { + $modelData = []; switch ($change['type']) { case Config::ENABLED: $modelData = [ From c77ffb85e8d0cfc0556b14e543c651e2bdb0e448 Mon Sep 17 00:00:00 2001 From: peterjaap <peterjaap@elgentos.nl> Date: Thu, 11 Oct 2018 13:50:39 +0200 Subject: [PATCH 121/240] Removed unused class import for Magento\Framework\App\State --- .../Developer/Console/Command/SourceThemeDeployCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Developer/Console/Command/SourceThemeDeployCommand.php b/app/code/Magento/Developer/Console/Command/SourceThemeDeployCommand.php index 25519e5c83054..a2db0f43061ea 100644 --- a/app/code/Magento/Developer/Console/Command/SourceThemeDeployCommand.php +++ b/app/code/Magento/Developer/Console/Command/SourceThemeDeployCommand.php @@ -5,7 +5,6 @@ */ namespace Magento\Developer\Console\Command; -use Magento\Framework\App\State; use Magento\Framework\Validator\Locale; use Magento\Framework\View\Asset\Repository; use Symfony\Component\Console\Command\Command; From 53a06a9ba12c0f4be0b28fdf6f9cf45540cebae6 Mon Sep 17 00:00:00 2001 From: Roman Strilenko <strelenko.roman@gmail.com> Date: Sun, 7 Oct 2018 12:12:04 +0200 Subject: [PATCH 122/240] MAGENTO-18131: Fixed EAV attributes values query --- .../Magento/Eav/Model/Entity/Collection/AbstractCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php index d61166e5c71a9..b19965e9f8c32 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php @@ -1232,7 +1232,7 @@ protected function _getLoadAttributesSelect($table, $attributeIds = []) if ($entity->getEntityTable() == \Magento\Eav\Model\Entity::DEFAULT_ENTITY_TABLE && $entity->getTypeId()) { $select->where( - 'entity_type_id =?', + 't_d.entity_type_id =?', $entity->getTypeId() ); } From ef426a722fd4bb80470329f83d5a08f7a0424064 Mon Sep 17 00:00:00 2001 From: Logan Stellway <loganstellway@users.noreply.github.com> Date: Thu, 4 Oct 2018 15:36:06 -0700 Subject: [PATCH 123/240] Fix for #12969 - server port detection for errors Updates `getHostUrl()` method to reference `HTTP_HOST` rather than `SERVER_PORT`. --- pub/errors/processor.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pub/errors/processor.php b/pub/errors/processor.php index 7240707f642c2..615a0c0f96ec3 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -268,10 +268,11 @@ public function getHostUrl() $isSecure = (!empty($_SERVER['HTTPS'])) && ($_SERVER['HTTPS'] != 'off'); $url = ($isSecure ? 'https://' : 'http://') . $host; - if (!empty($_SERVER['SERVER_PORT']) && !in_array($_SERVER['SERVER_PORT'], [80, 443]) + $port = explode(':', $host); + if (isset($port[1]) && !in_array($port[1], [80, 443]) && !preg_match('/.*?\:[0-9]+$/', $url) ) { - $url .= ':' . $_SERVER['SERVER_PORT']; + $url .= ':' . $port[1]; } return $url; } From 774a8a25688bd93cedc5b44cc79c9251a221d25b Mon Sep 17 00:00:00 2001 From: Logan Stellway <loganstellway@users.noreply.github.com> Date: Mon, 8 Oct 2018 09:10:59 -0700 Subject: [PATCH 124/240] Updates docblock comment for _renderPage() method --- pub/errors/processor.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pub/errors/processor.php b/pub/errors/processor.php index 615a0c0f96ec3..8b37798f468b8 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -380,6 +380,8 @@ protected function _loadXml($xmlFile) } /** + * Render page + * * @param string $template * @return string */ From 763be496a1c19e0008524a0eab8c1ae7554a2609 Mon Sep 17 00:00:00 2001 From: Logan Stellway <loganstellway@users.noreply.github.com> Date: Tue, 9 Oct 2018 07:52:02 -0700 Subject: [PATCH 125/240] Removes white space from end of line 384 --- pub/errors/processor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pub/errors/processor.php b/pub/errors/processor.php index 8b37798f468b8..e64956dce17c2 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -381,7 +381,7 @@ protected function _loadXml($xmlFile) /** * Render page - * + * * @param string $template * @return string */ From 5f98483500400ed8dc5d8d2259a648069dbf0fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Brada?= <jir.brada@gmail.com> Date: Thu, 20 Sep 2018 13:31:52 +0200 Subject: [PATCH 126/240] Added $fieldId parameter into Config::getFieldPath method for fix of "clone_field" system config feature. --- app/code/Magento/Config/Model/Config.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index 11ad1ef2a43e9..424c47b7ef9aa 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -227,13 +227,14 @@ private function getField(string $sectionId, string $groupId, string $fieldId): * Get field path * * @param Field $field + * @param string $fieldId Need for support of clone_field feature * @param array &$oldConfig Need for compatibility with _processGroup() * @param array &$extraOldGroups Need for compatibility with _processGroup() * @return string */ - private function getFieldPath(Field $field, array &$oldConfig, array &$extraOldGroups): string + private function getFieldPath(Field $field, string $fieldId, array &$oldConfig, array &$extraOldGroups): string { - $path = $field->getGroupPath() . '/' . $field->getId(); + $path = $field->getGroupPath() . '/' . $fieldId; /** * Look for custom defined field path @@ -293,7 +294,7 @@ private function getChangedPaths( if (isset($groupData['fields'])) { foreach ($groupData['fields'] as $fieldId => $fieldData) { $field = $this->getField($sectionId, $groupId, $fieldId); - $path = $this->getFieldPath($field, $oldConfig, $extraOldGroups); + $path = $this->getFieldPath($field, $fieldId, $oldConfig, $extraOldGroups); if ($this->isValueChanged($oldConfig, $path, $fieldData)) { $changedPaths[] = $path; } @@ -374,7 +375,7 @@ protected function _processGroup( $backendModel->addData($data); $this->_checkSingleStoreMode($field, $backendModel); - $path = $this->getFieldPath($field, $extraOldGroups, $oldConfig); + $path = $this->getFieldPath($field, $fieldId, $extraOldGroups, $oldConfig); $backendModel->setPath($path)->setValue($fieldData['value']); $inherit = !empty($fieldData['inherit']); @@ -580,4 +581,4 @@ public function getConfigDataValue($path, &$inherit = null, $configData = null) return $data; } -} +} \ No newline at end of file From d81bcc3362ef62d81a2b82981953068b78f85593 Mon Sep 17 00:00:00 2001 From: Milan Osztromok <tufa@polisys.hu> Date: Wed, 3 Oct 2018 17:26:29 +0200 Subject: [PATCH 127/240] move hardcoded MIME types from class private to DI configuration --- .../Magento/Catalog/Model/ImageUploader.php | 23 +++++++++++-------- app/code/Magento/Catalog/etc/di.xml | 6 +++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index 7599e46f76e30..1c980ba91fd50 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -69,14 +69,9 @@ class ImageUploader /** * List of allowed image mime types * - * @var array + * @var string[] */ - private $allowedMimeTypes = [ - 'image/jpg', - 'image/jpeg', - 'image/gif', - 'image/png' - ]; + protected $allowedMimeTypes; /** * ImageUploader constructor @@ -98,7 +93,8 @@ public function __construct( \Psr\Log\LoggerInterface $logger, $baseTmpPath, $basePath, - $allowedExtensions + $allowedExtensions, + $allowedMimeTypes ) { $this->coreFileStorageDatabase = $coreFileStorageDatabase; $this->mediaDirectory = $filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA); @@ -108,6 +104,7 @@ public function __construct( $this->baseTmpPath = $baseTmpPath; $this->basePath = $basePath; $this->allowedExtensions = $allowedExtensions; + $this->allowedMimeTypes = $allowedMimeTypes; } /** @@ -176,6 +173,14 @@ public function getAllowedExtensions() return $this->allowedExtensions; } + /** + * @return string[] + */ + public function getAllowedMimeTypes() + { + return $this->allowedMimeTypes; + } + /** * Retrieve path * @@ -242,7 +247,7 @@ public function saveFileToTmpDir($fileId) $uploader = $this->uploaderFactory->create(['fileId' => $fileId]); $uploader->setAllowedExtensions($this->getAllowedExtensions()); $uploader->setAllowRenameFiles(true); - if (!$uploader->checkMimeType($this->allowedMimeTypes)) { + if (!$uploader->checkMimeType($this->getAllowedMimeTypes())) { throw new \Magento\Framework\Exception\LocalizedException(__('File validation failed.')); } $result = $uploader->save($this->mediaDirectory->getAbsolutePath($baseTmpPath)); diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 3eecfb3c7453a..4d3a8a452ead9 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -211,6 +211,12 @@ <item name="gif" xsi:type="string">gif</item> <item name="png" xsi:type="string">png</item> </argument> + <argument name="allowedMimeTypes" xsi:type="array"> + <item name="jpg" xsi:type="string">image/jpg</item> + <item name="jpeg" xsi:type="string">image/jpeg</item> + <item name="gif" xsi:type="string">image/gif</item> + <item name="png" xsi:type="string">image/png</item> + </argument> </arguments> </virtualType> <type name="Magento\Catalog\Controller\Adminhtml\Category\Image\Upload"> From 13158f330ca74954ea7b7a5b09f831b5fbac6f35 Mon Sep 17 00:00:00 2001 From: Milan Osztromok <tufa@polisys.hu> Date: Wed, 3 Oct 2018 17:35:56 +0200 Subject: [PATCH 128/240] add missing phpdoc comment --- app/code/Magento/Catalog/Model/ImageUploader.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index 1c980ba91fd50..fe0bb23f574b9 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -84,6 +84,7 @@ class ImageUploader * @param string $baseTmpPath * @param string $basePath * @param string[] $allowedExtensions + * @param string[] $allowedMimeTypes */ public function __construct( \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase, From ef9c20367b95acef1b0bb653d041a6dae3041b1c Mon Sep 17 00:00:00 2001 From: Milan Osztromok <tufa@polisys.hu> Date: Wed, 3 Oct 2018 21:20:07 +0200 Subject: [PATCH 129/240] fix related test --- .../Catalog/Test/Unit/Model/ImageUploaderTest.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php index bd8871c21de9e..a2ef05b4492d3 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php @@ -69,10 +69,17 @@ class ImageUploaderTest extends \PHPUnit\Framework\TestCase /** * Allowed extensions * - * @var string + * @var array */ private $allowedExtensions; + /** + * Allowed mime types + * + * @var array + */ + private $allowedMimeTypes; + protected function setUp() { $this->coreFileStorageDatabaseMock = $this->createMock( @@ -97,6 +104,7 @@ protected function setUp() $this->baseTmpPath = 'base/tmp/'; $this->basePath = 'base/real/'; $this->allowedExtensions = ['.jpg']; + $this->allowedMimeTypes = ['image/jpg']; $this->imageUploader = new \Magento\Catalog\Model\ImageUploader( @@ -107,7 +115,8 @@ protected function setUp() $this->loggerMock, $this->baseTmpPath, $this->basePath, - $this->allowedExtensions + $this->allowedExtensions, + $this->allowedMimeTypes ); } From a6b0d1c1e52494a3d9d91910bc9d2b73ab800c03 Mon Sep 17 00:00:00 2001 From: Milan Osztromok <tufa@polisys.hu> Date: Thu, 4 Oct 2018 00:17:35 +0200 Subject: [PATCH 130/240] fix integration test --- .../testsuite/Magento/Catalog/Model/ImageUploaderTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php index 723ff963e2bfc..ea151c2f68750 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ImageUploaderTest.php @@ -46,7 +46,8 @@ protected function setUp() [ 'baseTmpPath' => $this->mediaDirectory->getRelativePath('tmp'), 'basePath' => __DIR__, - 'allowedExtensions' => ['jpg', 'jpeg', 'gif', 'png'] + 'allowedExtensions' => ['jpg', 'jpeg', 'gif', 'png'], + 'allowedMimeTypes' => ['image/jpg', 'image/jpeg', 'image/gif', 'image/png'] ] ); } From 05504c5ac74c7d86c8ea05d0fe5ce9b42951a87a Mon Sep 17 00:00:00 2001 From: Milan Osztromok <tufa@polisys.hu> Date: Thu, 4 Oct 2018 09:45:15 +0200 Subject: [PATCH 131/240] fix phpcbf errors --- app/code/Magento/Catalog/Model/ImageUploader.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index fe0bb23f574b9..429149d0f63a3 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -165,7 +165,7 @@ public function getBasePath() } /** - * Retrieve base path + * Retrieve allowed extensions * * @return string[] */ @@ -175,6 +175,8 @@ public function getAllowedExtensions() } /** + * Retrieve allowed mime types + * * @return string[] */ public function getAllowedMimeTypes() From a4099f82549cad0dd3139baa51ad8fa693de3862 Mon Sep 17 00:00:00 2001 From: Milan Osztromok <tufa@polisys.hu> Date: Thu, 4 Oct 2018 09:59:28 +0200 Subject: [PATCH 132/240] solve @orlangur requests. --- app/code/Magento/Catalog/Model/ImageUploader.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index 429149d0f63a3..1eab75e1e8039 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -71,7 +71,7 @@ class ImageUploader * * @var string[] */ - protected $allowedMimeTypes; + private $allowedMimeTypes; /** * ImageUploader constructor @@ -174,16 +174,6 @@ public function getAllowedExtensions() return $this->allowedExtensions; } - /** - * Retrieve allowed mime types - * - * @return string[] - */ - public function getAllowedMimeTypes() - { - return $this->allowedMimeTypes; - } - /** * Retrieve path * @@ -250,7 +240,7 @@ public function saveFileToTmpDir($fileId) $uploader = $this->uploaderFactory->create(['fileId' => $fileId]); $uploader->setAllowedExtensions($this->getAllowedExtensions()); $uploader->setAllowRenameFiles(true); - if (!$uploader->checkMimeType($this->getAllowedMimeTypes())) { + if (!$uploader->checkMimeType($this->allowedMimeTypes)) { throw new \Magento\Framework\Exception\LocalizedException(__('File validation failed.')); } $result = $uploader->save($this->mediaDirectory->getAbsolutePath($baseTmpPath)); From da491743f1dceffac055bd95f58ada5a9e4e5973 Mon Sep 17 00:00:00 2001 From: Milan Osztromok <tufa@polisys.hu> Date: Thu, 4 Oct 2018 10:17:00 +0200 Subject: [PATCH 133/240] fix failed unit test --- app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php index a2ef05b4492d3..4435446f77bfb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ImageUploaderTest.php @@ -104,7 +104,7 @@ protected function setUp() $this->baseTmpPath = 'base/tmp/'; $this->basePath = 'base/real/'; $this->allowedExtensions = ['.jpg']; - $this->allowedMimeTypes = ['image/jpg']; + $this->allowedMimeTypes = ['image/jpg', 'image/jpeg', 'image/gif', 'image/png']; $this->imageUploader = new \Magento\Catalog\Model\ImageUploader( From d653d75382b2e71fff60ce2041a838ba3ba8a52c Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 11 Oct 2018 12:48:00 +0300 Subject: [PATCH 134/240] Make constructor backward compatible --- app/code/Magento/Catalog/Model/ImageUploader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index 1eab75e1e8039..7f047327460ca 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -95,7 +95,7 @@ public function __construct( $baseTmpPath, $basePath, $allowedExtensions, - $allowedMimeTypes + $allowedMimeTypes = [] ) { $this->coreFileStorageDatabase = $coreFileStorageDatabase; $this->mediaDirectory = $filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA); From c6789a8a9bd2e102f548a0e2f3fc831a221c3c1e Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 17 Oct 2018 09:33:53 +0300 Subject: [PATCH 135/240] MAGETWO-86098: Cart Price Rule for configurable products --- .../Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml index ea8c6ecdeb889..a7c5fb484b2bf 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml @@ -17,7 +17,6 @@ <element name="estimateShippingAndTax" type="text" selector="#block-shipping-heading" timeout="5"/> <element name="flatRateShippingMethod" type="radio" selector="#s_method_flatrate_flatrate" timeout="30"/> <element name="itemDiscount" type="text" selector="tr[class='totals'] td.amount > span"/> - <element name="subtotal" type="text" selector="tr.totals.sub td.amount > span"/> <element name="countryParameterized" type="select" selector="select[name='country_id'] > option:nth-child({{var}})" timeout="10" parameterized="true"/> <element name="shippingHeading" type="button" selector="#block-shipping-heading"/> <element name="blockSummary" type="button" selector="#block-summary"/> From b5fcda63a15d10a41e269612ede1e62b40fae608 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Wed, 17 Oct 2018 00:15:58 -0700 Subject: [PATCH 136/240] MAGETWO-95173: Sorting by price for Configurable with Catalog Rule applied --- .../Mftf/ActionGroup/AdminProductAttributeActionGroup.xml | 6 ++---- .../Mftf/Section/AdminCreateProductAttributeSection.xml | 2 +- .../Test/Mftf/Section/AttributePropertiesSection.xml | 2 +- ...ngByPriceForConfigurableWithCatalogRuleAppliedTest.xml | 8 ++++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 4cf014400d403..341360ce87173 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -18,13 +18,11 @@ </actionGroup> <actionGroup name="changeUseForPromoRuleConditionsProductAttribute"> <arguments> - <argument name="useForRule" type="string" defaultValue="No"/> + <argument name="useForPromoRule" type="string" defaultValue="Yes"/> </arguments> <click selector="{{StorefrontPropertiesSection.storefrontPropertiesTab}}" stepKey="clickStoreFrontPropertiesTab"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="{{useForRule}}" stepKey="changeOption"/> + <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="{{useForPromoRule}}" stepKey="changeOption"/> <click selector="{{AttributePropertiesSection.save}}" stepKey="saveAttribute"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the product attribute." stepKey="successMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 7087959259908..e241370220921 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -13,7 +13,7 @@ <element name="Save" type="button" selector="#save"/> </section> <section name="StorefrontPropertiesSection"> - <element name="storefrontPropertiesTab" selector="#product_attribute_tabs_front" type="button"/> + <element name="storefrontPropertiesTab" selector="#product_attribute_tabs_front" type="button" timeout="30"/> <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AttributePropertiesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AttributePropertiesSection.xml index 55a1892389416..fbf07429b8408 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AttributePropertiesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AttributePropertiesSection.xml @@ -14,7 +14,7 @@ <element name="valueRequired" type="select" selector="#is_required"/> <element name="advancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> <element name="defaultValue" type="input" selector="#default_value_text"/> - <element name="save" type="button" selector="#save"/> + <element name="save" type="button" selector="#save" timeout="30"/> <element name="saveAndEdit" type="button" selector="#save_and_edit_button"/> <element name="checkIfTabOpen" selector="//div[@id='advanced_fieldset-wrapper' and not(contains(@class,'opened'))]" type="button"/> </section> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml index dee6f9284a49f..c8ca2ced0d60f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml @@ -91,9 +91,7 @@ <actionGroup ref="navigateToEditProductAttribute" stepKey="navigateToSkuProductAttribute"> <argument name="attributeLabel" value="sku"/> </actionGroup> - <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToYes"> - <argument name="useForRule" value="Yes"/> - </actionGroup> + <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToYes"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> @@ -112,7 +110,9 @@ <actionGroup ref="navigateToEditProductAttribute" stepKey="navigateToSkuProductAttribute"> <argument name="attributeLabel" value="sku"/> </actionGroup> - <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToNo"/> + <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToNo"> + <argument name="useForPromoRule" value="No"/> + </actionGroup> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> From 423f89e08eb2e6b15bab2e6f33910bc39498ddf7 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 17 Oct 2018 10:41:48 +0300 Subject: [PATCH 137/240] MAGETWO-95721: Removing Special Price changes final_price, min_price, max_price to 0.00 --- .../Indexer/Price/Query/BaseFinalPrice.php | 2 +- .../ResourceModel/Product/CollectionTest.php | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php index 8428ff3688b28..0005ac8dea58a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php @@ -190,7 +190,7 @@ public function getQuery(array $dimensions, string $productType, array $entityId $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}"; $specialToExpr = "{$specialTo} IS NULL OR {$specialToDate} >= {$currentDate}"; $specialPriceExpr = $connection->getCheckSql( - "{$specialPrice} IS NOT NULL AND {$specialFromExpr} AND {$specialToExpr}", + "{$specialPrice} IS NOT NULL AND ({$specialFromExpr}) AND ({$specialToExpr})", $specialPrice, $maxUnsignedBigint ); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index a14f58874adcb..6ac7a8551fd99 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -127,6 +127,36 @@ public function testGetProductsWithTierPrice() $this->assertEquals(5, $tierPrices[2]->getValue()); } + /** + * @magentoDataFixture Magento/Catalog/_files/products.php + * @magentoDbIsolation disabled + */ + public function testGetProductsWithSpecialPrice() + { + $product = $this->productRepository->get('simple'); + $originalFinalPrice = $product->getFinalPrice(); + + $specialPrice = 9; + $product->setSpecialPrice($specialPrice); + $product = $this->productRepository->save($product); + /** @var \Magento\Catalog\Model\Product $item */ + $item = $this->collection->addIdFilter($product->getId()) + ->addPriceData() + ->getFirstItem(); + $item->setPriceCalculation(false); + $this->assertEquals($specialPrice, $item->getFinalPrice()); + + $product->setSpecialPrice(null); + $product = $this->productRepository->save($product); + /** @var \Magento\Catalog\Model\Product $item */ + $item = $this->collection->clear() + ->addIdFilter($product->getId()) + ->addPriceData() + ->getFirstItem(); + $item->setPriceCalculation(false); + $this->assertEquals($originalFinalPrice, $item->getFinalPrice()); + } + /** * Test addAttributeToSort() with attribute 'is_saleable' works properly on frontend. * From 365df7ebfabaa80e7cb409c3b1c9851ddeae889c Mon Sep 17 00:00:00 2001 From: vtymchynskyi <vtymchynskyi@magento.com> Date: Wed, 17 Oct 2018 12:11:19 +0300 Subject: [PATCH 138/240] MAGETWO-95497: Please check the shipping address information. Invalid value of "GB" provided for the regionId field. --- .../ValidationRules/AllowedCountryValidationRule.php | 10 ++++++++-- .../ValidationRules/BillingAddressValidationRule.php | 6 +++++- .../ValidationRules/ShippingAddressValidationRule.php | 5 ++++- .../ValidationRules/ShippingMethodValidationRule.php | 6 ++++-- .../Magento/Quote/Model/QuoteValidatorTest.php | 2 +- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php index 840e94e3ece1f..2498e9976f009 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/AllowedCountryValidationRule.php @@ -10,6 +10,7 @@ use Magento\Directory\Model\AllowedCountries; use Magento\Framework\Validation\ValidationResultFactory; use Magento\Quote\Model\Quote; +use Magento\Store\Model\ScopeInterface; /** * @inheritdoc @@ -54,10 +55,15 @@ public function validate(Quote $quote): array $validationErrors = []; if (!$quote->isVirtual()) { + $shippingAddress = $quote->getShippingAddress(); + $shippingAddress->setStoreId($quote->getStoreId()); $validationResult = in_array( - $quote->getShippingAddress()->getCountryId(), - $this->allowedCountryReader->getAllowedCountries() + $shippingAddress->getCountryId(), + $this->allowedCountryReader->getAllowedCountries( + ScopeInterface::SCOPE_STORE, + $quote->getStoreId() + ) ); if (!$validationResult) { $validationErrors = [__($this->generalMessage)]; diff --git a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php index fbacbf1c8d30c..2c02c9f9eacc4 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php @@ -43,7 +43,11 @@ public function __construct( public function validate(Quote $quote): array { $validationErrors = []; - $validationResult = $quote->getBillingAddress()->validate(); + + $billingAddress = $quote->getBillingAddress(); + $billingAddress->setStoreId($quote->getStoreId()); + $validationResult = $billingAddress->validate(); + if ($validationResult !== true) { $validationErrors = [__($this->generalMessage)]; } diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php index f5eebe241acc9..1ced36b9bccf3 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php @@ -45,7 +45,10 @@ public function validate(Quote $quote): array $validationErrors = []; if (!$quote->isVirtual()) { - $validationResult = $quote->getShippingAddress()->validate(); + $shippingAddress = $quote->getShippingAddress(); + $shippingAddress->setStoreId($quote->getStoreId()); + $validationResult = $shippingAddress->validate(); + if ($validationResult !== true) { $validationErrors = [__($this->generalMessage)]; } diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php index 6df7f663b0630..3ef079f5a019a 100644 --- a/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php +++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingMethodValidationRule.php @@ -45,8 +45,10 @@ public function validate(Quote $quote): array $validationErrors = []; if (!$quote->isVirtual()) { - $shippingMethod = $quote->getShippingAddress()->getShippingMethod(); - $shippingRate = $quote->getShippingAddress()->getShippingRateByCode($shippingMethod); + $shippingAddress = $quote->getShippingAddress(); + $shippingAddress->setStoreId($quote->getStoreId()); + $shippingMethod = $shippingAddress->getShippingMethod(); + $shippingRate = $shippingAddress->getShippingRateByCode($shippingMethod); $validationResult = $shippingMethod && $shippingRate; if (!$validationResult) { $validationErrors = [__($this->generalMessage)]; diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php index 1088d0f4fbeb0..14ecf3a7301e2 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteValidatorTest.php @@ -64,7 +64,7 @@ public function testValidateBeforeSubmitCountryIsNotAllowed() )->setValue( 'general/country/allow', 'US', - \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITES + \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); $quote = $this->objectManager->create(Quote::class); $quote->load('quote123', 'reserved_order_id'); From d5e706c1e5868b64740a6c535e7aa9998a974ab0 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 17 Oct 2018 13:15:28 +0300 Subject: [PATCH 139/240] MAGETWO-73449: Changes in default scope not effect product images in other scopes --- .../Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index f8df53aabd53d..be92bd041f538 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -132,7 +132,7 @@ <!--Switch to New Store view--> <actionGroup name="SwitchToTheNewStoreView"> <arguments> - <argument name="storeViewName" type="string"/> + <argument name="storeViewName"/> </arguments> <scrollTo selector="{{AdminHeaderSection.pageTitle}}" stepKey="scrollToUp"/> <waitForElementVisible selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="waitForElementBecomeVisible"/> From b8f78cc6656a40b61106d5c70431faf8c1e990e1 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 17 Oct 2018 13:31:42 +0300 Subject: [PATCH 140/240] MAGETWO-94988: Image downsampling to 80% on the product page --- .../Block/DataProviders/UploadConfig.php | 40 +++++++++++++++++++ .../Magento/Backend/etc/adminhtml/system.xml | 15 ++++++- app/code/Magento/Backend/etc/config.xml | 1 + .../adminhtml/templates/media/uploader.phtml | 3 +- .../view/adminhtml/web/js/media-uploader.js | 28 ++++++++----- .../Product/Helper/Form/Gallery/Content.php | 19 ++++++++- .../Magento/Catalog/Model/Product/Image.php | 9 +++-- .../Magento/Catalog/etc/adminhtml/system.xml | 14 +++++++ app/code/Magento/Catalog/etc/config.xml | 3 ++ .../layout/cms_wysiwyg_images_index.xml | 7 +++- .../templates/browser/content/uploader.phtml | 16 +++++--- 11 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 app/code/Magento/Backend/Block/DataProviders/UploadConfig.php diff --git a/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php b/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php new file mode 100644 index 0000000000000..24ef8cddce7e8 --- /dev/null +++ b/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Block\DataProviders; + +use Magento\Framework\View\Element\Block\ArgumentInterface; + + +/** + * Provides additional data for image uploader + */ +class UploadConfig implements ArgumentInterface +{ + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $config; + + /** + * @param \Magento\Framework\App\Config\ScopeConfigInterface $config + */ + public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $config) + { + $this->config = $config; + } + + /** + * Get image resize configuration + * + * @return int + */ + public function getIsResizeEnabled(): int + { + return (int) $this->config->getValue('system/upload_configuration/enable_resize'); + } +} diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index ecf11ac601861..e3411166ee4a4 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -322,15 +322,26 @@ </group> <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Images Upload Configuration</label> - <field id="max_width" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable_resize" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <label>Enable Frontend Resize</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>Resize performed via javascript before file upload.</comment> + </field> + <field id="max_width" translate="label comment" type="text" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Maximum Width</label> <validate>validate-greater-than-zero validate-number required-entry</validate> <comment>Maximum allowed width for uploaded image.</comment> + <depends> + <field id="enable_resize">1</field> + </depends> </field> - <field id="max_height" translate="label comment" type="text" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_height" translate="label comment" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Maximum Height</label> <validate>validate-greater-than-zero validate-number required-entry</validate> <comment>Maximum allowed height for uploaded image.</comment> + <depends> + <field id="enable_resize">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Backend/etc/config.xml b/app/code/Magento/Backend/etc/config.xml index 45d283ad3ff22..8283fa18dd370 100644 --- a/app/code/Magento/Backend/etc/config.xml +++ b/app/code/Magento/Backend/etc/config.xml @@ -29,6 +29,7 @@ <enable_charts>1</enable_charts> </dashboard> <upload_configuration> + <enable_resize>1</enable_resize> <max_width>1920</max_width> <max_height>1200</max_height> </upload_configuration> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml index 966372773f295..50cfdc4e07681 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml @@ -14,7 +14,8 @@ "Magento_Backend/js/media-uploader" : { "maxFileSize": <?= /* @escapeNotVerified */ $block->getFileSizeService()->getMaxFileSize() ?>, "maxWidth":<?= /* @escapeNotVerified */ $block->getImageUploadMaxWidth() ?> , - "maxHeight": <?= /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?> + "maxHeight": <?= /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?>, + "isResizeEnabled": <?= /* @noEscape */ $block->getImageUploadConfigData()->getIsResizeEnabled() ?> } }' > diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js index 03403a4ec4a04..34e388bac3d33 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js @@ -25,9 +25,20 @@ define([ * @private */ _create: function () { - var - self = this, - progressTmpl = mageTemplate('[data-template="uploader"]'); + var self = this, + progressTmpl = mageTemplate('[data-template="uploader"]'), + isResizeEnabled = this.options.isResizeEnabled, + resizeConfiguration = { + action: 'resize', + maxWidth: this.options.maxWidth, + maxHeight: this.options.maxHeight + }; + + if (!isResizeEnabled) { + resizeConfiguration = { + action: 'resize' + }; + } this.element.find('input[type=file]').fileupload({ dataType: 'json', @@ -44,8 +55,7 @@ define([ * @param {Object} data */ add: function (e, data) { - var - fileSize, + var fileSize, tmpl; $.each(data.files, function (index, file) { @@ -115,11 +125,9 @@ define([ process: [{ action: 'load', fileTypes: /^image\/(gif|jpeg|png)$/ - }, { - action: 'resize', - maxWidth: this.options.maxWidth, - maxHeight: this.options.maxHeight - }, { + }, + resizeConfiguration, + { action: 'save' }] }); diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php index ac9e75493bdc3..dd7554a9b7b19 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php @@ -17,9 +17,16 @@ use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\App\ObjectManager; +use Magento\Backend\Block\DataProviders\UploadConfig as ImageUploadConfigDataProvider; class Content extends \Magento\Backend\Block\Widget { + /** + * @var ImageUploadConfigDataProvider + */ + private $imageUploadConfigDataProvider; + /** * @var string */ @@ -45,16 +52,20 @@ class Content extends \Magento\Backend\Block\Widget * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder * @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig * @param array $data + * @param ImageUploadConfigDataProvider $imageUploadConfigDataProvider */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\Json\EncoderInterface $jsonEncoder, \Magento\Catalog\Model\Product\Media\Config $mediaConfig, - array $data = [] + array $data = [], + ImageUploadConfigDataProvider $imageUploadConfigDataProvider = null ) { $this->_jsonEncoder = $jsonEncoder; $this->_mediaConfig = $mediaConfig; parent::__construct($context, $data); + $this->imageUploadConfigDataProvider = $imageUploadConfigDataProvider + ?: ObjectManager::getInstance()->get(ImageUploadConfigDataProvider::class); } /** @@ -62,7 +73,11 @@ public function __construct( */ protected function _prepareLayout() { - $this->addChild('uploader', \Magento\Backend\Block\Media\Uploader::class); + $this->addChild( + 'uploader', + \Magento\Backend\Block\Media\Uploader::class, + ['image_upload_config_data' => $this->imageUploadConfigDataProvider] + ); $this->getUploader()->getConfig()->setUrl( $this->_urlBuilder->addSessionParam()->getUrl('catalog/product_gallery/upload') diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index 74e5e94889253..2463734503d88 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -36,7 +36,7 @@ class Image extends \Magento\Framework\Model\AbstractModel * * @var int */ - protected $_quality = 80; + protected $_quality = null; /** * @var bool @@ -294,7 +294,8 @@ public function setQuality($quality) */ public function getQuality() { - return $this->_quality; + return $this->_quality === null + ? $this->_scopeConfig->getValue('system/upload_configuration/jpeg_quality') : $this->_quality; } /** @@ -471,7 +472,7 @@ public function getImageProcessor() $this->_processor->keepTransparency($this->_keepTransparency); $this->_processor->constrainOnly($this->_constrainOnly); $this->_processor->backgroundColor($this->_backgroundColor); - $this->_processor->quality($this->_quality); + $this->_processor->quality($this->getQuality()); return $this->_processor; } @@ -854,7 +855,7 @@ private function getMiscParams() 'constrain_only' => ($this->_constrainOnly ? 'do' : 'not') . 'constrainonly', 'background' => $this->_rgbToString($this->_backgroundColor), 'angle' => $this->_angle, - 'quality' => $this->_quality, + 'quality' => $this->getQuality(), ]; // if has watermark add watermark params to hash diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 1f78de93b369d..77b69185fc355 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -185,5 +185,19 @@ </field> </group> </section> + <section id="system" translate="label" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> + <class>separator-top</class> + <label>System</label> + <tab>advanced</tab> + <resource>Magento_Config::config_system</resource> + <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> + <label>Images Upload Configuration</label> + <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <label>Quality</label> + <validate>validate-digits validate-digits-range digits-range-1-100 required-entry</validate> + <comment>Jpeg quality for resized images 1-100%.</comment> + </field> + </group> + </section> </system> </config> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 74f7a0e082dd6..b7733a1ab0239 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -65,6 +65,9 @@ <product_custom_options_fodler>custom_options</product_custom_options_fodler> </allowed_resources> </media_storage_configuration> + <upload_configuration> + <jpeg_quality>80</jpeg_quality> + </upload_configuration> </system> <design> <watermark> diff --git a/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml b/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml index 1bc8828ef6c8e..bc0fea6f82bc6 100644 --- a/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml +++ b/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml @@ -9,7 +9,10 @@ <container name="root"> <block class="Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Content" name="wysiwyg_images.content" template="Magento_Cms::browser/content.phtml"> <block class="Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Tree" name="wysiwyg_images.tree" template="Magento_Cms::browser/tree.phtml"/> - <block class="Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Content\Uploader" name="wysiwyg_images.uploader" template="Magento_Cms::browser/content/uploader.phtml"/> - </block> + <block class="Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Content\Uploader" name="wysiwyg_images.uploader" template="Magento_Cms::browser/content/uploader.phtml"> + <arguments> + <argument name="image_upload_config_data" xsi:type="object">Magento\Backend\Block\DataProviders\UploadConfig</argument> + </arguments> + </block> </container> </layout> diff --git a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml index f87dc07dc04f4..27f14434b5fcf 100644 --- a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml +++ b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml @@ -7,6 +7,14 @@ // @codingStandardsIgnoreFile /** @var $block \Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Content\Uploader */ + +$resizeConfig = $block->getImageUploadConfigData()->getIsResizeEnabled() + ? "{action: 'resize', maxWidth: " + . $block->getImageUploadMaxWidth() + . ", maxHeight: " + . $block->getImageUploadMaxHeight() + . "}" + : "{action: 'resize'}"; ?> <div id="<?= $block->getHtmlId() ?>" class="uploader"> @@ -99,11 +107,9 @@ require([ action: 'load', fileTypes: /^image\/(gif|jpeg|png)$/, maxFileSize: <?= (int) $block->getFileSizeService()->getMaxFileSize() ?> * 10 - }, { - action: 'resize', - maxWidth: <?= (int) $block->getImageUploadMaxWidth() ?> , - maxHeight: <?= (int) $block->getImageUploadMaxHeight() ?> - }, { + }, + <?= $block->escapeHtml($resizeConfig) ?>, + { action: 'save' }] }); From 072498e01173114e1f7d96267162a212576d90c6 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 17 Oct 2018 13:38:33 +0300 Subject: [PATCH 141/240] ENGCOM-2271: Fix functional test failures. --- .../Test/Block/System/Config/AnalyticsForm.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Analytics/Test/Block/System/Config/AnalyticsForm.php b/dev/tests/functional/tests/app/Magento/Analytics/Test/Block/System/Config/AnalyticsForm.php index 07b62a9518ae4..bf1f55915108b 100644 --- a/dev/tests/functional/tests/app/Magento/Analytics/Test/Block/System/Config/AnalyticsForm.php +++ b/dev/tests/functional/tests/app/Magento/Analytics/Test/Block/System/Config/AnalyticsForm.php @@ -95,12 +95,17 @@ public function getAnalyticsStatus() /** * @param string $vertical - * @return array|string + * @return $this */ public function setAnalyticsVertical($vertical) { - return $this->_rootElement->find($this->analyticsVertical, Locator::SELECTOR_CSS, 'select') - ->setValue($vertical); + + $element = $this->_rootElement->find($this->analyticsVertical, Locator::SELECTOR_CSS, 'select'); + if ($element->isVisible()) { + $element->setValue($vertical); + } + + return $this; } /** From 5e4f717e3f61cdd3fda4f835066f748228c2144c Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 17 Oct 2018 13:54:32 +0300 Subject: [PATCH 142/240] ENGCOM-2271: Updated functional test. --- .../Test/Constraint/AssertConfigAnalyticsDisabled.php | 4 ++++ .../Test/Constraint/AssertConfigAnalyticsEnabled.php | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsDisabled.php b/dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsDisabled.php index 0f65835a32aa7..b061a5d644484 100644 --- a/dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsDisabled.php +++ b/dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsDisabled.php @@ -34,6 +34,10 @@ public function processAssert(ConfigAnalytics $configAnalytics, OpenAnalyticsCon 'Subscription status: Disabled', 'Magento Advanced Reporting service subscription status is not disabled.' ); + \PHPUnit_Framework_Assert::assertFalse( + (bool)$configAnalytics->getAnalyticsForm()->getAnalyticsVerticalScope(), + 'Industry Data is visible.' + ); } /** diff --git a/dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsEnabled.php b/dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsEnabled.php index 8fd04e06b14bb..46d5d8e2d5f78 100644 --- a/dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsEnabled.php +++ b/dev/tests/functional/tests/app/Magento/Analytics/Test/Constraint/AssertConfigAnalyticsEnabled.php @@ -29,12 +29,15 @@ public function processAssert(ConfigAnalytics $configAnalytics, OpenAnalyticsCon (bool)$configAnalytics->getAnalyticsForm()->isAnalyticsEnabled(), 'Magento Advanced Reporting service is not enabled.' ); - \PHPUnit_Framework_Assert::assertEquals( $configAnalytics->getAnalyticsForm()->getAnalyticsStatus(), 'Subscription status: Pending', 'Magento Advanced Reporting service subscription status is not pending.' ); + \PHPUnit_Framework_Assert::assertTrue( + (bool)$configAnalytics->getAnalyticsForm()->getAnalyticsVerticalScope(), + 'Industry Data is not visible.' + ); } /** From a93b9e20a8f51ba62c8144a8299cf484a380719c Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 17 Oct 2018 14:34:12 +0300 Subject: [PATCH 143/240] MAGETWO-94988: Image downsampling to 80% on the product page --- .../Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml b/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml index bc0fea6f82bc6..5bf66ef302f04 100644 --- a/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml +++ b/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml @@ -14,5 +14,6 @@ <argument name="image_upload_config_data" xsi:type="object">Magento\Backend\Block\DataProviders\UploadConfig</argument> </arguments> </block> + </block> </container> </layout> From 6c273949e4d2e5f7f933a4dfab965a7abde9bf5d Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 17 Oct 2018 15:38:39 +0300 Subject: [PATCH 144/240] MAGETWO-94988: Image downsampling to 80% on the product page --- .../Block/DataProviders/UploadConfigTest.php | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php diff --git a/app/code/Magento/Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php b/app/code/Magento/Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php new file mode 100644 index 0000000000000..febaad82b230e --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Backend\Test\Unit\Block\DataProviders; + +use Magento\Framework\App\Config\ScopeConfigInterface as ScopeConfig; +use Magento\Backend\Block\DataProviders\UploadConfig; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class UploadConfigTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ScopeConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $config; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var UploadConfig + */ + private $uploadConfig; + + protected function setUp() + { + $this->config = $this->getMockBuilder(ScopeConfig::class) + ->setMethods(['getValue']) + ->getMockForAbstractClass(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->uploadConfig = $this->objectManagerHelper->getObject( + UploadConfig::class, + [ + 'config' => $this->config + ] + ); + } + + /** + * @dataProvider configValuesDataProvider() + * @param int $configValue + * @param int $expectedValue + * @return void + */ + public function testGetIsResizeEnabled(int $configValue, int $expectedValue) + { + $this->config->expects($this->once()) + ->method('getValue') + ->with('system/upload_configuration/enable_resize') + ->willReturn($configValue); + $this->assertEquals($expectedValue, $this->uploadConfig->getIsResizeEnabled()); + } + + public function configValuesDataProvider(): array + { + return [ + [1, 1], + [0, 0] + ]; + } +} From 9299bcf3096cb9eed7db32fac10db792e02af162 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 17 Oct 2018 15:39:13 +0300 Subject: [PATCH 145/240] MAGETWO-94988: Image downsampling to 80% on the product page --- .../Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php b/app/code/Magento/Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php index febaad82b230e..481c08aed4a01 100644 --- a/app/code/Magento/Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php +++ b/app/code/Magento/Backend/Test/Unit/Block/DataProviders/UploadConfigTest.php @@ -31,7 +31,6 @@ protected function setUp() $this->config = $this->getMockBuilder(ScopeConfig::class) ->setMethods(['getValue']) ->getMockForAbstractClass(); - $this->objectManagerHelper = new ObjectManagerHelper($this); $this->uploadConfig = $this->objectManagerHelper->getObject( UploadConfig::class, From c5987cec7fa9b9c0fe0ef3cbd71085c66b7e2711 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 17 Oct 2018 15:53:54 +0300 Subject: [PATCH 146/240] MAGETWO-94988: Image downsampling to 80% on the product page --- app/code/Magento/Backend/Block/DataProviders/UploadConfig.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php b/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php index 24ef8cddce7e8..3f79b6bd49df9 100644 --- a/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php +++ b/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php @@ -9,7 +9,6 @@ use Magento\Framework\View\Element\Block\ArgumentInterface; - /** * Provides additional data for image uploader */ From 13a8b927b2ce03b6d5a99ee07933352c8741a5e7 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 17 Oct 2018 15:54:18 +0300 Subject: [PATCH 147/240] MAGETWO-94988: Image downsampling to 80% on the product page --- app/code/Magento/Backend/Block/DataProviders/UploadConfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php b/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php index 3f79b6bd49df9..5c66ccff0826b 100644 --- a/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php +++ b/app/code/Magento/Backend/Block/DataProviders/UploadConfig.php @@ -34,6 +34,6 @@ public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $ */ public function getIsResizeEnabled(): int { - return (int) $this->config->getValue('system/upload_configuration/enable_resize'); + return (int)$this->config->getValue('system/upload_configuration/enable_resize'); } } From 70a0ae084d9327fee1f5c2a7ba64606725a88830 Mon Sep 17 00:00:00 2001 From: Sven Reichel <github-sr@hotmail.com> Date: Wed, 17 Oct 2018 14:56:10 +0200 Subject: [PATCH 148/240] Remove necessary class import, see #18280 --- app/code/Magento/Customer/Model/GroupManagement.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/GroupManagement.php b/app/code/Magento/Customer/Model/GroupManagement.php index eb5d90f2fd7ea..48cb5d55061c5 100644 --- a/app/code/Magento/Customer/Model/GroupManagement.php +++ b/app/code/Magento/Customer/Model/GroupManagement.php @@ -15,7 +15,6 @@ use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Data\Collection; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Model\StoreManagerInterface; @@ -170,7 +169,7 @@ public function getLoggedInGroups() ->create(); $groupNameSortOrder = $this->sortOrderBuilder ->setField('customer_group_code') - ->setDirection(Collection::SORT_ORDER_ASC) + ->setAscendingDirection() ->create(); $searchCriteria = $this->searchCriteriaBuilder ->addFilters($notLoggedInFilter) From a05d9437684d17030734f7bb10ad1b6e829713f9 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 17 Oct 2018 17:22:40 +0300 Subject: [PATCH 149/240] MAGETWO-94988: Image downsampling to 80% on the product page --- .../Test/Unit/Model/Product/ImageTest.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php index 627aa1848506e..f5ed96882b981 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php @@ -9,6 +9,7 @@ use Magento\Catalog\Model\View\Asset\PlaceholderFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\Config\ScopeConfigInterface as ScopeConfig; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -81,6 +82,11 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ private $cacheManager; + /** + * @var ScopeConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -89,7 +95,6 @@ protected function setUp() ->disableOriginalConstructor() ->getMockForAbstractClass(); $this->context->expects($this->any())->method('getCacheManager')->will($this->returnValue($this->cacheManager)); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManager::class) ->disableOriginalConstructor() ->setMethods(['getStore', 'getWebsite'])->getMock(); @@ -98,24 +103,20 @@ protected function setUp() $store->expects($this->any())->method('getId')->will($this->returnValue(1)); $store->expects($this->any())->method('getBaseUrl')->will($this->returnValue('http://magento.com/media/')); $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($store)); - $this->config = $this->getMockBuilder(\Magento\Catalog\Model\Product\Media\Config::class) ->setMethods(['getBaseMediaPath'])->disableOriginalConstructor()->getMock(); $this->config->expects($this->any())->method('getBaseMediaPath')->will($this->returnValue('catalog/product')); $this->coreFileHelper = $this->getMockBuilder(\Magento\MediaStorage\Helper\File\Storage\Database::class) ->setMethods(['saveFile', 'deleteFolder'])->disableOriginalConstructor()->getMock(); - $this->mediaDirectory = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\Write::class) ->disableOriginalConstructor() ->setMethods(['create', 'isFile', 'isExist', 'getAbsolutePath']) ->getMock(); - $this->filesystem = $this->createMock(\Magento\Framework\Filesystem::class); $this->filesystem->expects($this->once())->method('getDirectoryWrite') ->with(DirectoryList::MEDIA) ->will($this->returnValue($this->mediaDirectory)); $this->factory = $this->createMock(\Magento\Framework\Image\Factory::class); - $this->viewAssetImageFactory = $this->getMockBuilder(ImageFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) @@ -141,6 +142,10 @@ function ($value) { return json_decode($value, true); } ); + $this->scopeConfig = $this->getMockBuilder(ScopeConfig::class) + ->setMethods(['getValue'])->getMockForAbstractClass(); + $this->scopeConfig->expects($this->any())->method('getValue') + ->with('system/upload_configuration/jpeg_quality')->willReturn(80); $this->image = $objectManager->getObject( \Magento\Catalog\Model\Product\Image::class, @@ -153,7 +158,8 @@ function ($value) { 'imageFactory' => $this->factory, 'viewAssetImageFactory' => $this->viewAssetImageFactory, 'viewAssetPlaceholderFactory' => $this->viewAssetPlaceholderFactory, - 'serializer' => $this->serializer + 'serializer' => $this->serializer, + 'scopeConfig' => $this->scopeConfig ] ); From 828cec6a996846099bf9ddd9038171b5ab850ecb Mon Sep 17 00:00:00 2001 From: Tomash Khamlai <tomash.khamlai@gmail.com> Date: Wed, 17 Oct 2018 23:40:34 +0300 Subject: [PATCH 150/240] Set fallback values for email and _website columns to avoid 'undefined index' error in CustomerComposite.php --- .../CustomerImportExport/Model/Import/CustomerComposite.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php b/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php index 36c1e761d013c..1ff1adc3dac58 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php @@ -299,8 +299,8 @@ public function validateData() $rows = []; foreach ($source as $row) { $rows[] = [ - Address::COLUMN_EMAIL => $row[Customer::COLUMN_EMAIL], - Address::COLUMN_WEBSITE => $row[Customer::COLUMN_WEBSITE] + Address::COLUMN_EMAIL => $row[Customer::COLUMN_EMAIL] ?? null, + Address::COLUMN_WEBSITE => $row[Customer::COLUMN_WEBSITE] ?? null ]; } $source->rewind(); From 0ac0f867ba91a9a96580a47bfdb287f93f89aedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Mour=C3=A3o?= <rodrigo@webjump.com.br> Date: Wed, 17 Oct 2018 20:40:22 -0300 Subject: [PATCH 151/240] Update Config.php Fix new line EOF --- app/code/Magento/Config/Model/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index 424c47b7ef9aa..670ba204cefb4 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -581,4 +581,4 @@ public function getConfigDataValue($path, &$inherit = null, $configData = null) return $data; } -} \ No newline at end of file +} From ad68105413c6375dbb48884b434a40bb17f72b25 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Thu, 18 Oct 2018 12:15:13 +0530 Subject: [PATCH 152/240] Fix SKU limit in import new products for 2.3 with backward compatible allow 64 characters for SKU --- .../CatalogImportExport/Model/Import/Product/Validator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 40cf70b8f4e73..793ad935363d3 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -71,7 +71,7 @@ protected function textValidation($attrCode, $type) if ($type == 'text') { $valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH; } else if ($attrCode == Product::COL_SKU) { - $valid = $this->string->strlen($val) < SKU::SKU_MAX_LENGTH; + $valid = $this->string->strlen($val) <= SKU::SKU_MAX_LENGTH; } else { $valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH; } From eeb8af6c33dfeb6604fd0a548a0b40ec0a41eb66 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Thu, 18 Oct 2018 05:38:24 -0700 Subject: [PATCH 153/240] MAGETWO-95328: Customer can place order with new addresses that was edited during checkout with several conditions --- ...eckoutFillNewBillingAddressActionGroup.xml | 33 ++++++- .../Checkout/Test/Mftf/Page/CheckoutPage.xml | 3 +- .../Section/CheckoutAddressPopupSection.xml | 14 +++ .../Mftf/Section/CheckoutShippingSection.xml | 5 +- .../StorefrontCustomerCheckoutWithTaxTest.xml | 1 - ...OrderWithNewAddressesThatWasEditedTest.xml | 91 +++++++++++++++++++ .../Customer/Test/Mftf/Data/AddressData.xml | 15 ++- .../Page/StorefrontCustomerAddressPage.xml | 13 +++ .../StorefrontCustomerAddressesSection.xml | 13 +++ .../StorefrontCustomerOrderViewSection.xml | 4 +- 10 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Section/CheckoutAddressPopupSection.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressPage.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressesSection.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml index afe37a10e6634..11cd095334113 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Guest checkout filling billing section --> <actionGroup name="GuestCheckoutFillNewBillingAddressActionGroup"> <arguments> @@ -25,4 +25,35 @@ <fillField selector="{{CheckoutPaymentSection.guestTelephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> </actionGroup> + <actionGroup name="LoggedInCheckoutFillNewBillingAddressActionGroup"> + <arguments> + <argument name="customerAddress"/> + <!-- the classPrefix argument is to specifically select the inputs of the correct form + this is to prevent having 3 action groups doing essentially the same thing --> + <argument name="classPrefix" type="string" defaultValue=""/> + </arguments> + <fillField stepKey="fillFirstName" selector="{{classPrefix}} {{CheckoutShippingSection.firstName}}" userInput="{{customerAddress.firstname}}"/> + <fillField stepKey="fillLastName" selector="{{classPrefix}} {{CheckoutShippingSection.lastName}}" userInput="{{customerAddress.lastname}}"/> + <fillField stepKey="fillPhoneNumber" selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" userInput="{{customerAddress.telephone}}"/> + <fillField stepKey="fillStreetAddress1" selector="{{classPrefix}} {{CheckoutShippingSection.street}}" userInput="{{customerAddress.street[0]}}"/> + <fillField stepKey="fillCityName" selector="{{classPrefix}} {{CheckoutShippingSection.city}}" userInput="{{customerAddress.city}}"/> + <selectOption stepKey="selectState" selector="{{classPrefix}} {{CheckoutShippingSection.region}}" userInput="{{customerAddress.state}}"/> + <fillField stepKey="fillZip" selector="{{classPrefix}} {{CheckoutShippingSection.postcode}}" userInput="{{customerAddress.postcode}}"/> + <selectOption stepKey="selectCounty" selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="{{customerAddress.country_id}}"/> + <waitForPageLoad stepKey="waitForFormUpdate2"/> + </actionGroup> + + <actionGroup name="clearCheckoutAddressPopupFieldsActionGroup"> + <arguments> + <argument name="classPrefix" type="string" defaultValue=""/> + </arguments> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.firstName}}" stepKey="clearFieldFirstName"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.lastName}}" stepKey="clearFieldLastName"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.street}}" stepKey="clearFieldStreetAddress1"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.city}}" stepKey="clearFieldCityName"/> + <selectOption selector="{{classPrefix}} {{CheckoutShippingSection.region}}" userInput="" stepKey="clearFieldRegion"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.postcode}}" stepKey="clearFieldZip"/> + <selectOption selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> + </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml index f1d792a638746..0c6a1682a7719 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_Checkout"> <section name="CheckoutShippingSection"/> <section name="CheckoutShippingMethodsSection"/> @@ -15,5 +15,6 @@ <section name="CheckoutSuccessMainSection"/> <section name="CheckoutPaymentSection"/> <section name="CheckoutGuestShippingInfoSection"/> + <section name="CheckoutAddressPopupSection"/> </page> </pages> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutAddressPopupSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutAddressPopupSection.xml new file mode 100644 index 0000000000000..9e8adab362a88 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutAddressPopupSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CheckoutAddressPopupSection"> + <element name="newAddressModalPopup" type="block" selector=".modal-popup.modal-slide._inner-scroll"/> + <element name="closeAddressModalPopup" type="button" selector=".action-hide-popup"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index f36d2a9b667cd..2a7437c44eccf 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutShippingSection"> <element name="isShippingStep" type="text" selector="//*[@class='opc-progress-bar']/li[contains(@class, '_active') and span[contains(.,'Shipping')]]"/> <element name="email" type="input" selector="#customer-email"/> @@ -24,7 +24,7 @@ <element name="newAddressButton" type="button" selector="#checkout-step-shipping button"/> <element name="next" type="button" selector="[data-role='opc-continue']"/> <element name="stateInput" type="input" selector="input[name=region]"/> - <element name="newAdress" type="button" selector="button.action.action-show-popup"/> + <element name="newAdress" type="button" selector="button.action.action-show-popup" timeout="30"/> <element name="addStreet" type="input" selector="#shipping-new-address-form input[name='street[0]']"/> <element name="addCity" type="input" selector="#shipping-new-address-form input[name='city']"/> <element name="addState" type="select" selector="#shipping-new-address-form select[name='region_id']"/> @@ -32,5 +32,6 @@ <element name="addTelephone" type="input" selector="#shipping-new-address-form input[name='telephone']"/> <element name="addcCountry" type="select" selector="#shipping-new-address-form select[name='country_id']"/> <element name="addSaveButton" type="button" selector=".action.primary.action-save-address"/> + <element name="editActiveAddress" type="button" selector="//div[@class='shipping-address-item selected-item']//span[text()='Edit']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithTaxTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithTaxTest.xml index ad26118ac2f06..7dba25812f0dd 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithTaxTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithTaxTest.xml @@ -70,7 +70,6 @@ <waitForPageLoad stepKey="waitForPage"/> <!-- Step 7: Click New Address button --> <click selector="{{CheckoutShippingSection.newAdress}}" stepKey="clickAddNewAddress"/> - <waitForPageLoad stepKey="waitForPagePopup"/> <!-- Step 8: Fill form with valid data and set: California --> <!-- Step 9: Click Save Address --> <actionGroup ref="LoggedInUserCheckoutAddNewAddressInShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingAddress"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml new file mode 100644 index 0000000000000..c44d1c5d9c72f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via the Storefront"/> + <title value="Customer Checkout"/> + <description value="Customer can place order with new addresses."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-77181"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + <!--Go to Storefront as Customer--> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="customerLogin"> + <argument name="customer" value="$$createCustomer$$" /> + </actionGroup> + + <!-- Add simple product to cart and go to checkout--> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> + + <!-- Click "+ New Address" and Fill new address--> + <click selector="{{CheckoutShippingSection.newAdress}}" stepKey="addAddress"/> + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="customerAddress" value="UK_Default_Address"/> + <argument name="classPrefix" value="._show"/> + </actionGroup> + + <!--Click "Save Addresses" --> + <click selector="{{CheckoutShippingSection.addSaveButton}}" stepKey="saveAddress"/> + <waitForPageLoad stepKey="waitForAddressSaved"/> + <dontSeeElement selector="{{CheckoutAddressPopupSection.newAddressModalPopup}}" stepKey="dontSeeModalPopup"/> + + <!--Select Shipping Rate "Flat Rate"--> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('Flat Rate')}}" stepKey="selectFlatShippingMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> + + <!--Click "Edit" for the new address and clear required fields--> + <click selector="{{CheckoutShippingSection.editActiveAddress}}" stepKey="editNewAddress"/> + <actionGroup ref="clearCheckoutAddressPopupFieldsActionGroup" stepKey="clearRequiredFields"> + <argument name="classPrefix" value="._show"/> + </actionGroup> + + <!--Close Popup and click next--> + <click selector="{{CheckoutAddressPopupSection.closeAddressModalPopup}}" stepKey="closePopup"/> + <!--<waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/>--> + <waitForPageLoad stepKey="123"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> + + <!--Refresh Page and Place Order--> + <reloadPage stepKey="reloadPage"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <seeElement selector="{{CheckoutSuccessMainSection.success}}" stepKey="orderIsSuccessfullyPlaced"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="grabOrderNumber"/> + + <!--Verify New addresses in Customer's Address Book--> + <amOnPage url="{{StorefrontCustomerAddressesPage.url}}" stepKey="goToCustomerAddressBook"/> + <see userInput="{{UK_Default_Address.street[0]}} {{UK_Default_Address.city}}, {{UK_Default_Address.postcode}}" + selector="{{StorefrontCustomerAddressesSection.addressesList}}" stepKey="checkNewAddresses"/> + <!--Order review page has address that was created during checkout--> + <amOnPage url="{{StorefrontCustomerOrderViewPage.url({$grabOrderNumber})}}" stepKey="goToOrderReviewPage"/> + <see userInput="{{UK_Default_Address.street[0]}} {{UK_Default_Address.city}}, {{UK_Default_Address.postcode}}" + selector="{{StorefrontCustomerOrderViewSection.shippingAddress}}" stepKey="checkShippingAddress"/> + <see userInput="{{UK_Default_Address.street[0]}} {{UK_Default_Address.city}}, {{UK_Default_Address.postcode}}" + selector="{{StorefrontCustomerOrderViewSection.billingAddress}}" stepKey="checkBillingAddress"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index e4420f189900e..c8ba70c7f8b50 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CustomerAddressSimple" type="address"> <data key="id">0</data> <data key="customer_id">12</data> @@ -89,4 +89,17 @@ <data key="default_shipping">Yes</data> <requiredEntity type="region">RegionNY</requiredEntity> </entity> + <entity name="UK_Default_Address" type="address"> + <data key="firstname">Jane</data> + <data key="lastname">Doe</data> + <data key="company">Magento</data> + <array key="street"> + <item>172, Westminster Bridge Rd</item> + </array> + <data key="city">London</data> + <data key="state"></data> + <data key="postcode">SE1 7RW</data> + <data key="country_id">GB</data> + <data key="telephone">444-44-444-44</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressPage.xml new file mode 100644 index 0000000000000..bdbbcab67da67 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerAddressesPage" url="/customer/address/" area="storefront" module="Magento_Customer"> + <section name="StorefrontCustomerAddressesSection"/> + </page> +</pages> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressesSection.xml new file mode 100644 index 0000000000000..88b46d245105f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressesSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerAddressesSection"> + <element name="addressesList" type="text" selector=".block-addresses-list" /> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml index 01b758c9c3e3b..f6a6cb2d457e1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml @@ -7,12 +7,14 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerOrderViewSection"> <element name="orderTitle" type="text" selector=".page-title span"/> <element name="myOrdersTable" type="text" selector="#my-orders-table"/> <element name="subtotal" type="text" selector=".subtotal .amount"/> <element name="paymentMethod" type="text" selector=".payment-method dt"/> <element name="printOrderLink" type="text" selector="a.action.print" timeout="30"/> + <element name="shippingAddress" type="text" selector=".box.box-order-shipping-address"/> + <element name="billingAddress" type="text" selector=".box.box-order-billing-address"/> </section> </sections> From e8781055d6a6ad183a09c45b8b3bf788d5615cc6 Mon Sep 17 00:00:00 2001 From: Neeta Kangiya <neeta@wagento.com> Date: Fri, 19 Oct 2018 11:44:40 +0530 Subject: [PATCH 154/240] add deprectaed in event arguments --- .../Model/Indexer/Fulltext/Action/DataProvider.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 80034eb9f8308..ff9cc7e919170 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -316,6 +316,10 @@ public function getSearchableAttributes($backendType = null) /** @var \Magento\Eav\Model\Entity\Attribute[] $attributes */ $attributes = $productAttributes->getItems(); + /** + * Event argument `catelogsearch_searchable_attributes_load_after` is @deprecated 100.2.5. Use + * `catalogsearch_searchable_attributes_load_after` instead. + */ $this->eventManager->dispatch( 'catalogsearch_searchable_attributes_load_after', ['engine' => $this->engine, 'attributes' => $attributes] From 07d31d991bf181baf2db02ee2a2f5a7c317c12de Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Fri, 19 Oct 2018 01:19:18 -0700 Subject: [PATCH 155/240] MAGETWO-95328: Customer can place order with new addresses that was edited during checkout with several conditions --- ...CustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml index c44d1c5d9c72f..c80e284633f12 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml @@ -65,16 +65,13 @@ <!--Close Popup and click next--> <click selector="{{CheckoutAddressPopupSection.closeAddressModalPopup}}" stepKey="closePopup"/> - <!--<waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/>--> - <waitForPageLoad stepKey="123"/> + <waitForPageLoad stepKey="waitForPopupClosed"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> <!--Refresh Page and Place Order--> <reloadPage stepKey="reloadPage"/> - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <seeElement selector="{{CheckoutSuccessMainSection.success}}" stepKey="orderIsSuccessfullyPlaced"/> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="placeOrder"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="grabOrderNumber"/> <!--Verify New addresses in Customer's Address Book--> From fa529366822a4e049ddcab820212af24ee931c2b Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Fri, 19 Oct 2018 14:02:30 -0500 Subject: [PATCH 156/240] Issue magento/magento2#18094: Should getQty() return int/float or string - remove typecasting from the setter --- app/code/Magento/Catalog/Model/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index b5e0a2c0f48b6..8a9233f176c61 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -972,7 +972,7 @@ public function afterSave() public function setQty($qty) { if ($this->getData('qty') != $qty) { - $this->setData('qty', (float)$qty); + $this->setData('qty', $qty); $this->reloadPriceInfo(); } return $this; From 59d1e79293cc37ba5e37c3eef4f04649908ce610 Mon Sep 17 00:00:00 2001 From: nmalevanec <feaec9b174ab7404a5814cd67315bd99cf0917c2> Date: Mon, 22 Oct 2018 13:03:09 +0300 Subject: [PATCH 157/240] ENGCOM-2444: Resolve incorrect scope code selection when the requested scopeCode is null #16940 --- app/code/Magento/Store/Model/StoreManager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Model/StoreManager.php b/app/code/Magento/Store/Model/StoreManager.php index a3423b96c3fe4..c47750c0fd8d0 100644 --- a/app/code/Magento/Store/Model/StoreManager.php +++ b/app/code/Magento/Store/Model/StoreManager.php @@ -234,11 +234,11 @@ public function getWebsites($withDefault = false, $codeKey = false) public function reinitStores() { $this->currentStoreId = null; - $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, [StoreResolver::CACHE_TAG, Store::CACHE_TAG]); - $this->scopeConfig->clean(); $this->storeRepository->clean(); $this->websiteRepository->clean(); $this->groupRepository->clean(); + $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, [StoreResolver::CACHE_TAG, Store::CACHE_TAG]); + $this->scopeConfig->clean(); } /** From 36a3c26bd7492f4638fe02085156d2510687a34a Mon Sep 17 00:00:00 2001 From: Giel Berkers <giel.berkers@isaac.nl> Date: Mon, 22 Oct 2018 13:43:18 +0200 Subject: [PATCH 158/240] [BUGFIX] GITHUB-18264 Backport of #17799 for the 2.2 branch --- .../Product/Indexer/Price/Query/BaseFinalPrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php index 8428ff3688b28..0005ac8dea58a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php @@ -190,7 +190,7 @@ public function getQuery(array $dimensions, string $productType, array $entityId $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}"; $specialToExpr = "{$specialTo} IS NULL OR {$specialToDate} >= {$currentDate}"; $specialPriceExpr = $connection->getCheckSql( - "{$specialPrice} IS NOT NULL AND {$specialFromExpr} AND {$specialToExpr}", + "{$specialPrice} IS NOT NULL AND ({$specialFromExpr}) AND ({$specialToExpr})", $specialPrice, $maxUnsignedBigint ); From 8bbc5a23c0082d5d47bfd3c078a37210a3d68222 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Mon, 22 Oct 2018 15:05:14 +0300 Subject: [PATCH 159/240] MAGETWO-94425: [2.2] Mini cart not getting updated if product disabled from backed --- .../MarkQuotesRecollectMassDisabled.php | 55 +++++++ .../Quote/Model/Quote/TotalsCollector.php | 3 +- .../ResourceModel/Quote/Item/Collection.php | 138 ++++++++++++------ ...atusProductUsingProductGridActionGroup.xml | 33 +++++ .../Mftf/Section/AdminProductGridSection.xml | 14 ++ ...efrontGuestCheckoutDisabledProductTest.xml | 121 +++++++++++++++ app/code/Magento/Quote/etc/di.xml | 3 + 7 files changed, 321 insertions(+), 46 deletions(-) create mode 100644 app/code/Magento/Quote/Model/Product/Plugin/MarkQuotesRecollectMassDisabled.php create mode 100644 app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml create mode 100644 app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml create mode 100644 app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml diff --git a/app/code/Magento/Quote/Model/Product/Plugin/MarkQuotesRecollectMassDisabled.php b/app/code/Magento/Quote/Model/Product/Plugin/MarkQuotesRecollectMassDisabled.php new file mode 100644 index 0000000000000..f18bb46fa63fb --- /dev/null +++ b/app/code/Magento/Quote/Model/Product/Plugin/MarkQuotesRecollectMassDisabled.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model\Product\Plugin; + +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Action as ProductAction; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; + +/** + * Remove quote items after mass disabling products + */ +class MarkQuotesRecollectMassDisabled +{ + /** @var QuoteResource$quoteResource */ + private $quoteResource; + + /** + * @param QuoteResource $quoteResource + */ + public function __construct( + QuoteResource $quoteResource + ) { + $this->quoteResource = $quoteResource; + } + + /** + * Clean quote items after mass disabling product + * + * @param \Magento\Catalog\Model\Product\Action $subject + * @param \Magento\Catalog\Model\Product\Action $result + * @param int[] $productIds + * @param int[] $attrData + * @param int $storeId + * @return \Magento\Catalog\Model\Product\Action + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterUpdateAttributes( + ProductAction $subject, + ProductAction $result, + $productIds, + $attrData, + $storeId + ): ProductAction { + if (isset($attrData['status']) && $attrData['status'] === Status::STATUS_DISABLED) { + $this->quoteResource->markQuotesRecollect($productIds); + } + + return $result; + } +} diff --git a/app/code/Magento/Quote/Model/Quote/TotalsCollector.php b/app/code/Magento/Quote/Model/Quote/TotalsCollector.php index 8fa03232a0e8d..8f18a04a102fa 100644 --- a/app/code/Magento/Quote/Model/Quote/TotalsCollector.php +++ b/app/code/Magento/Quote/Model/Quote/TotalsCollector.php @@ -203,11 +203,12 @@ protected function _validateCouponCode(\Magento\Quote\Model\Quote $quote) */ protected function _collectItemsQtys(\Magento\Quote\Model\Quote $quote) { + $quoteItems = $quote->getAllVisibleItems(); $quote->setItemsCount(0); $quote->setItemsQty(0); $quote->setVirtualItemsQty(0); - foreach ($quote->getAllVisibleItems() as $item) { + foreach ($quoteItems as $item) { if ($item->getParentItem()) { continue; } diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php index ef1705e0ad100..309c89e3702f5 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php @@ -3,9 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Quote\Model\ResourceModel\Quote\Item; -use \Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use Magento\Quote\Model\ResourceModel\Quote\Item as ResourceQuoteItem; /** * Quote item resource collection @@ -50,6 +57,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\VersionContro */ private $storeManager; + /** + * @var bool $recollectQuote + */ + private $recollectQuote = false; + /** * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory * @param \Psr\Log\LoggerInterface $logger @@ -102,7 +114,7 @@ public function __construct( */ protected function _construct() { - $this->_init(\Magento\Quote\Model\Quote\Item::class, \Magento\Quote\Model\ResourceModel\Quote\Item::class); + $this->_init(QuoteItem::class, ResourceQuoteItem::class); } /** @@ -110,7 +122,7 @@ protected function _construct() * * @return int */ - public function getStoreId() + public function getStoreId(): int { // Fallback to current storeId if no quote is provided // (see https://github.com/magento/magento2/commit/9d3be732a88884a66d667b443b3dc1655ddd0721) @@ -119,12 +131,12 @@ public function getStoreId() } /** - * Set Quote object to Collection + * Set Quote object to Collection. * - * @param \Magento\Quote\Model\Quote $quote + * @param Quote $quote * @return $this */ - public function setQuote($quote) + public function setQuote($quote): self { $this->_quote = $quote; $quoteId = $quote->getId(); @@ -138,14 +150,15 @@ public function setQuote($quote) } /** - * Reset the collection and inner join it to quotes table + * Reset the collection and inner join it to quotes table. + * * Optionally can select items with specified product id only * * @param string $quotesTableName * @param int $productId * @return $this */ - public function resetJoinQuotes($quotesTableName, $productId = null) + public function resetJoinQuotes($quotesTableName, $productId = null): self { $this->getSelect()->reset()->from( ['qi' => $this->getResource()->getMainTable()], @@ -162,11 +175,11 @@ public function resetJoinQuotes($quotesTableName, $productId = null) } /** - * After load processing + * After load processing. * * @return $this */ - protected function _afterLoad() + protected function _afterLoad(): self { parent::_afterLoad(); @@ -195,11 +208,11 @@ protected function _afterLoad() } /** - * Add options to items + * Add options to items. * * @return $this */ - protected function _assignOptions() + protected function _assignOptions(): self { $itemIds = array_keys($this->_items); $optionCollection = $this->_itemOptionCollectionFactory->create()->addItemFilter($itemIds); @@ -213,12 +226,12 @@ protected function _assignOptions() } /** - * Add products to items and item options + * Add products to items and item options. * * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - protected function _assignProducts() + protected function _assignProducts(): self { \Magento\Framework\Profiler::start('QUOTE:' . __METHOD__, ['group' => 'QUOTE', 'method' => __METHOD__]); $productCollection = $this->_productCollectionFactory->create()->setStoreId( @@ -240,46 +253,30 @@ protected function _assignProducts() ['collection' => $productCollection] ); - $recollectQuote = false; foreach ($this as $item) { + /** @var ProductInterface $product */ $product = $productCollection->getItemById($item->getProductId()); - if ($product) { + $isValidProduct = $this->isValidProduct($product); + $qtyOptions = []; + if ($isValidProduct) { $product->setCustomOptions([]); - $qtyOptions = []; - $optionProductIds = []; - foreach ($item->getOptions() as $option) { - /** - * Call type-specific logic for product associated with quote item - */ - $product->getTypeInstance()->assignProductToOption( - $productCollection->getItemById($option->getProductId()), - $option, - $product - ); - - if (is_object($option->getProduct()) && $option->getProduct()->getId() != $product->getId()) { - $optionProductIds[$option->getProduct()->getId()] = $option->getProduct()->getId(); - } - } - - if ($optionProductIds) { - foreach ($optionProductIds as $optionProductId) { - $qtyOption = $item->getOptionByCode('product_qty_' . $optionProductId); - if ($qtyOption) { - $qtyOptions[$optionProductId] = $qtyOption; - } + $optionProductIds = $this->getOptionProductIds($item, $product, $productCollection); + foreach ($optionProductIds as $optionProductId) { + $qtyOption = $item->getOptionByCode('product_qty_' . $optionProductId); + if ($qtyOption) { + $qtyOptions[$optionProductId] = $qtyOption; } } - - $item->setQtyOptions($qtyOptions)->setProduct($product); } else { $item->isDeleted(true); - $recollectQuote = true; + $this->recollectQuote = true; + } + if (!$item->isDeleted()) { + $item->setQtyOptions($qtyOptions)->setProduct($product); + $item->checkData(); } - $item->checkData(); } - - if ($recollectQuote && $this->_quote) { + if ($this->recollectQuote && $this->_quote) { $this->_quote->collectTotals(); } \Magento\Framework\Profiler::stop('QUOTE:' . __METHOD__); @@ -287,6 +284,57 @@ protected function _assignProducts() return $this; } + /** + * Get product Ids from option. + * + * @param QuoteItem $item + * @param ProductInterface $product + * @param ProductCollection $productCollection + * @return array + */ + private function getOptionProductIds( + QuoteItem $item, + ProductInterface $product, + ProductCollection $productCollection + ): array { + $optionProductIds = []; + foreach ($item->getOptions() as $option) { + /** + * Call type-specific logic for product associated with quote item + */ + $product->getTypeInstance()->assignProductToOption( + $productCollection->getItemById($option->getProductId()), + $option, + $product + ); + + if (is_object($option->getProduct()) && $option->getProduct()->getId() != $product->getId()) { + $isValidProduct = $this->isValidProduct($option->getProduct()); + if (!$isValidProduct && !$item->isDeleted()) { + $item->isDeleted(true); + $this->recollectQuote = true; + continue; + } + $optionProductIds[$option->getProduct()->getId()] = $option->getProduct()->getId(); + } + } + + return $optionProductIds; + } + + /** + * Check is valid product. + * + * @param ProductInterface $product + * @return bool + */ + private function isValidProduct(ProductInterface $product): bool + { + $result = ($product && (int)$product->getStatus() !== ProductStatus::STATUS_DISABLED); + + return $result; + } + /** * Prevents adding stock status filter to the collection of products. * diff --git a/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml b/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml new file mode 100644 index 0000000000000..dba4a94f3db2a --- /dev/null +++ b/app/code/Magento/Quote/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <!--Disabled a product by filtering grid and using change status action--> + <actionGroup name="ChangeStatusProductUsingProductGridActionGroup"> + <arguments> + <argument name="product"/> + <argument name="status" defaultValue="Enable" type="string" /> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadInitial"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickChangeStatusAction"/> + <click selector="{{AdminProductGridSection.changeStatus('status')}}" stepKey="clickChangeStatusDisabled" parameterized="true"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been updated." stepKey="seeSuccessMessage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.xml new file mode 100644 index 0000000000000..32ac73aca7c03 --- /dev/null +++ b/app/code/Magento/Quote/Test/Mftf/Section/AdminProductGridSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminProductGridSection"> + <element name="changeStatus" selector="//div[contains(@class,'admin__data-grid-header-row') and contains(@class, 'row')]//div[contains(@class, 'action-menu-item')]//ul/li/span[text() = '{{status}}']" stepKey="clickChangeStatus" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml new file mode 100644 index 0000000000000..0c20eb54d5446 --- /dev/null +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontGuestCheckoutDisabledProductTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via the Storefront"/> + <title value="Remove item from cart if product disabled"/> + <description value="Remove item from cart if simple or configurable product is disabled"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-95844"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create the configurable product based on the data in the /data folder --> + <createData entity="BaseConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Make the configurable product have two options, that are children of the default attribute set --> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="SimpleOption" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="SimpleOption" stepKey="createConfigChildProduct2"> + <field key="sku">SimpleTwoOption</field> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <!-- Assign the two products to the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + </after> + <!-- Step 1: Add simple product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="goToConfigProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontLoad"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectOption"/> + <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$createConfigProduct.name$$)}}" time="30" stepKey="assertMessage"/> + <!--Disabled via admin panel--> + <openNewTab stepKey="openNewTab"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Find the first simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <argument name="product" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForFiltersToBeApplied"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <!-- Disabled child configurable product --> + <click selector="{{AdminProductFormSection.enableProductAttributeLabel}}" stepKey="clickDisableProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + <waitForPageLoad stepKey="waitForProductPageSaved"/> + <!-- Disabled simple product from grid --> + <actionGroup ref="ChangeStatusProductUsingProductGridActionGroup" stepKey="disabledProductFromGrid"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="status" value="Disable"/> + </actionGroup> + <closeTab stepKey="closeTab"/> + <!--Check cart--> + <reloadPage stepKey="reloadPage"/> + <waitForPageLoad stepKey="waitForCheckoutPageReload2"/> + <click selector="{{StorefrontMiniCartSection.show}}" stepKey="clickMiniCart"/> + <dontSeeElement selector="{{StorefrontMiniCartSection.quantity}}" stepKey="dontSeeCartItem"/> + </test> +</tests> diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index ecc6426855679..8add3786eb9a3 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -96,6 +96,9 @@ <plugin name="clean_quote_items_after_product_delete" type="Magento\Quote\Model\Product\Plugin\RemoveQuoteItems"/> <plugin name="update_quote_items_after_product_save" type="Magento\Quote\Model\Product\Plugin\UpdateQuoteItems"/> </type> + <type name="Magento\Catalog\Model\Product\Action"> + <plugin name="quoteProductMassChange" type="Magento\Quote\Model\Product\Plugin\MarkQuotesRecollectMassDisabled"/> + </type> <type name="Magento\Quote\Model\ValidationRules\QuoteValidationComposite"> <arguments> <argument name="validationRules" xsi:type="array"> From 3b440e918fb40f659c952d135c6e08e73c9685ef Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sat, 13 Oct 2018 21:28:41 +0300 Subject: [PATCH 160/240] Fix the typo in PHPDoc comment --- .../Backend/Model/Config/SessionLifetime/BackendModel.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php b/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php index 09f33abd0d44d..d658ca235ca8d 100644 --- a/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php +++ b/app/code/Magento/Backend/Model/Config/SessionLifetime/BackendModel.php @@ -15,14 +15,17 @@ */ class BackendModel extends Value { - /** Maximum dmin session lifetime; 1 year*/ + /** Maximum admin session lifetime; 1 year*/ const MAX_LIFETIME = 31536000; /** Minimum admin session lifetime */ const MIN_LIFETIME = 60; /** + * Processing object before save data + * * @since 100.1.0 + * @throws LocalizedException */ public function beforeSave() { From 104ed4863d5281530c761af2f1a69b2d1ab2f648 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Fri, 12 Oct 2018 09:19:00 +0300 Subject: [PATCH 161/240] Add unit test for ModuleService --- .../Unit/Service/V1/ModuleServiceTest.php | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php diff --git a/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php b/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php new file mode 100644 index 0000000000000..db103c0de05d5 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Test\Unit\Model; + +use Magento\Backend\Service\V1\ModuleService; +use Magento\Framework\Module\ModuleListInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * Module List Service Test + * + * Covers \Magento\Sales\Model\ValidatorResultMerger + */ +class ValidatorResultMergerTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var ModuleService + */ + private $moduleService; + + /** + * @var ModuleListInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $moduleListMock; + + /** + * Object Manager + * + * @var ObjectManager + */ + private $objectManager; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->moduleListMock = $this->createMock(ModuleListInterface::class); + $this->objectManager = new ObjectManager($this); + $this->moduleService = $this->objectManager->getObject( + ModuleService::class, + [ + 'moduleList' => $this->moduleListMock, + ] + ); + } + + /** + * Test getModules method + * + * @return void + */ + public function testGetModules() + { + $moduleNames = ['Magento_Backend', 'Magento_Catalog', 'Magento_Customer']; + $this->moduleListMock->expects($this->once())->method('getNames')->willReturn($moduleNames); + + $expected = $moduleNames; + $actual = $this->moduleService->getModules(); + $this->assertEquals($expected, $actual); + } +} From 9d0b7356c84fd6401c4ff7f45d6fb63c2ce257b9 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Fri, 12 Oct 2018 09:20:10 +0300 Subject: [PATCH 162/240] Fix the class name --- .../Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php b/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php index db103c0de05d5..f86747aac1259 100644 --- a/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php +++ b/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php @@ -16,7 +16,7 @@ * * Covers \Magento\Sales\Model\ValidatorResultMerger */ -class ValidatorResultMergerTest extends \PHPUnit\Framework\TestCase +class ModuleServiceTest extends \PHPUnit\Framework\TestCase { /** * Testable Object From 4503707cdbed7a29e83c700c47b70cc8aea2f12a Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Fri, 12 Oct 2018 13:26:05 +0300 Subject: [PATCH 163/240] Fixed the namespace --- .../Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php b/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php index f86747aac1259..c7ff1d95617b6 100644 --- a/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php +++ b/app/code/Magento/Backend/Test/Unit/Service/V1/ModuleServiceTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Backend\Test\Unit\Model; +namespace Magento\Backend\Test\Unit\Service\V1; use Magento\Backend\Service\V1\ModuleService; use Magento\Framework\Module\ModuleListInterface; From a12039c642716207e970a7eb3dab005eb4b493af Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Tue, 23 Oct 2018 14:22:28 +0300 Subject: [PATCH 164/240] MAGETWO-85932: Product pages that are part of a related products rule that uses Price (percentage) condition load blank page --- .../Mftf/Data/CatalogAttributeSetData.xml | 16 ++++++++++++ .../Catalog/Test/Mftf/Data/ProductData.xml | 14 +++++++++++ .../Metadata/catalog_attribute_set-meta.xml | 25 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml new file mode 100644 index 0000000000000..67e71cc3b9e92 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogAttributeSetData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CatalogAttributeSet" type="CatalogAttributeSet"> + <data key="attribute_set_name" unique="suffix">test_set_</data> + <data key="attributeGroupId">7</data> + <data key="skeletonId">4</data> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 35e6d198c7f54..007529a06d9f4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -249,4 +249,18 @@ <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionDropDownWithLongValuesTitle</requiredEntity> </entity> + <entity name="SimpleProductWithCustomAttributeSet" type="product"> + <data key="sku" unique="suffix">testSku</data> + <data key="type_id">simple</data> + <var key="attribute_set_id" entityKey="attribute_set_id" entityType="CatalogAttributeSet"/> + <data key="visibility">4</data> + <data key="name" unique="suffix">testProductName</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">testurlkey</data> + <data key="status">1</data> + <data key="weight">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml new file mode 100644 index 0000000000000..00a5f8fef18f1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_attribute_set-meta.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="AddCatalogAttributeToAttributeSet" dataType="CatalogAttributeSet" type="create" auth="adminOauth" url="/V1/products/attribute-sets" method="POST"> + <contentType>application/json</contentType> + <object key="attributeSet" dataType="CatalogAttributeSet"> + <field key="attribute_set_name">string</field> + <field key="sort_order">integer</field> + </object> + <field key="skeletonId">integer</field> + </operation> + <operation name="DeleteCatalogAttributeFromAttributeSet" dataType="CatalogAttributeSet" type="delete" auth="adminOauth" url="/V1/products/attribute-sets/{attribute_set_id}" method="DELETE"> + <contentType>application/json</contentType> + </operation> + <operation name="GetCatalogAttributesFromDefaultSet" dataType="CatalogAttributeSet" type="get" auth="adminOauth" url="/V1/products/attribute-sets/{attribute_set_id}" method="GET"> + <contentType>application/json</contentType> + </operation> +</operations> From 82d203380a36d33a1bf18031b8afdd14032c9c5e Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Tue, 23 Oct 2018 17:01:14 +0300 Subject: [PATCH 165/240] MAGETWO-85932: Product pages that are part of a related products rule that uses Price (percentage) condition load blank page --- ...ductInStorefrontProductPageActionGroup.xml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..fc2a8964ea302 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey"> + <arguments> + <argument name="product"/> + </arguments> + <!-- Go to storefront product page, assert product name and sku --> + <amOnPage url="{{product.custom_attributes[url_key]}}.html" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <seeInTitle userInput="{{product.name}}" stepKey="assertProductNameTitle"/> + <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> + </actionGroup> +</actionGroups> From d2fadf79a635841c4a76a14ba6e04d61bd766784 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 24 Oct 2018 17:24:30 +0300 Subject: [PATCH 166/240] MAGETWO-95901: [TSG] Pull Request 50 stabilization --- .../AdminFilteringCategoryProductsUsingScopeSelectorTest.xml | 4 ++++ .../Test/StorefrontPurchaseProductWithCustomOptionsTest.xml | 3 ++- ...ontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml | 1 + .../Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml | 1 + .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index 3f69b5cf70fac..2431d626387e3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -129,6 +129,8 @@ <waitForPageLoad stepKey="waitForCategoryPageLoad1"/> <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewOption('Default Store View')}}" stepKey="clickStoreView"/> + <waitForElementVisible selector="{{AdminCategoryMainActionsSection.categoryStoreViewModalAccept}}" + stepKey="waitForPopup1"/> <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewModalAccept}}" stepKey="clickActionAccept"/> <waitForPageLoad stepKey="waitForCategoryPageLoad2"/> <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection1"/> @@ -148,6 +150,8 @@ <waitForPageLoad stepKey="waitForCategoryPageLoad3"/> <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewOption(secondStore.name)}}" stepKey="clickStoreView1"/> + <waitForElementVisible selector="{{AdminCategoryMainActionsSection.categoryStoreViewModalAccept}}" + stepKey="waitForPopup2"/> <click selector="{{AdminCategoryMainActionsSection.categoryStoreViewModalAccept}}" stepKey="clickActionAccept1"/> <waitForPageLoad stepKey="waitForCategoryPageLoad4"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml index e92c47d2a3f48..31ed251a34795 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml @@ -120,6 +120,7 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> <fillField selector="{{OrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{OrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> @@ -176,4 +177,4 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index 714ea833d5b0b..b5f7ddd8f00cf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -65,6 +65,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml index bb6c19ac7fe47..dff957457da95 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml @@ -63,6 +63,7 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> <fillField selector="{{OrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{OrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index eaad9912c6de1..82ac6d9515ce6 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -61,6 +61,7 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onAdminOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> <fillField selector="{{OrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> <click selector="{{OrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> From 194429936730a6350d1f2cde0e09bcaf6f963a35 Mon Sep 17 00:00:00 2001 From: Magently <hello@magently.com> Date: Thu, 25 Oct 2018 11:25:33 +0200 Subject: [PATCH 167/240] Separate the logic of checking if the attribute is the price in Catalog EavModifier --- .../Product/Form/Modifier/Eav.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) 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 61cf6bb19b66e..65aaa003f1370 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 @@ -399,10 +399,7 @@ public function modifyData(array $data) foreach ($attributes as $attribute) { if (null !== ($attributeValue = $this->setupAttributeData($attribute))) { - if ($attribute->getFrontendInput() === 'price' - && is_scalar($attributeValue) - && !$this->isBundleSpecialPrice($attribute) - ) { + if ($this->isPriceAttribute($attribute, $attributeValue)) { $attributeValue = $this->formatPrice($attributeValue); } $data[$productId][self::DATA_SOURCE_DEFAULT][$attribute->getAttributeCode()] = $attributeValue; @@ -413,6 +410,20 @@ public function modifyData(array $data) return $data; } + /** + * Obtain if given attribute is a price + * + * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @param string|integer $attributeValue + * @return bool + */ + private function isPriceAttribute(ProductAttributeInterface $attribute, $attributeValue) + { + return $attribute->getFrontendInput() === 'price' + && is_scalar($attributeValue) + && !$this->isBundleSpecialPrice($attribute); + } + /** * Obtain if current product is bundle and given attribute is special_price * From 413ad52dda901de30f591c34f9228177df646e54 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 25 Oct 2018 12:30:43 +0300 Subject: [PATCH 168/240] MAGETWO-95914: [FT] Magento\Reports\Test\TestCase\ProductsInCartReportEntityTest.test with data set "ProductsInCartReportEntityVariation1_0" --- .../Reports/Test/TestCase/ProductsInCartReportEntityTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/ProductsInCartReportEntityTest.php b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/ProductsInCartReportEntityTest.php index 30e790f978c42..1a4cb787bf1c7 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/ProductsInCartReportEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/ProductsInCartReportEntityTest.php @@ -102,10 +102,12 @@ public function test( $productUrl = $_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'; $browser->open($productUrl); $this->catalogProductView->getViewBlock()->addToCart($product); + $this->catalogProductView->getMessagesBlock()->waitSuccessMessage(); if ($isGuest) { $this->objectManager->create(\Magento\Customer\Test\TestStep\LogoutCustomerOnFrontendStep::class)->run(); $browser->open($productUrl); $this->catalogProductView->getViewBlock()->addToCart($product); + $this->catalogProductView->getMessagesBlock()->waitSuccessMessage(); } } From f0d6a81955c7d7992efc04985ca579974db169f7 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Thu, 25 Oct 2018 13:20:46 +0300 Subject: [PATCH 169/240] MAGETWO-93947: Recently viewed block shows the product which is currenly open --- .../view/frontend/web/js/product/provider.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/product/provider.js b/app/code/Magento/Catalog/view/frontend/web/js/product/provider.js index c53b2fa6e2a7a..b29ebe7d57d1c 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/product/provider.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/product/provider.js @@ -5,11 +5,13 @@ define([ 'underscore', + 'jquery', 'mageUtils', 'uiElement', 'Magento_Catalog/js/product/storage/storage-service', - 'Magento_Customer/js/customer-data' -], function (_, utils, Element, storage, customerData) { + 'Magento_Customer/js/customer-data', + 'Magento_Catalog/js/product/view/product-ids-resolver' +], function (_, $, utils, Element, storage, customerData, productResolver) { 'use strict'; return Element.extend({ @@ -135,11 +137,16 @@ define([ */ filterIds: function (ids) { var _ids = {}, - currentTime = new Date().getTime() / 1000; + currentTime = new Date().getTime() / 1000, + currentProductIds = productResolver($('#product_addtocart_form')); _.each(ids, function (id) { - if (currentTime - id['added_at'] < ~~this.idsStorage.lifetime) { + if ( + currentTime - id['added_at'] < ~~this.idsStorage.lifetime && + !_.contains(currentProductIds, id['product_id']) + ) { _ids[id['product_id']] = id; + } }, this); From 102196bfd0069189da530d6bde43862c16732f4f Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Thu, 25 Oct 2018 16:25:23 +0300 Subject: [PATCH 170/240] MAGETWO-93988: Product Review "Save and Next" and "Save and Previous" not working --- .../Review/Product/Collection.php | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php b/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php index 99c963501a9d4..4e55484e5dd94 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php +++ b/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Review\Model\ResourceModel\Review\Product; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; @@ -403,12 +404,20 @@ public function getAllIds($limit = null, $offset = null) public function getResultingIds() { $idsSelect = clone $this->getSelect(); - $idsSelect->reset(Select::LIMIT_COUNT); - $idsSelect->reset(Select::LIMIT_OFFSET); - $idsSelect->reset(Select::COLUMNS); - $idsSelect->reset(Select::ORDER); - $idsSelect->columns('rt.review_id'); - return $this->getConnection()->fetchCol($idsSelect); + $data = $this->getConnection() + ->fetchAll( + $idsSelect + ->reset(Select::LIMIT_COUNT) + ->reset(Select::LIMIT_OFFSET) + ->columns('rt.review_id') + ); + + return array_map( + function ($value) { + return $value['review_id']; + }, + $data + ); } /** From bf700aa60fe7a958ef5fe828f3de931e799af2fc Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Fri, 26 Oct 2018 01:54:57 +0530 Subject: [PATCH 171/240] Issue #18150 fixed for 2.2.6 --- app/code/Magento/Backup/Controller/Adminhtml/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index.php b/app/code/Magento/Backup/Controller/Adminhtml/Index.php index dcafbc7370d2d..142211fe90a44 100644 --- a/app/code/Magento/Backup/Controller/Adminhtml/Index.php +++ b/app/code/Magento/Backup/Controller/Adminhtml/Index.php @@ -19,7 +19,7 @@ abstract class Index extends \Magento\Backend\App\Action * * @see _isAllowed() */ - const ADMIN_RESOURCE = 'Magento_Backend::backup'; + const ADMIN_RESOURCE = 'Magento_Backup::backup'; /** * Core registry From d43759cb99c704e0e16635dffc6452cccefcbe5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Ca=C3=A7ador?= <20863811+samuel27m@users.noreply.github.com> Date: Thu, 25 Oct 2018 23:37:05 +0100 Subject: [PATCH 172/240] Backport [PR 18772] Remove unnecesary "header" block redeclaration Backport [PR 18772] Remove unnecesary header block redeclaration on Magento Luma Theme, which made impossible to set the header template through custom modules. --- .../Magento/luma/Magento_Customer/layout/default.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Customer/layout/default.xml b/app/design/frontend/Magento/luma/Magento_Customer/layout/default.xml index 1f8c162ef923a..4b08bf28ece9f 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/layout/default.xml +++ b/app/design/frontend/Magento/luma/Magento_Customer/layout/default.xml @@ -15,11 +15,6 @@ </arguments> </block> </referenceBlock> - <block class="Magento\Theme\Block\Html\Header" name="header" as="header"> - <arguments> - <argument name="show_part" xsi:type="string">welcome</argument> - </arguments> - </block> <move element="header" destination="header.links" before="-"/> <move element="register-link" destination="header.links"/> <move element="top.links" destination="customer"/> From 9d838f95c8cda6a5e60ffce1ac18fe75f2c81e95 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 26 Oct 2018 09:24:10 +0300 Subject: [PATCH 173/240] MAGETWO-95890: [FT] Magento\Checkout\Test\TestCase\AddProductsToShoppingCartEntityTest failed on variation "AddProductsToShoppingCartEntityTestVariation5_0" --- .../app/Magento/Checkout/Test/Block/Cart.php | 17 ++++++ .../Checkout/Test/Block/Cart/Shipping.php | 54 +++++++++++++++++++ .../Checkout/Test/Block/Cart/Totals.php | 11 ++++ .../Constraint/AssertCartItemsOptions.php | 5 ++ .../AssertGrandTotalInShoppingCart.php | 4 ++ .../Constraint/AssertPriceInShoppingCart.php | 5 ++ .../AssertProductQtyInShoppingCart.php | 5 ++ .../AssertSubtotalInShoppingCart.php | 4 ++ .../Constraint/Utils/CartPageLoadTrait.php | 25 +++++++++ ...eProductFromMiniShoppingCartEntityTest.php | 1 + ...oveShoppingCartProductsOnOrderPageTest.php | 1 + 11 files changed, 132 insertions(+) create mode 100644 dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/Utils/CartPageLoadTrait.php diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php index d1a6ce2d07f2b..86624001756b0 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php @@ -113,6 +113,13 @@ class Cart extends Block */ private $popupWindowContent = '#main'; + /** + * Locator for page with ajax loading state. + * + * @var string + */ + private $ajaxLoading = 'body.ajax-loading'; + /** * Wait for PayPal page is loaded. * @@ -295,4 +302,14 @@ public function waitForCheckoutButton() { $this->waitForElementVisible($this->inContextPaypalCheckoutButton); } + + /** + * Wait loading. + * + * @return void + */ + public function waitForLoader() + { + $this->waitForElementNotVisible($this->ajaxLoading); + } } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Shipping.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Shipping.php index 10299486a08ce..3d293700db8c9 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Shipping.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Shipping.php @@ -72,6 +72,13 @@ class Shipping extends Form */ protected $commonShippingPriceSelector = '.totals.shipping .price'; + /** + * Estimate shipping and tax form locator. + * + * @var string + */ + private $estimateShippingForm = '#shipping-zip-form'; + /** * Open estimate shipping and tax form. * @@ -250,4 +257,51 @@ public function waitForCommonShippingPriceBlock() { $this->waitForElementVisible($this->commonShippingPriceSelector, Locator::SELECTOR_CSS); } + + /** + * Wait until estimation form to appear. + * + * @return void + */ + public function waitForEstimateShippingAndTaxForm() + { + $browser = $this->browser; + $selector = $this->estimateShippingForm; + + $browser->waitUntil( + function () use ($browser, $selector) { + $element = $browser->find($selector); + return $element->isPresent() ? true : null; + } + ); + } + + /** + * Wait for shipping method form. + * + * @return void + */ + public function waitForShippingMethodForm() + { + $browser = $this->browser; + $selector = $this->shippingMethodForm; + + $browser->waitUntil( + function () use ($browser, $selector) { + $element = $browser->find($selector); + return $element->isPresent() ? true : null; + } + ); + } + + /** + * Wait for summary block to be loaded. + * + * @return void + */ + public function waitForSummaryBlock() + { + $this->waitForEstimateShippingAndTaxForm(); + $this->waitForShippingMethodForm(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Totals.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Totals.php index fda29a2fe78fc..f632cdc3d7464 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Totals.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Totals.php @@ -264,4 +264,15 @@ public function waitForShippingPriceBlock() { $this->waitForElementVisible($this->shippingPriceBlockSelector, Locator::SELECTOR_CSS); } + + /** + * Wait for "Grand Total" row to appear. + * + * @return void + */ + public function waitForGrandTotal() + { + $this->waitForUpdatedTotals(); + $this->waitForElementVisible($this->grandTotal); + } } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCartItemsOptions.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCartItemsOptions.php index a4da0c0ce604d..ac6889323ba00 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCartItemsOptions.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCartItemsOptions.php @@ -7,6 +7,7 @@ namespace Magento\Checkout\Test\Constraint; use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Checkout\Test\Constraint\Utils\CartPageLoadTrait; use Magento\Checkout\Test\Fixture\Cart; use Magento\Checkout\Test\Fixture\Cart\Items; use Magento\Checkout\Test\Page\CheckoutCart; @@ -21,6 +22,8 @@ */ class AssertCartItemsOptions extends AbstractAssertForm { + use CartPageLoadTrait; + /** * Error message for verify options * @@ -44,6 +47,8 @@ class AssertCartItemsOptions extends AbstractAssertForm public function processAssert(CheckoutCart $checkoutCart, Cart $cart) { $checkoutCart->open(); + $this->waitForCartPageLoaded($checkoutCart); + /** @var Items $sourceProducts */ $sourceProducts = $cart->getDataFieldConfig('items')['source']; $products = $sourceProducts->getProducts(); diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertGrandTotalInShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertGrandTotalInShoppingCart.php index 8806f25c3fddd..7ced144b10f46 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertGrandTotalInShoppingCart.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertGrandTotalInShoppingCart.php @@ -6,6 +6,7 @@ namespace Magento\Checkout\Test\Constraint; +use Magento\Checkout\Test\Constraint\Utils\CartPageLoadTrait; use Magento\Checkout\Test\Fixture\Cart; use Magento\Checkout\Test\Page\CheckoutCart; use Magento\Mtf\Constraint\AbstractConstraint; @@ -16,6 +17,8 @@ */ class AssertGrandTotalInShoppingCart extends AbstractConstraint { + use CartPageLoadTrait; + /** * Assert that grand total is equal to expected * @@ -28,6 +31,7 @@ public function processAssert(CheckoutCart $checkoutCart, Cart $cart, $requireRe { if ($requireReload) { $checkoutCart->open(); + $this->waitForCartPageLoaded($checkoutCart); $checkoutCart->getTotalsBlock()->waitForUpdatedTotals(); } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPriceInShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPriceInShoppingCart.php index 799fced1210dd..b913fd83cc074 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPriceInShoppingCart.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertPriceInShoppingCart.php @@ -7,6 +7,7 @@ namespace Magento\Checkout\Test\Constraint; use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Checkout\Test\Constraint\Utils\CartPageLoadTrait; use Magento\Checkout\Test\Fixture\Cart; use Magento\Checkout\Test\Fixture\Cart\Items; use Magento\Checkout\Test\Page\CheckoutCart; @@ -19,6 +20,8 @@ */ class AssertPriceInShoppingCart extends AbstractAssertForm { + use CartPageLoadTrait; + /** * Assert that price in the shopping cart equals to expected price from data set * @@ -29,6 +32,8 @@ class AssertPriceInShoppingCart extends AbstractAssertForm public function processAssert(CheckoutCart $checkoutCart, Cart $cart) { $checkoutCart->open(); + $this->waitForCartPageLoaded($checkoutCart); + /** @var Items $sourceProducts */ $sourceProducts = $cart->getDataFieldConfig('items')['source']; $products = $sourceProducts->getProducts(); diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInShoppingCart.php index f128905e74a0a..a343c2d8f466c 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInShoppingCart.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertProductQtyInShoppingCart.php @@ -7,6 +7,7 @@ namespace Magento\Checkout\Test\Constraint; use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Checkout\Test\Constraint\Utils\CartPageLoadTrait; use Magento\Checkout\Test\Fixture\Cart; use Magento\Checkout\Test\Fixture\Cart\Items; use Magento\Checkout\Test\Page\CheckoutCart; @@ -19,6 +20,8 @@ */ class AssertProductQtyInShoppingCart extends AbstractAssertForm { + use CartPageLoadTrait; + /** * Assert that quantity in the shopping cart is equals to expected quantity from data set * @@ -29,6 +32,8 @@ class AssertProductQtyInShoppingCart extends AbstractAssertForm public function processAssert(CheckoutCart $checkoutCart, Cart $cart) { $checkoutCart->open(); + $this->waitForCartPageLoaded($checkoutCart); + /** @var Items $sourceProducts */ $sourceProducts = $cart->getDataFieldConfig('items')['source']; $products = $sourceProducts->getProducts(); diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertSubtotalInShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertSubtotalInShoppingCart.php index cd6e29410d571..fe593ccc76c6f 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertSubtotalInShoppingCart.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertSubtotalInShoppingCart.php @@ -7,6 +7,7 @@ namespace Magento\Checkout\Test\Constraint; use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Checkout\Test\Constraint\Utils\CartPageLoadTrait; use Magento\Checkout\Test\Fixture\Cart; use Magento\Checkout\Test\Fixture\Cart\Items; use Magento\Checkout\Test\Page\CheckoutCart; @@ -19,6 +20,8 @@ */ class AssertSubtotalInShoppingCart extends AbstractAssertForm { + use CartPageLoadTrait; + /** * Assert that subtotal total in the shopping cart is equals to expected total from data set * @@ -31,6 +34,7 @@ public function processAssert(CheckoutCart $checkoutCart, Cart $cart, $requireRe { if ($requireReload) { $checkoutCart->open(); + $this->waitForCartPageLoaded($checkoutCart); } /** @var Items $sourceProducts */ diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/Utils/CartPageLoadTrait.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/Utils/CartPageLoadTrait.php new file mode 100644 index 0000000000000..8f4eef3e6be0e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/Utils/CartPageLoadTrait.php @@ -0,0 +1,25 @@ +<?php + +namespace Magento\Checkout\Test\Constraint\Utils; + +use Magento\Checkout\Test\Fixture\Cart; +use Magento\Checkout\Test\Page\CheckoutCart; + +/** + * Check if cart page is fully loaded. + */ +trait CartPageLoadTrait +{ + /** + * @param CheckoutCart $checkoutCart + * @return void + */ + public function waitForCartPageLoaded(CheckoutCart $checkoutCart) + { + $checkoutCart->getCartBlock()->waitForLoader(); + if (!$checkoutCart->getCartBlock()->cartIsEmpty()) { + $checkoutCart->getShippingBlock()->waitForSummaryBlock(); + $checkoutCart->getTotalsBlock()->waitForGrandTotal(); + } + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.php index 5935a68c43c21..af267cfa30ec1 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.php @@ -115,6 +115,7 @@ public function test( } else { $miniShoppingCart->getCartItem($newProduct)->clickEditItem(); $this->catalogProductView->getViewBlock()->addToCart($newProduct); + $this->catalogProductView->getMessagesBlock()->waitSuccessMessage(); } // Prepare data for asserts: $cart['data']['items'] = ['products' => [$newProduct]]; diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveShoppingCartProductsOnOrderPageTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveShoppingCartProductsOnOrderPageTest.php index 2b0bd7178cdad..91b4f711700b5 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveShoppingCartProductsOnOrderPageTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveShoppingCartProductsOnOrderPageTest.php @@ -144,6 +144,7 @@ public function test(Customer $customer, $product) )->run(); $this->browser->open($_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'); $this->catalogProductView->getViewBlock()->addToCart($product); + $this->catalogProductView->getMessagesBlock()->waitSuccessMessage(); //Steps $this->customerIndex->open(); From f5aa7858232919139db2a9ee22d9f79076341042 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 26 Oct 2018 12:01:32 +0300 Subject: [PATCH 174/240] MAGETWO-95890: [FT] Magento\Checkout\Test\TestCase\AddProductsToShoppingCartEntityTest failed on variation "AddProductsToShoppingCartEntityTestVariation5_0" --- .../Checkout/Test/Constraint/Utils/CartPageLoadTrait.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/Utils/CartPageLoadTrait.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/Utils/CartPageLoadTrait.php index 8f4eef3e6be0e..6b36e0ae65a3a 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/Utils/CartPageLoadTrait.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/Utils/CartPageLoadTrait.php @@ -1,8 +1,11 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ namespace Magento\Checkout\Test\Constraint\Utils; -use Magento\Checkout\Test\Fixture\Cart; use Magento\Checkout\Test\Page\CheckoutCart; /** From 528ec3ecf8b60ba373d7631a11fdba569859fca0 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Fri, 19 Oct 2018 22:03:15 +0300 Subject: [PATCH 175/240] Cover \Magento\GiftMessage\Observer\SalesEventQuoteMerge with Unit test --- .../Observer/SalesEventQuoteMergeTest.php | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php diff --git a/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php new file mode 100644 index 0000000000000..f82bc01c485e9 --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\GiftMessage\Test\Unit\Observer; + +use Magento\GiftMessage\Observer\SalesEventQuoteMerge; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use \Magento\Framework\Event\Observer; +use Magento\Quote\Model\Quote; + +/** + * SalesEventQuoteMergeTest + */ +class SalesEventQuoteMergeTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var SalesEventQuoteMerge + */ + private $salesEventQuoteMerge; + + /** + * @return void + */ + public function setUp(): void + { + $objectManger = new ObjectManager($this); + $this->salesEventQuoteMerge = $objectManger->getObject(SalesEventQuoteMerge::class); + } + + /** + * @dataProvider dataProviderGiftMessageId + * + * @param null|int $giftMessageId + * + * @return void + */ + public function testExecute($giftMessageId): void + { + $sourceQuoteMock = $this->createPartialMock(Quote::class, ['getGiftMessageId']); + $sourceQuoteMock->expects($this->once()) + ->method('getGiftMessageId') + ->willReturn($giftMessageId); + + $targetQuoteMock = $this->createPartialMock(Quote::class, ['setGiftMessageId']); + + if ($giftMessageId) { + $targetQuoteMock->expects($this->once()) + ->method('setGiftMessageId'); + } else { + $targetQuoteMock->expects($this->never()) + ->method('setGiftMessageId'); + } + + $observer = $this->createMock(Observer::class); + $observer->expects($this->exactly(2)) + ->method('getData') + ->willReturnMap([ + ['quote', null, $targetQuoteMock], + ['source', null, $sourceQuoteMock] + ]); + + $this->salesEventQuoteMerge->execute($observer); + } + + /** + * @return array + */ + public function dataProviderGiftMessageId(): array + { + return [ + [null], + [1] + ]; + } +} From a5a93ad86fa71b6baa0d5b66f9bfc65837975dd6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 22 Oct 2018 15:34:12 +0200 Subject: [PATCH 176/240] Minor code style fixes --- .../GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php index f82bc01c485e9..7a3000f7c0743 100644 --- a/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php +++ b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php @@ -10,7 +10,7 @@ use Magento\GiftMessage\Observer\SalesEventQuoteMerge; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use \Magento\Framework\Event\Observer; +use Magento\Framework\Event\Observer; use Magento\Quote\Model\Quote; /** From ff8cf5d1c4a6109675afd021f4ed80dc04f97e70 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Fri, 19 Oct 2018 21:07:16 +0300 Subject: [PATCH 177/240] Cover \Magento\Email\Model\Template\SenderResolver class with Unit test --- .../Model/Template/SenderResolverTest.php | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 app/code/Magento/Email/Test/Unit/Model/Template/SenderResolverTest.php diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/SenderResolverTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/SenderResolverTest.php new file mode 100644 index 0000000000000..fb25290f9d27b --- /dev/null +++ b/app/code/Magento/Email/Test/Unit/Model/Template/SenderResolverTest.php @@ -0,0 +1,112 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Email\Test\Unit\Model\Template; + +use Magento\Email\Model\Template\SenderResolver; +use Magento\Framework\Exception\MailException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\App\Config\ScopeConfigInterface; + +/** + * SenderResolverTest + */ +class SenderResolverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var SenderResolver + */ + private $senderResolver; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + + /** + * @return void + */ + public function setUp(): void + { + $objectManager = new ObjectManager($this); + + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + + $this->senderResolver = $objectManager->getObject( + SenderResolver::class, + [ + 'scopeConfig' => $this->scopeConfig + ] + ); + } + + /** + * Test returned information for given sender's name and email + * + * @return void + */ + public function testResolve(): void + { + $sender = 'general'; + $scopeId = null; + + $this->scopeConfig->expects($this->exactly(2)) + ->method('getValue') + ->willReturnMap([ + [ + 'trans_email/ident_' . $sender . '/name', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $scopeId, + 'Test Name' + ], + [ + 'trans_email/ident_' . $sender . '/email', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $scopeId, + 'test@email.com' + ] + ]); + + $result = $this->senderResolver->resolve($sender); + + $this->assertTrue(isset($result['name'])); + $this->assertEquals('Test Name', $result['name']); + + $this->assertTrue(isset($result['email'])); + $this->assertEquals('test@email.com', $result['email']); + } + + /** + * Test if exception is thrown in case there is no name or email in result + * + * @dataProvider dataProvidedSenderArray + * @param array $sender + * + * @return void + */ + public function testResolveThrowException(array $sender): void + { + $this->expectExceptionMessage('Invalid sender data'); + $this->expectException(MailException::class); + $this->senderResolver->resolve($sender); + } + + /** + * @return array + */ + public function dataProvidedSenderArray() + { + return [ + [ + ['name' => 'Name'] + ], + [ + ['email' => 'test@email.com'] + ] + ]; + } +} From 4f5cb9b842b3f09dc2f9c4727a088555b1436e12 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Fri, 19 Oct 2018 09:51:12 +0300 Subject: [PATCH 178/240] Added Unit Test for WindowsSmtpConfig Plugin --- .../Model/Plugin/WindowsSmtpConfigTest.php | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 app/code/Magento/Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php diff --git a/app/code/Magento/Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php b/app/code/Magento/Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php new file mode 100644 index 0000000000000..5f7c44b988c66 --- /dev/null +++ b/app/code/Magento/Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Email\Test\Unit\Model\Plugin; + +use Magento\Email\Model\Plugin\WindowsSmtpConfig; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\Mail\TransportInterface; +use Magento\Framework\OsInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * WindowsSmtpConfigTest + */ +class WindowsSmtpConfigTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var WindowsSmtpConfig + */ + private $windowsSmtpConfig; + + /** + * @var OsInfo|\PHPUnit_Framework_MockObject_MockObject + */ + private $osInfoMock; + + /** + * @var ReinitableConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + /** + * @var TransportInterface + */ + private $transportMock; + + /** + * setUp + * + * @return void + */ + public function setUp(): void + { + $objectManager = new ObjectManager($this); + + $this->osInfoMock = $this->createMock(OsInfo::class); + $this->configMock = $this->createMock(ReinitableConfigInterface::class); + $this->transportMock = $this->createMock(TransportInterface::class); + + $this->windowsSmtpConfig = $objectManager->getObject( + WindowsSmtpConfig::class, + [ + 'config' => $this->configMock, + 'osInfo' => $this->osInfoMock + ] + ); + } + + /** + * Test if SMTP settings if windows server + * + * @return void + */ + public function testBeforeSendMessageOsWindows(): void + { + $this->osInfoMock->expects($this->once()) + ->method('isWindows') + ->willReturn(true); + + $this->configMock->expects($this->exactly(2)) + ->method('getValue') + ->willReturnMap([ + [WindowsSmtpConfig::XML_SMTP_HOST, '127.0.0.1'], + [WindowsSmtpConfig::XML_SMTP_PORT, '80'] + ]); + + $this->windowsSmtpConfig->beforeSendMessage($this->transportMock); + } + + /** + * Test if SMTP settings if not windows server + * + * @return void + */ + public function testBeforeSendMessageOsIsWindows(): void + { + $this->osInfoMock->expects($this->once()) + ->method('isWindows') + ->willReturn(false); + + $this->configMock->expects($this->never()) + ->method('getValue'); + + $this->windowsSmtpConfig->beforeSendMessage($this->transportMock); + } +} From 8d801cbe65ecd9c48bf5a697e78b7abf9a95cc9f Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 26 Oct 2018 17:42:03 +0300 Subject: [PATCH 179/240] MAGETWO-95890: [FT] Magento\Checkout\Test\TestCase\AddProductsToShoppingCartEntityTest failed on variation "AddProductsToShoppingCartEntityTestVariation5_0" --- .../Constraint/AssertBundleProductOnConfigureCartPage.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleProductOnConfigureCartPage.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleProductOnConfigureCartPage.php index 8828f31f9965a..f71c55be8c9fc 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleProductOnConfigureCartPage.php +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleProductOnConfigureCartPage.php @@ -8,6 +8,7 @@ use Magento\Bundle\Test\Fixture\BundleProduct; use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Checkout\Test\Constraint\Utils\CartPageLoadTrait; use Magento\Checkout\Test\Fixture\Cart; use Magento\Checkout\Test\Page\CheckoutCart; use Magento\Mtf\Constraint\AbstractAssertForm; @@ -17,6 +18,8 @@ */ class AssertBundleProductOnConfigureCartPage extends AbstractAssertForm { + use CartPageLoadTrait; + /** * Check bundle product options correctly displayed on cart configuration page. * @@ -28,6 +31,8 @@ class AssertBundleProductOnConfigureCartPage extends AbstractAssertForm public function processAssert(CheckoutCart $checkoutCart, Cart $cart, CatalogProductView $catalogProductView) { $checkoutCart->open(); + $this->waitForCartPageLoaded($checkoutCart); + $sourceProducts = $cart->getDataFieldConfig('items')['source']; $products = $sourceProducts->getProducts(); foreach ($cart->getItems() as $key => $item) { From 5a84c5a483386fdcf297126ecba90db1e1b13f89 Mon Sep 17 00:00:00 2001 From: U1PR01 <nirav@krishtechnolabs.com> Date: Sat, 27 Oct 2018 12:30:34 +0530 Subject: [PATCH 180/240] Fixed - Default tax region/state appears in customer & order data #16684 --- .../Checkout/view/frontend/web/js/model/new-customer-address.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js index f9dae2691e5f3..22e03c86f5d0a 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js @@ -25,6 +25,8 @@ define([ if (countryId) { if (addressData.region && addressData.region['region_id']) { regionId = addressData.region['region_id']; + } else if (!addressData['region_id']) { + regionId = null; } else if (countryId === window.checkoutConfig.defaultCountryId) { regionId = window.checkoutConfig.defaultRegionId; } From 109a27eb8bd2e5b8f37fcb84ef4c0d19490a5e16 Mon Sep 17 00:00:00 2001 From: Bartosz Kubicki <bartosz.kubicki@lizardmedia.pl> Date: Tue, 11 Sep 2018 19:38:22 +0200 Subject: [PATCH 181/240] Adding trimming sku value function to sku backend model. Adding trimming sku value function to sku backend model. Adding frontend validation for sku field. --- .../Model/Product/Attribute/Backend/Sku.php | 14 ++++++++++++++ .../DataProvider/Product/Form/Modifier/General.php | 3 ++- app/code/Magento/Ui/i18n/en_US.csv | 1 + .../Ui/view/base/web/js/lib/validation/rules.js | 6 ++++++ lib/web/i18n/en_US.csv | 1 + lib/web/mage/validation.js | 6 ++++++ 6 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php index a9da2a3400de9..752007aced094 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php @@ -97,6 +97,7 @@ protected function _generateUniqueSku($object) public function beforeSave($object) { $this->_generateUniqueSku($object); + $this->trimValue($object); return parent::beforeSave($object); } @@ -127,4 +128,17 @@ protected function _getLastSimilarAttributeValueIncrement($attribute, $object) $data = $connection->fetchOne($select, $bind); return abs((int)str_replace($value, '', $data)); } + + /** + * @param Product $object + * @return void + */ + private function trimValue($object) + { + $attrCode = $this->getAttribute()->getAttributeCode(); + $value = $object->getData($attrCode); + if ($value) { + $object->setData($attrCode, trim($value)); + } + } } 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 819788d6aa6be..21c4471ca5760 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 @@ -372,7 +372,8 @@ protected function customizeNameListeners(array $meta) $skuPath . static::META_CONFIG_PATH, $meta, [ - 'autoImportIfEmpty' => true + 'autoImportIfEmpty' => true, + 'validation' => ['no-marginal-whitespace' => true] ] ); diff --git a/app/code/Magento/Ui/i18n/en_US.csv b/app/code/Magento/Ui/i18n/en_US.csv index cff52a3fd6fed..981d444b31d53 100644 --- a/app/code/Magento/Ui/i18n/en_US.csv +++ b/app/code/Magento/Ui/i18n/en_US.csv @@ -58,6 +58,7 @@ Keyword,Keyword "Letters, numbers, spaces or underscores only please","Letters, numbers, spaces or underscores only please" "Letters only please","Letters only please" "No white space please","No white space please" +"No marginal white space please","No marginal white space please" "Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx","Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx" "A positive or negative non-decimal number please","A positive or negative non-decimal number please" "The specified vehicle identification number (VIN) is invalid.","The specified vehicle identification number (VIN) is invalid." diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 9c7d4b9d4fa81..5c9292cd9be15 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -115,6 +115,12 @@ define([ }, $.mage.__('No white space please') ], + 'no-marginal-whitespace': [ + function (value) { + return !/^\s+|\s+$/i.test(value); + }, + $.mage.__('No marginal white space please') + ], 'zip-range': [ function (value) { return /^90[2-5]-\d{2}-\d{4}$/.test(value); diff --git a/lib/web/i18n/en_US.csv b/lib/web/i18n/en_US.csv index 4acc62aa6dc81..7938068871963 100644 --- a/lib/web/i18n/en_US.csv +++ b/lib/web/i18n/en_US.csv @@ -27,6 +27,7 @@ Submit,Submit "Letters, numbers, spaces or underscores only please","Letters, numbers, spaces or underscores only please" "Letters only please","Letters only please" "No white space please","No white space please" +"No marginal white space please","No marginal white space please" "Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx","Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx" "A positive or negative non-decimal number please","A positive or negative non-decimal number please" "The specified vehicle identification number (VIN) is invalid.","The specified vehicle identification number (VIN) is invalid." diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index 5f088f0446bb5..e14ecf982bc96 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -253,6 +253,12 @@ }, $.mage.__('No white space please') ], + 'no-marginal-whitespace': [ + function (value, element) { + return this.optional(element) || !/^\s+|\s+$/i.test(value); + }, + $.mage.__('No marginal white space please') + ], 'zip-range': [ function (value, element) { return this.optional(element) || /^90[2-5]-\d{2}-\d{4}$/.test(value); From 7dc2d1b1f6c7d38b6c672218d6ce71728977fcb2 Mon Sep 17 00:00:00 2001 From: Shubham Sharma <shubham.sharma967@webkul.com> Date: Sat, 27 Oct 2018 16:03:55 +0530 Subject: [PATCH 182/240] fixed issue #18458 --- app/code/Magento/Ui/view/base/web/js/modal/modal-component.js | 2 +- app/code/Magento/Ui/view/base/web/js/modal/modal.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js b/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js index e7b2444dad95c..f82d24e82736a 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal-component.js @@ -93,7 +93,7 @@ define([ * @returns {Object} Chainable. */ initModalEvents: function () { - this.options.keyEventHandlers.escapeKey = this.options.outerClickHandler = this[this.onCancel].bind(this); + this.options.keyEventHandlers.escapeKey = this[this.onCancel].bind(this); return this; }, diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js index 147a18c08c54c..884d6f46defcd 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js @@ -424,8 +424,7 @@ define([ * Creates overlay, append it to wrapper, set previous click event on overlay. */ _createOverlay: function () { - var events, - outerClickHandler = this.options.outerClickHandler || this.closeModal; + var events; this.overlay = $('.' + this.options.overlayClass); @@ -437,7 +436,6 @@ define([ } events = $._data(this.overlay.get(0), 'events'); events ? this.prevOverlayHandler = events.click[0].handler : false; - this.options.clickableOverlay ? this.overlay.unbind().on('click', outerClickHandler) : false; }, /** From b57ae0188bdf5522a2d56693d386e54348c368e5 Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Sun, 16 Sep 2018 17:14:58 -0300 Subject: [PATCH 183/240] #17744 Adding logic to get default billing address --- .../web/js/model/checkout-data-resolver.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 73f4df567903c..6586f647e2404 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -219,15 +219,26 @@ define([ */ applyBillingAddress: function () { var shippingAddress; + var isBillingAddressInitialized; if (quote.billingAddress()) { selectBillingAddress(quote.billingAddress()); return; } - shippingAddress = quote.shippingAddress(); + shippingAddress = quote.billingAddress(); + if(quote.isVirtual()) { + isBillingAddressInitialized = addressList.some(function (addrs) { + if (addrs.isDefaultBilling()) { + selectBillingAddress(addrs); + return true; + } + return false; + }); + } - if (shippingAddress && + if (!isBillingAddressInitialized && + shippingAddress && shippingAddress.canUseForBilling() && (shippingAddress.isDefaultShipping() || !quote.isVirtual()) ) { From f7a8160bb2d759644de61012cb5b3bdb0b8e2770 Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Mon, 17 Sep 2018 11:05:54 -0300 Subject: [PATCH 184/240] #17744 Reorganizing code --- .../view/frontend/web/js/model/checkout-data-resolver.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 6586f647e2404..7a1ba9513ec42 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -226,7 +226,7 @@ define([ return; } - shippingAddress = quote.billingAddress(); + if(quote.isVirtual()) { isBillingAddressInitialized = addressList.some(function (addrs) { if (addrs.isDefaultBilling()) { @@ -237,6 +237,7 @@ define([ }); } + shippingAddress = quote.shippingAddress(); if (!isBillingAddressInitialized && shippingAddress && shippingAddress.canUseForBilling() && From f3f9cff9a1a707db0b022748662888c559e44d4a Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Mon, 17 Sep 2018 17:18:04 -0300 Subject: [PATCH 185/240] #17744 Fixing syntax pattern --- .../view/frontend/web/js/model/checkout-data-resolver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 7a1ba9513ec42..6f4c99438610b 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -227,7 +227,7 @@ define([ return; } - if(quote.isVirtual()) { + if (quote.isVirtual()) { isBillingAddressInitialized = addressList.some(function (addrs) { if (addrs.isDefaultBilling()) { selectBillingAddress(addrs); From 3836bd2743d1f68a3443e11c1a0746a69902849c Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <strpwebstudio@gmail.com> Date: Tue, 18 Sep 2018 10:34:27 +0300 Subject: [PATCH 186/240] #17744 Adding logic to get default billing address Fixed var declaration --- .../view/frontend/web/js/model/checkout-data-resolver.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 6f4c99438610b..142f6af91cc29 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -218,8 +218,8 @@ define([ * Apply resolved billing address to quote */ applyBillingAddress: function () { - var shippingAddress; - var isBillingAddressInitialized; + var shippingAddress, + isBillingAddressInitialized; if (quote.billingAddress()) { selectBillingAddress(quote.billingAddress()); From d88d1332ec96d548e4da6b187f3909172ecc4ec1 Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Mon, 1 Oct 2018 15:31:32 -0300 Subject: [PATCH 187/240] #17744 Adding isVirtual mock to quote instance && fixs --- .../view/frontend/web/js/model/checkout-data-resolver.js | 3 +++ .../frontend/js/view/payment/method-renderer/paypal.test.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 142f6af91cc29..28e04699f8daf 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -231,13 +231,16 @@ define([ isBillingAddressInitialized = addressList.some(function (addrs) { if (addrs.isDefaultBilling()) { selectBillingAddress(addrs); + return true; } + return false; }); } shippingAddress = quote.shippingAddress(); + if (!isBillingAddressInitialized && shippingAddress && shippingAddress.canUseForBilling() && diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js index bcfaecd277754..80597eebc5910 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js @@ -24,7 +24,10 @@ define([ paymentMethod: ko.observable(), totals: ko.observable({ 'base_grand_total': 0 - }) + }), + isVirtual: function () { + return false; + } }, 'Magento_Braintree/js/view/payment/adapter': { config: {}, From 7116fedf8e8e218529c8ce0f8332193f447656db Mon Sep 17 00:00:00 2001 From: lucascalazans <calazans95@hotmail.com> Date: Mon, 1 Oct 2018 16:55:30 -0300 Subject: [PATCH 188/240] #17744 Adding others isVirtual mock and annotations --- .../js/view/payment/method-renderer/cc-form.test.js | 7 ++++++- .../js/view/payment/method-renderer/paypal.test.js | 2 ++ .../method-renderer/paypal-express-abstract.test.js | 6 +++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js index 52739eec2782b..df6996afeb965 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js @@ -19,7 +19,12 @@ define([ billingAddress: ko.observable(), shippingAddress: ko.observable(), paymentMethod: ko.observable(), - totals: ko.observable({}) + totals: ko.observable({}), + + /** Stub */ + isVirtual: function () { + return false; + } }, 'Magento_Braintree/js/view/payment/validator-handler': jasmine.createSpyObj( 'validator-handler', diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js index 80597eebc5910..a2373cfb99091 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js @@ -25,6 +25,8 @@ define([ totals: ko.observable({ 'base_grand_total': 0 }), + + /** Stub */ isVirtual: function () { return false; } diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js index 47e3507ea1321..12e12eb492c89 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js @@ -28,8 +28,12 @@ define([ billingAddress: ko.observable(), shippingAddress: ko.observable(), paymentMethod: ko.observable(), - totals: ko.observable({}) + totals: ko.observable({}), + /** Stub */ + isVirtual: function () { + return false; + } }, 'Magento_Checkout/js/action/set-payment-information': setPaymentMock, 'Magento_Checkout/js/model/payment/additional-validators': { From 70cebbd672615f12541653156a412b7714aa08e2 Mon Sep 17 00:00:00 2001 From: Jakub Idziak <j.idziak@macopedia.pl> Date: Thu, 18 Oct 2018 16:13:36 +0200 Subject: [PATCH 189/240] ISSUE-5021 - fixed place order for custom shipping methods with underscore in carrier code --- .../Magento/Checkout/Model/PaymentInformationManagement.php | 5 ++--- .../Test/Unit/Model/PaymentInformationManagementTest.php | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php index 3d6b0aa0cdc12..eca747f9c042f 100644 --- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php @@ -114,9 +114,8 @@ public function savePaymentInformation( $quote->setDataChanges(true); $shippingAddress = $quote->getShippingAddress(); if ($shippingAddress && $shippingAddress->getShippingMethod()) { - $shippingDataArray = explode('_', $shippingAddress->getShippingMethod()); - $shippingCarrier = array_shift($shippingDataArray); - $shippingAddress->setLimitCarrier($shippingCarrier); + $shippingRate = $shippingAddress->getShippingRateByCode($shippingAddress->getShippingMethod()); + $shippingAddress->setLimitCarrier($shippingRate->getCarrier()); } } $this->paymentMethodManagement->set($cartId, $paymentMethod); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php index b40b2b244ac4c..6ecd759b8b826 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php @@ -168,9 +168,11 @@ private function getMockForAssignBillingAddress($cartId, $billingAddressMock) $billingAddressId = 1; $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); $quoteBillingAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + $shippingRate = $this->createPartialMock(\Magento\Quote\Model\Quote\Address\Rate::class, []); + $shippingRate->setCarrier('flatrate'); $quoteShippingAddress = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, - ['setLimitCarrier', 'getShippingMethod'] + ['setLimitCarrier', 'getShippingMethod', 'getShippingRateByCode'] ); $this->cartRepositoryMock->expects($this->any())->method('getActive')->with($cartId)->willReturn($quoteMock); $quoteMock->expects($this->once())->method('getBillingAddress')->willReturn($quoteBillingAddress); @@ -179,6 +181,7 @@ private function getMockForAssignBillingAddress($cartId, $billingAddressMock) $quoteMock->expects($this->once())->method('removeAddress')->with($billingAddressId); $quoteMock->expects($this->once())->method('setBillingAddress')->with($billingAddressMock); $quoteMock->expects($this->once())->method('setDataChanges')->willReturnSelf(); + $quoteShippingAddress->expects($this->any())->method('getShippingRateByCode')->willReturn($shippingRate); $quoteShippingAddress->expects($this->any())->method('getShippingMethod')->willReturn('flatrate_flatrate'); $quoteShippingAddress->expects($this->once())->method('setLimitCarrier')->with('flatrate')->willReturnSelf(); } From fdf855c7b134d489b40585134ec7d41d46d93add Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 19 Oct 2018 08:01:01 -0400 Subject: [PATCH 190/240] Allow set billing information via API with existing address Not setting the customerId with an existing address caused a `NoSuchEntityException` to be thrown during address validation https://github.com/magento/magento2/blob/56af1e73ce21867b770a7458ab6e109f4a1eface/app/code/Magento/Quote/Model/QuoteAddressValidator.php#L84 Fixes #17485 --- app/code/Magento/Quote/Model/BillingAddressManagement.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Quote/Model/BillingAddressManagement.php b/app/code/Magento/Quote/Model/BillingAddressManagement.php index 2cbca917c26a1..c89fc37b44689 100644 --- a/app/code/Magento/Quote/Model/BillingAddressManagement.php +++ b/app/code/Magento/Quote/Model/BillingAddressManagement.php @@ -76,6 +76,7 @@ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $addres { /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); + $address->setCustomerId($quote->getCustomerId()); $quote->removeAddress($quote->getBillingAddress()->getId()); $quote->setBillingAddress($address); try { From 06ca13a554082ed8fcbe7852d765aa2916b48fb1 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Sat, 20 Oct 2018 12:11:28 -0400 Subject: [PATCH 191/240] Prevent exception when option text converts to false The function would incorrectly through an exception when the option text was set to a string that would evaluate to false such as "0" Fixes #13083 --- .../Magento/Eav/Model/Entity/Attribute/OptionManagement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php b/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php index b0508fd8cc626..3cefd4b2abba3 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/OptionManagement.php @@ -132,7 +132,7 @@ public function getItems($entityType, $attributeCode) */ protected function validateOption($attribute, $optionId) { - if (!$attribute->getSource()->getOptionText($optionId)) { + if ($attribute->getSource()->getOptionText($optionId) === false) { throw new NoSuchEntityException( __('Attribute %1 does not contain option with Id %2', $attribute->getAttributeCode(), $optionId) ); From 3f17772611af437165443aa2d4ac9c3a27945225 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Fri, 19 Oct 2018 23:39:54 +0300 Subject: [PATCH 192/240] Fixed issue #4468 "Unable to insert multiple catalog product list widgets (with pager) in CMS page" --- .../Block/Product/ProductsList.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php index 9a55f981b7607..ad7b5ffcba6c1 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php +++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php @@ -196,7 +196,7 @@ public function getProductPriceHtml( ? $arguments['display_minimal_price'] : true; - /** @var \Magento\Framework\Pricing\Render $priceRender */ + /** @var \Magento\Framework\Pricing\Render $priceRender */ $priceRender = $this->getLayout()->getBlock('product.price.render.default'); $price = ''; @@ -338,7 +338,7 @@ public function getPagerHtml() if (!$this->pager) { $this->pager = $this->getLayout()->createBlock( \Magento\Catalog\Block\Product\Widget\Html\Pager::class, - 'widget.products.list.pager' + $this->getWidgetPagerBlockName() ); $this->pager->setUseContainer(true) @@ -398,4 +398,19 @@ private function getPriceCurrency() } return $this->priceCurrency; } + + /** + * @return string + */ + private function getWidgetPagerBlockName() + { + $pageName = $this->getData('page_var_name'); + $pagerBlockName = 'widget.products.list.pager'; + + if (!$pageName) { + return $pagerBlockName; + } + + return $pagerBlockName . '.' . $pageName; + } } From 999e79f85c81fb49d5c90732bc275afda36f854a Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 21 Oct 2018 20:58:29 +0300 Subject: [PATCH 193/240] Sections less mixins: fix the issue with missing rules and incorrect default variables #18729 --- lib/web/css/source/lib/_sections.less | 15 +++++++++------ lib/web/css/source/lib/variables/_sections.less | 7 +++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/web/css/source/lib/_sections.less b/lib/web/css/source/lib/_sections.less index 372008d061580..b73e317835955 100644 --- a/lib/web/css/source/lib/_sections.less +++ b/lib/web/css/source/lib/_sections.less @@ -49,16 +49,16 @@ @_tab-control-color: @tab-control__color, @_tab-control-text-decoration: @tab-control__text-decoration, - @_tab-control-color-visited: @tab-control__color, - @_tab-control-text-decoration-visited: @tab-control__text-decoration, + @_tab-control-color-visited: @tab-control__visited__color, + @_tab-control-text-decoration-visited: @tab-control__visited__text-decoration, @_tab-control-background-color-hover: @tab-control__hover__background-color, @_tab-control-color-hover: @tab-control__hover__color, - @_tab-control-text-decoration-hover: @tab-control__text-decoration, + @_tab-control-text-decoration-hover: @tab-control__hover__text-decoration, @_tab-control-background-color-active: @tab-control__active__background-color, @_tab-control-color-active: @tab-control__active__color, - @_tab-control-text-decoration-active: @tab-control__text-decoration, + @_tab-control-text-decoration-active: @tab-control__active__text-decoration, @_tab-control-height: @tab-control__height, @_tab-control-margin-right: @tab-control__margin-right, @@ -121,6 +121,7 @@ &.active > .switch:hover { .lib-css(background, @_tab-control-background-color-active); .lib-css(color, @_tab-control-color-active); + .lib-css(text-decoration, @_tab-control-text-decoration-active); } &.active > .switch, @@ -200,8 +201,8 @@ @_accordion-control-color: @accordion-control__color, @_accordion-control-text-decoration: @accordion-control__text-decoration, - @_accordion-control-color-visited: @accordion-control__color, - @_accordion-control-text-decoration-visited: @accordion-control__text-decoration, + @_accordion-control-color-visited: @accordion-control__visited__color, + @_accordion-control-text-decoration-visited: @accordion-control__visited__text-decoration, @_accordion-control-background-color-hover: @accordion-control__hover__background-color, @_accordion-control-color-hover: @accordion-control__hover__color, @@ -275,6 +276,8 @@ &.active > .switch:focus, &.active > .switch:hover { .lib-css(background, @_accordion-control-background-color-active); + .lib-css(color, @_accordion-control-color-active); + .lib-css(text-decoration, @_accordion-control-text-decoration-active); .lib-css(padding-bottom, @_accordion-control-padding-bottom); } } diff --git a/lib/web/css/source/lib/variables/_sections.less b/lib/web/css/source/lib/variables/_sections.less index 7d961077e6f59..0868ce73c49ef 100644 --- a/lib/web/css/source/lib/variables/_sections.less +++ b/lib/web/css/source/lib/variables/_sections.less @@ -40,6 +40,9 @@ @tab-control__active__color: @text__color; @tab-control__active__text-decoration: @tab-control__text-decoration; +@tab-control__visited__color: @accordion-control__color; +@tab-control__visited__text-decoration: @accordion-control__text-decoration; + @tab-content__background-color: @tab-control__active__background-color; @tab-content__border-top-status: false; @tab-content__border: @tab-control__border-width solid @tab-control__border-color; @@ -72,8 +75,8 @@ @accordion-control__padding-bottom: @tab-control__padding-bottom; @accordion-control__padding-left: @accordion-control__padding-right; -@accordion-control__visited__color: @accordion-control__color; -@accordion-control__visited__text-decoration: @accordion-control__text-decoration; +@accordion-control__visited__color: @tab-control__visited__color; +@accordion-control__visited__text-decoration: @tab-control__visited__text-decoration; @accordion-control__hover__background-color: @tab-control__hover__background-color; @accordion-control__hover__color: @tab-control__hover__color; From 5333810773c2d8160b6c234ac27f8e09edb1382c Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 21 Oct 2018 21:36:13 +0300 Subject: [PATCH 194/240] Fix a typo in variable name #18730 --- lib/web/css/source/lib/variables/_sections.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/css/source/lib/variables/_sections.less b/lib/web/css/source/lib/variables/_sections.less index 0868ce73c49ef..05226d8aa3a3c 100644 --- a/lib/web/css/source/lib/variables/_sections.less +++ b/lib/web/css/source/lib/variables/_sections.less @@ -40,8 +40,8 @@ @tab-control__active__color: @text__color; @tab-control__active__text-decoration: @tab-control__text-decoration; -@tab-control__visited__color: @accordion-control__color; -@tab-control__visited__text-decoration: @accordion-control__text-decoration; +@tab-control__visited__color: @tab-control__color; +@tab-control__visited__text-decoration: @tab-control__text-decoration; @tab-content__background-color: @tab-control__active__background-color; @tab-content__border-top-status: false; From 503b913ffddcea9672530e3541b0afed76b8ca07 Mon Sep 17 00:00:00 2001 From: GwanYeong Kim <gy741.kim@gmail.com> Date: Sun, 21 Oct 2018 21:45:51 +0900 Subject: [PATCH 195/240] Fix Useless use of Cat --- dev/travis/before_script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 7cf55ca8083f1..a6aa8ab375fcf 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -72,7 +72,7 @@ case $TEST_SUITE in --base-path="$TRAVIS_BUILD_DIR" \ --repo='https://github.com/magento/magento2.git' \ --branch="$TRAVIS_BRANCH" - cat "$changed_files_ce" | sed 's/^/ + including /' + sed 's/^/ + including /' "$changed_files_ce" cd ../../.. ;; From 443e681a41dfcd9c3a446c8c9c7b6c240c52857e Mon Sep 17 00:00:00 2001 From: rahul <rahul@webkul.com> Date: Sat, 27 Oct 2018 17:51:08 +0530 Subject: [PATCH 196/240] fixed Translation issue send-friend in send.phtml --- .../Magento/SendFriend/view/frontend/templates/send.phtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/SendFriend/view/frontend/templates/send.phtml b/app/code/Magento/SendFriend/view/frontend/templates/send.phtml index 2b25e0efab84a..4922a9f365ced 100644 --- a/app/code/Magento/SendFriend/view/frontend/templates/send.phtml +++ b/app/code/Magento/SendFriend/view/frontend/templates/send.phtml @@ -17,13 +17,13 @@ <div class="secondary"> <button type="button" id="btn-remove<%- data._index_ %>" class="action remove" title="<?= $block->escapeHtmlAttr(__('Remove Recipent')) ?>"> - <span><?= $block->escapeJs($block->escapeHtml(__('Remove'))) ?></span> + <span><?= $block->escapeHtml(__('Remove')) ?></span> </button> </div> </div> <fieldset class="fieldset"> <div class="field name required"> - <label for="recipients-name<%- data._index_ %>" class="label"><span><?= $block->escapeJs($block->escapeHtml(__('Name'))) ?></span></label> + <label for="recipients-name<%- data._index_ %>" class="label"><span><?= $block->escapeHtml(__('Name')) ?></span></label> <div class="control"> <input name="recipients[name][<%- data._index_ %>]" type="text" title="<?= $block->escapeHtmlAttr(__('Name')) ?>" class="input-text" id="recipients-name<%- data._index_ %>" data-validate="{required:true}"/> @@ -31,7 +31,7 @@ </div> <div class="field email required"> - <label for="recipients-email<%- data._index_ %>" class="label"><span><?= $block->escapeJs($block->escapeHtml(__('Email'))) ?></span></label> + <label for="recipients-email<%- data._index_ %>" class="label"><span><?= $block->escapeHtml(__('Email')) ?></span></label> <div class="control"> <input name="recipients[email][<%- data._index_ %>]" title="<?= $block->escapeHtmlAttr(__('Email')) ?>" id="recipients-email<%- data._index_ %>" type="email" class="input-text" From f1b2360a18cc04104ad0c04a2978af3c99c401d2 Mon Sep 17 00:00:00 2001 From: rahul <rahul@webkul.com> Date: Sun, 28 Oct 2018 17:01:06 +0530 Subject: [PATCH 197/240] fixed - Unable to select payment method according to country of the address at checkout time --- .../frontend/web/js/model/shipping-save-processor/default.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js index 3a6574bff8948..447d626b339bd 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js @@ -35,9 +35,8 @@ define([ saveShippingInformation: function () { var payload; - if (!quote.billingAddress()) { - selectBillingAddressAction(quote.shippingAddress()); - } + /* Assign selected address every time buyer selects address*/ + selectBillingAddressAction(quote.shippingAddress()); payload = { addressInformation: { From e4b430a89a1d4e5ed92dccbed8c0c93aef7f82f4 Mon Sep 17 00:00:00 2001 From: U1PR01 <nirav@krishtechnolabs.com> Date: Mon, 29 Oct 2018 13:17:48 +0530 Subject: [PATCH 198/240] Update code for fixed issue #16684 --- .../Checkout/view/frontend/web/js/model/new-customer-address.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js index 22e03c86f5d0a..7b858c92bee34 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/new-customer-address.js @@ -26,7 +26,7 @@ define([ if (addressData.region && addressData.region['region_id']) { regionId = addressData.region['region_id']; } else if (!addressData['region_id']) { - regionId = null; + regionId = undefined; } else if (countryId === window.checkoutConfig.defaultCountryId) { regionId = window.checkoutConfig.defaultRegionId; } From 7ceea962f0eab3baff1a4881a80b3cc2ac96c51f Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 29 Oct 2018 09:56:28 +0200 Subject: [PATCH 199/240] MAGETWO-93988: Product Review "Save and Next" and "Save and Previous" not working --- .../Review/Product/CollectionTest.php | 83 +++++++++++++++++-- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php index a7f859b23dfa2..b44ef7aa20c6a 100644 --- a/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Review/Model/ResourceModel/Review/Product/CollectionTest.php @@ -3,20 +3,89 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Review\Model\ResourceModel\Review\Product; +use Magento\Review\Model\Review; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Tests some functionality of the Product Review collection + */ class CollectionTest extends \PHPUnit\Framework\TestCase { /** + * Checks resulting ids count + * + * @param int $status + * @param int $expectedCount + * @param string $sortAttribute + * @param string $dir + * @param callable $assertion + * @dataProvider sortOrderAssertionsDataProvider * @magentoDataFixture Magento/Review/_files/different_reviews.php */ - public function testGetResultingIds() - { - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Review\Model\ResourceModel\Review\Product\Collection::class - ); - $collection->addStatusFilter(\Magento\Review\Model\Review::STATUS_APPROVED); + public function testGetResultingIds( + int $status, + int $expectedCount, + string $sortAttribute, + string $dir, + callable $assertion + ) { + $collection = Bootstrap::getObjectManager()->create(Collection::class); + if ($status > 0) { + $collection->addStatusFilter($status); + } + $collection->setOrder($sortAttribute, $dir); $actual = $collection->getResultingIds(); - $this->assertCount(2, $actual); + $this->assertCount($expectedCount, $actual); + $assertion($actual); + } + + /** + * Sort order assertions data provider + * + * @return array + */ + public function sortOrderAssertionsDataProvider(): array + { + return [ + [ + Review::STATUS_APPROVED, + 2, + 'rt.review_id', + 'DESC', + function (array $actual) { + $this->assertLessThan($actual[0], $actual[1]); + } + ], + [ + Review::STATUS_APPROVED, + 2, + 'rt.review_id', + 'ASC', + function (array $actual) { + $this->assertLessThan($actual[1], $actual[0]); + } + ], + [ + Review::STATUS_APPROVED, + 2, + 'rt.created_at', + 'ASC', + function (array $actual) { + $this->assertLessThan($actual[1], $actual[0]); + } + ], + [ + 0, + 3, + 'rt.review_id', + 'ASC', + function (array $actual) { + $this->assertLessThan($actual[1], $actual[0]); + } + ] + ]; } } From 3cbc2555af0fda5af46a9c1e619d2ba090c826d2 Mon Sep 17 00:00:00 2001 From: Kajal Solanki <kajal10395@gmail.com> Date: Mon, 29 Oct 2018 14:21:45 +0530 Subject: [PATCH 200/240] fixed-Global-search icon misaligned-#18913 --- .../web/css/source/module/header/actions-group/_search.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/actions-group/_search.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/actions-group/_search.less index 3265c8b2fc387..8fcb0f94fff4e 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/actions-group/_search.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/actions-group/_search.less @@ -151,7 +151,7 @@ background-color: transparent; border: 1px solid transparent; font-size: @search-global-input__font-size; - height: @search-global-input__height; + height: @search-global-input__height + .2; padding: @search-global-input__padding-top @search-global-input__padding-side @search-global-input__padding-bottom; position: absolute; right: 0; From e84dfd58440a0ea98d01341d38c1831e648163e4 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 29 Oct 2018 11:33:14 +0200 Subject: [PATCH 201/240] magento-engcom/magento2ce#2285: Code style fixes --- .../Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php | 6 +++--- .../Email/Test/Unit/Model/Template/SenderResolverTest.php | 6 +++--- .../Test/Unit/Observer/SalesEventQuoteMergeTest.php | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php b/app/code/Magento/Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php index 5f7c44b988c66..b06dc3cb48cbc 100644 --- a/app/code/Magento/Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/Plugin/WindowsSmtpConfigTest.php @@ -43,7 +43,7 @@ class WindowsSmtpConfigTest extends \PHPUnit\Framework\TestCase * * @return void */ - public function setUp(): void + public function setUp() { $objectManager = new ObjectManager($this); @@ -65,7 +65,7 @@ public function setUp(): void * * @return void */ - public function testBeforeSendMessageOsWindows(): void + public function testBeforeSendMessageOsWindows() { $this->osInfoMock->expects($this->once()) ->method('isWindows') @@ -86,7 +86,7 @@ public function testBeforeSendMessageOsWindows(): void * * @return void */ - public function testBeforeSendMessageOsIsWindows(): void + public function testBeforeSendMessageOsIsWindows() { $this->osInfoMock->expects($this->once()) ->method('isWindows') diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/SenderResolverTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/SenderResolverTest.php index fb25290f9d27b..8d2931f3e38f5 100644 --- a/app/code/Magento/Email/Test/Unit/Model/Template/SenderResolverTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/Template/SenderResolverTest.php @@ -30,7 +30,7 @@ class SenderResolverTest extends \PHPUnit\Framework\TestCase /** * @return void */ - public function setUp(): void + public function setUp() { $objectManager = new ObjectManager($this); @@ -49,7 +49,7 @@ public function setUp(): void * * @return void */ - public function testResolve(): void + public function testResolve() { $sender = 'general'; $scopeId = null; @@ -88,7 +88,7 @@ public function testResolve(): void * * @return void */ - public function testResolveThrowException(array $sender): void + public function testResolveThrowException(array $sender) { $this->expectExceptionMessage('Invalid sender data'); $this->expectException(MailException::class); diff --git a/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php index 7a3000f7c0743..e7d9277593350 100644 --- a/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php +++ b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventQuoteMergeTest.php @@ -27,7 +27,7 @@ class SalesEventQuoteMergeTest extends \PHPUnit\Framework\TestCase /** * @return void */ - public function setUp(): void + public function setUp() { $objectManger = new ObjectManager($this); $this->salesEventQuoteMerge = $objectManger->getObject(SalesEventQuoteMerge::class); @@ -40,7 +40,7 @@ public function setUp(): void * * @return void */ - public function testExecute($giftMessageId): void + public function testExecute($giftMessageId) { $sourceQuoteMock = $this->createPartialMock(Quote::class, ['getGiftMessageId']); $sourceQuoteMock->expects($this->once()) From 6178035ece28d2a6a40fefcb09859515d78157a2 Mon Sep 17 00:00:00 2001 From: Neeta Kangiya <neeta@wagento.com> Date: Mon, 29 Oct 2018 16:11:02 +0530 Subject: [PATCH 202/240] add old event --- .../Model/Indexer/Fulltext/Action/DataProvider.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index ff9cc7e919170..37ef0a7689f9d 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -317,9 +317,14 @@ public function getSearchableAttributes($backendType = null) $attributes = $productAttributes->getItems(); /** - * Event argument `catelogsearch_searchable_attributes_load_after` is @deprecated 100.2.5. Use + * Event argument `catelogsearch_searchable_attributes_load_after` is @deprecated. Use * `catalogsearch_searchable_attributes_load_after` instead. */ + $this->eventManager->dispatch( + 'catelogsearch_searchable_attributes_load_after', + ['engine' => $this->engine, 'attributes' => $attributes] + ); + $this->eventManager->dispatch( 'catalogsearch_searchable_attributes_load_after', ['engine' => $this->engine, 'attributes' => $attributes] From 2647a89266b6fca8a9fb1b885091da67cfb2c693 Mon Sep 17 00:00:00 2001 From: Neeta Kangiya <neeta@wagento.com> Date: Mon, 29 Oct 2018 16:29:54 +0530 Subject: [PATCH 203/240] add proper @deprecated and @see --- .../Model/Indexer/Fulltext/Action/DataProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 37ef0a7689f9d..286b6427c8132 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -317,8 +317,8 @@ public function getSearchableAttributes($backendType = null) $attributes = $productAttributes->getItems(); /** - * Event argument `catelogsearch_searchable_attributes_load_after` is @deprecated. Use - * `catalogsearch_searchable_attributes_load_after` instead. + * @deprecated Event argument catelogsearch_searchable_attributes_load_after. + * @see catalogsearch_searchable_attributes_load_after instead. */ $this->eventManager->dispatch( 'catelogsearch_searchable_attributes_load_after', From 4ea1d0ef22b21b9b713e06b9269fb549065c6c9e Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 29 Oct 2018 13:53:10 +0200 Subject: [PATCH 204/240] MAGETWO-82530: Redirect fix when Unsubscribe execute --- .../Controller/Subscriber/Unsubscribe.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Newsletter/Controller/Subscriber/Unsubscribe.php b/app/code/Magento/Newsletter/Controller/Subscriber/Unsubscribe.php index efc469e15deaa..36a4ccfb11580 100644 --- a/app/code/Magento/Newsletter/Controller/Subscriber/Unsubscribe.php +++ b/app/code/Magento/Newsletter/Controller/Subscriber/Unsubscribe.php @@ -4,13 +4,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Newsletter\Controller\Subscriber; + +/** + * Controller for unsubscribing customers. + */ class Unsubscribe extends \Magento\Newsletter\Controller\Subscriber { /** * Unsubscribe newsletter - * @return void + * @return \Magento\Backend\Model\View\Result\Redirect */ public function execute() { @@ -27,6 +32,10 @@ public function execute() $this->messageManager->addException($e, __('Something went wrong while unsubscribing you.')); } } - $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl()); + /** @var \Magento\Backend\Model\View\Result\Redirect $redirect */ + $redirect = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT); + $redirectUrl = $this->_redirect->getRedirectUrl(); + + return $redirect->setUrl($redirectUrl); } } From fc2f1da860952e5ce60c2ef2e828ccb3feb1bf73 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 29 Oct 2018 14:09:45 +0200 Subject: [PATCH 205/240] MAGETWO-82530: Redirect fix when Unsubscribe execute --- .../Magento/Newsletter/Controller/Subscriber/Unsubscribe.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Controller/Subscriber/Unsubscribe.php b/app/code/Magento/Newsletter/Controller/Subscriber/Unsubscribe.php index 36a4ccfb11580..ed415d04450a6 100644 --- a/app/code/Magento/Newsletter/Controller/Subscriber/Unsubscribe.php +++ b/app/code/Magento/Newsletter/Controller/Subscriber/Unsubscribe.php @@ -7,7 +7,6 @@ namespace Magento\Newsletter\Controller\Subscriber; - /** * Controller for unsubscribing customers. */ From 1226cb301699f642b9cdd4e541b9327df07a1b63 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 29 Oct 2018 15:35:31 +0200 Subject: [PATCH 206/240] MAGETWO-69766: [Magento Cloud] - Cant flush Images cache in admin --- .../Framework/Filesystem/Directory/Write.php | 57 ++++++++++++++++++- .../Framework/Filesystem/Driver/File.php | 21 +++++-- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php index 4566f61b15572..8be0fd9ae230a 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php @@ -3,10 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Filesystem\Directory; use Magento\Framework\Exception\FileSystemException; +/** + * Write Interface implementation + */ class Write extends Read implements WriteInterface { /** @@ -164,6 +168,7 @@ public function createSymlink($path, $destination, WriteInterface $targetDirecto */ public function delete($path = null) { + $exceptionMessages = []; if (!$this->isExist($path)) { return true; } @@ -171,11 +176,59 @@ public function delete($path = null) if ($this->driver->isFile($absolutePath)) { $this->driver->deleteFile($absolutePath); } else { - $this->driver->deleteDirectory($absolutePath); + try { + $this->deleteFilesRecursively($absolutePath); + } catch (FileSystemException $e) { + $exceptionMessages[] = $e->getMessage(); + } + try { + $this->driver->deleteDirectory($absolutePath); + } catch (FileSystemException $e) { + $exceptionMessages[] = $e->getMessage(); + } + + if (!empty($exceptionMessages)) { + throw new FileSystemException( + new \Magento\Framework\Phrase( + \implode(' ', $exceptionMessages) + ) + ); + } } return true; } + /** + * Delete files recursively + * + * Implemented in order to delete as much files as possible and collect all exceptions + * + * @param string $path + * @return void + * @throws FileSystemException + */ + private function deleteFilesRecursively(string $path) + { + $exceptionMessages = []; + $entitiesList = $this->driver->readDirectoryRecursively($path); + foreach ($entitiesList as $entityPath) { + if ($this->driver->isFile($entityPath)) { + try { + $this->driver->deleteFile($entityPath); + } catch (FileSystemException $e) { + $exceptionMessages[] = $e->getMessage(); + } + } + } + if (!empty($exceptionMessages)) { + throw new FileSystemException( + new \Magento\Framework\Phrase( + \implode(' ', $exceptionMessages) + ) + ); + } + } + /** * Change permissions of given path * @@ -224,7 +277,7 @@ public function touch($path, $modificationTime = null) /** * Check if given path is writable * - * @param null $path + * @param string|null $path * @return bool * @throws \Magento\Framework\Exception\FileSystemException */ diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/File.php b/lib/internal/Magento/Framework/Filesystem/Driver/File.php index c236228a00b75..53322f06bbc6f 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/File.php @@ -5,6 +5,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Filesystem\Driver; use Magento\Framework\Exception\FileSystemException; @@ -396,16 +397,28 @@ public function deleteFile($path) */ public function deleteDirectory($path) { + $exceptionMessages = []; $flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; $iterator = new \FilesystemIterator($path, $flags); /** @var \FilesystemIterator $entity */ foreach ($iterator as $entity) { - if ($entity->isDir()) { - $this->deleteDirectory($entity->getPathname()); - } else { - $this->deleteFile($entity->getPathname()); + try { + if ($entity->isDir()) { + $this->deleteDirectory($entity->getPathname()); + } else { + $this->deleteFile($entity->getPathname()); + } + } catch (FileSystemException $exception) { + $exceptionMessages[] = $exception->getMessage(); } } + if (!empty($exceptionMessages)) { + throw new FileSystemException( + new \Magento\Framework\Phrase( + \implode(' ', $exceptionMessages) + ) + ); + } $fullPath = $this->getScheme() . $path; if (is_link($fullPath)) { From 01f51f20b26025417bc1b151ee1f48875976124e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Mon, 29 Oct 2018 16:26:22 +0200 Subject: [PATCH 207/240] MAGETWO-86052: "Sort by" not working on Catalog Search results page --- app/code/Magento/Catalog/etc/adminhtml/system.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 77b69185fc355..7d62d46210ea2 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -83,8 +83,9 @@ <backend_model>Magento\Catalog\Model\Indexer\Product\Flat\System\Config\Mode</backend_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="default_sort_by" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="default_sort_by" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Product Listing Sort by</label> + <comment>Applies to category pages</comment> <source_model>Magento\Catalog\Model\Config\Source\ListSort</source_model> </field> <field id="list_allow_all" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> From 2f31de4da80d13910cfcea664bc49fcbe02be81a Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 30 Oct 2018 12:39:40 +0200 Subject: [PATCH 208/240] MAGETWO-86419: Some products use Categories Path for Product URLs --- .../Magento/Catalog/Model/Product/Url.php | 37 ++++--- .../ResourceModel/Product/Collection.php | 5 + .../Magento/Catalog/Model/Product/UrlTest.php | 104 ++++++++++++++++-- .../_files/config_use_category_in_url.php | 33 ++++++ .../config_use_category_in_url_rollback.php | 33 ++++++ 5 files changed, 187 insertions(+), 25 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url_rollback.php diff --git a/app/code/Magento/Catalog/Model/Product/Url.php b/app/code/Magento/Catalog/Model/Product/Url.php index c291dc33fedab..2f2fa1fa8f302 100644 --- a/app/code/Magento/Catalog/Model/Product/Url.php +++ b/app/code/Magento/Catalog/Model/Product/Url.php @@ -7,6 +7,7 @@ use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\Framework\App\Config\ScopeConfigInterface; /** * Product Url model @@ -45,6 +46,11 @@ class Url extends \Magento\Framework\DataObject */ protected $urlFinder; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + /** * @param \Magento\Framework\UrlFactory $urlFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -52,6 +58,7 @@ class Url extends \Magento\Framework\DataObject * @param \Magento\Framework\Session\SidResolverInterface $sidResolver * @param UrlFinderInterface $urlFinder * @param array $data + * @param ScopeConfigInterface|null $scopeConfig */ public function __construct( \Magento\Framework\UrlFactory $urlFactory, @@ -59,7 +66,8 @@ public function __construct( \Magento\Framework\Filter\FilterManager $filter, \Magento\Framework\Session\SidResolverInterface $sidResolver, UrlFinderInterface $urlFinder, - array $data = [] + array $data = [], + ScopeConfigInterface $scopeConfig = null ) { parent::__construct($data); $this->urlFactory = $urlFactory; @@ -67,16 +75,8 @@ public function __construct( $this->filter = $filter; $this->sidResolver = $sidResolver; $this->urlFinder = $urlFinder; - } - - /** - * Retrieve URL Instance - * - * @return \Magento\Framework\UrlInterface - */ - private function getUrlInstance() - { - return $this->urlFactory->create(); + $this->scopeConfig = $scopeConfig ?: + \Magento\Framework\App\ObjectManager::getInstance()->get(ScopeConfigInterface::class); } /** @@ -157,10 +157,19 @@ public function getUrl(\Magento\Catalog\Model\Product $product, $params = []) UrlRewrite::ENTITY_TYPE => \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE, UrlRewrite::STORE_ID => $storeId, ]; - if ($categoryId) { + $useCategories = $this->scopeConfig->getValue( + \Magento\Catalog\Helper\Product::XML_PATH_PRODUCT_URL_USE_CATEGORY, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + if ($useCategories && $categoryId) { $filterData[UrlRewrite::METADATA]['category_id'] = $categoryId; + } elseif (!$useCategories) { + $filterData[UrlRewrite::METADATA]['category_id'] = ''; } + $rewrite = $this->urlFinder->findOneByData($filterData); + if ($rewrite) { $requestPath = $rewrite->getRequestPath(); $product->setRequestPath($requestPath); @@ -194,6 +203,8 @@ public function getUrl(\Magento\Catalog\Model\Product $product, $params = []) $routeParams['_query'] = []; } - return $this->getUrlInstance()->setScope($storeId)->getUrl($routePath, $routeParams); + $url = $this->urlFactory->create()->setScope($storeId); + + return $url->getUrl($routePath, $routeParams); } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 937b21a9c8421..2c137ccbb26ee 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1422,6 +1422,11 @@ protected function _addUrlRewrite() ['cu' => $this->getTable('catalog_url_rewrite_product_category')], 'u.url_rewrite_id=cu.url_rewrite_id' )->where('cu.category_id IN (?)', $this->_urlRewriteCategory); + } else { + $select->joinLeft( + ['cu' => $this->getTable('catalog_url_rewrite_product_category')], + 'u.url_rewrite_id=cu.url_rewrite_id' + )->where('cu.url_rewrite_id IS NULL'); } // more priority is data with category id diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php index f9075a58c39ef..2411d333a5a19 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php @@ -23,6 +23,11 @@ class UrlTest extends \PHPUnit\Framework\TestCase */ protected $urlPathGenerator; + /** + * @var \Magento\Framework\Registry + */ + private $registry; + protected function setUp() { $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -31,23 +36,22 @@ protected function setUp() $this->urlPathGenerator = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::class ); + + /** @var \Magento\Framework\Registry $registry */ + $this->registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Framework\Registry::class + ); } public function testGetUrlInStore() { - $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ProductRepository::class - ); - $product = $repository->get('simple'); + $product = $this->getProduct(); $this->assertStringEndsWith('simple-product.html', $this->_model->getUrlInStore($product)); } public function testGetProductUrl() { - $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ProductRepository::class - ); - $product = $repository->get('simple'); + $product = $this->getProduct(); $this->assertStringEndsWith('simple-product.html', $this->_model->getProductUrl($product)); } @@ -80,10 +84,7 @@ public function testGetUrlPath() */ public function testGetUrl() { - $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ProductRepository::class - ); - $product = $repository->get('simple'); + $product = $this->getProduct(); $this->assertStringEndsWith('simple-product.html', $this->_model->getUrl($product)); $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -92,4 +93,83 @@ public function testGetUrl() $product->setId(100); $this->assertContains('catalog/product/view/id/100/', $this->_model->getUrl($product)); } + + /** + * @magentoAppArea frontend + * + * @magentoDataFixture Magento/Catalog/_files/config_use_category_in_url.php + * @magentoDataFixture Magento/Catalog/_files/url_rewrites.php + * + * @return void + */ + public function testGetUrlWithCategoryInUrl() + { + $product = $this->getProduct(); + $category = $this->getCategory($product); + + $this->assertStringEndsWith( + $category->getUrlKey() . '/' . $product->getUrlKey() . '.html', + $this->_model->getUrl($product) + ); + } + + /** + * @magentoAppArea frontend + * + * @return void + */ + public function testGetUrlWithoutCategoryInUrl() + { + $product = $this->getProduct(); + $category = $this->getCategory($product); + + $url = $this->_model->getUrl($product); + + $this->assertStringEndsWith($product->getUrlKey() . '.html', $url); + $this->assertNotContains($url, $category->getUrlKey()); + } + + /** + * @return \Magento\Catalog\Api\Data\ProductInterface + */ + private function getProduct(): \Magento\Catalog\Api\Data\ProductInterface + { + /** @var \Magento\Catalog\Model\ProductRepository $productRepository */ + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\ProductRepository::class + ); + + return $productRepository->get('simple'); + } + + /** + * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @return \Magento\Catalog\Api\Data\CategoryInterface + */ + private function getCategory( + \Magento\Catalog\Api\Data\ProductInterface $product + ): \Magento\Catalog\Api\Data\CategoryInterface { + /** @var \Magento\Catalog\Model\CategoryRepository $categoryRepository */ + $categoryRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\CategoryRepository::class + ); + + $categoryId = $product->getCategoryIds()[0]; + + $category = $categoryRepository->get($categoryId); + $this->registry->unregister('current_category'); + $this->registry->register('current_category', $category); + + return $category; + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('current_category'); + + parent::tearDown(); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url.php new file mode 100644 index 0000000000000..785bab340de33 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Helper\Product; +use Magento\Config\Model\Config\Factory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$configData = [ + ScopeConfigInterface::SCOPE_TYPE_DEFAULT => [ + '' => [ + Product::XML_PATH_PRODUCT_URL_USE_CATEGORY => '1', + ], + ], +]; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Factory $configFactory */ +$configFactory = $objectManager->create(Factory::class); + +foreach ($configData as $scope => $data) { + foreach ($data as $scopeCode => $scopeData) { + foreach ($scopeData as $path => $value) { + $config = $configFactory->create(); + $config->setScope($scope); + $config->setDataByPath($path, $value); + $config->save(); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url_rollback.php new file mode 100644 index 0000000000000..d1a5fec5bd133 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Helper\Product; +use Magento\Config\Model\Config\Factory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$configData = [ + ScopeConfigInterface::SCOPE_TYPE_DEFAULT => [ + '' => [ + Product::XML_PATH_PRODUCT_URL_USE_CATEGORY => '0', + ], + ], +]; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Factory $configFactory */ +$configFactory = $objectManager->create(Factory::class); + +foreach ($configData as $scope => $data) { + foreach ($data as $scopeCode => $scopeData) { + foreach ($scopeData as $path => $value) { + $config = $configFactory->create(); + $config->setScope($scope); + $config->setDataByPath($path, $value); + $config->save(); + } + } +} From acb52e14494a04a28ab0aa4c044224a0d8c1700c Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 30 Oct 2018 13:45:35 +0200 Subject: [PATCH 209/240] MAGETWO-87027: Text Attribute doesn't display all character on product page --- app/code/Magento/Swatches/view/frontend/web/css/swatches.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/css/swatches.css b/app/code/Magento/Swatches/view/frontend/web/css/swatches.css index dc7b1abd65bf9..67b9fffcf1282 100644 --- a/app/code/Magento/Swatches/view/frontend/web/css/swatches.css +++ b/app/code/Magento/Swatches/view/frontend/web/css/swatches.css @@ -35,7 +35,7 @@ /*width: 30px;*/ padding: 1px 2px; min-width: 30px; - max-width: 90px; + max-width: 100%; height: 20px; float: left; margin: 0 10px 5px 0; From 7a2c440c8e6f288d1718662548846eff4a132cc8 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 30 Oct 2018 14:25:34 +0200 Subject: [PATCH 210/240] MAGETWO-86419: Some products use Categories Path for Product URLs --- .../Magento/CatalogImportExport/Model/Import/ProductTest.php | 1 + 1 file changed, 1 insertion(+) 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 a9c24829f9ad8..f3cb01a9bbe2e 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -1070,6 +1070,7 @@ public function testProductsWithMultipleStores() * * @magentoDataFixture Magento/Store/_files/core_fixturestore.php * @magentoDataFixture Magento/Catalog/_files/category_with_two_stores.php + * @magentoDataFixture Magento/Catalog/_files/config_use_category_in_url.php * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ From 44667044ed7ed63add20dff0e77570e082139e16 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 30 Oct 2018 14:34:41 +0200 Subject: [PATCH 211/240] MAGETWO-86273: Admin user with role scope set to custom is unable to view abandoned carts report --- .../Model/ResourceModel/Quote/Collection.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Quote/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Quote/Collection.php index 671acc9701012..7b3e165687bac 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Quote/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Quote/Collection.php @@ -5,7 +5,11 @@ */ namespace Magento\Reports\Model\ResourceModel\Quote; +use Magento\Store\Model\Store; + /** + * Collection of abandoned quotes with reports join. + * * @api * @since 100.0.2 */ @@ -48,6 +52,24 @@ public function __construct( $this->customerResource = $customerResource; } + /** + * Filter collections by stores. + * + * @param array $storeIds + * @param bool $withAdmin + * @return $this + */ + public function addStoreFilter(array $storeIds, bool $withAdmin = true) + { + if ($withAdmin) { + $storeIds[] = Store::DEFAULT_STORE_ID; + } + + $this->addFieldToFilter('store_id', ['in' => $storeIds]); + + return $this; + } + /** * Prepare for abandoned report * From b0066e3558333231f2683bb3f614d393f0ef21d4 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 30 Oct 2018 14:40:32 +0200 Subject: [PATCH 212/240] MAGETWO-73301: [Magento Cloud] - Admin able to send order comment email without Sales Email privileges --- app/code/Magento/Sales/Block/Adminhtml/Order/View/History.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/View/History.php b/app/code/Magento/Sales/Block/Adminhtml/Order/View/History.php index 1912655a9292d..10b80b6f4e527 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/View/History.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/View/History.php @@ -88,7 +88,8 @@ public function getStatuses() */ public function canSendCommentEmail() { - return $this->_salesData->canSendOrderCommentEmail($this->getOrder()->getStore()->getId()); + return $this->_salesData->canSendOrderCommentEmail($this->getOrder()->getStore()->getId()) + && $this->_authorization->isAllowed('Magento_Sales::email'); } /** From e94a42313d6e8cec819475ea1de144ec34b4464a Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 30 Oct 2018 15:26:45 +0200 Subject: [PATCH 213/240] ENGCOM-970: Fixed cast to integer. --- .../Magento/GroupedProduct/Model/Product/Type/Grouped.php | 2 +- .../Magento/GroupedProduct/Model/Product/Type/GroupedTest.php | 4 ++-- .../_files/product_grouped_with_out_of_stock.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 0b88f0f3a00f9..7bdf7c0112795 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -341,7 +341,7 @@ protected function getProductInfo(\Magento\Framework\DataObject $buyRequest, $pr if ($isStrictProcessMode && !$subProduct->getQty()) { return __('Please specify the quantity of product(s).')->render(); } - $productsInfo[$subProduct->getId()] = $subProduct->isSalable() ? (int)$subProduct->getQty() : 0; + $productsInfo[$subProduct->getId()] = $subProduct->isSalable() ? (float)$subProduct->getQty() : 0; } } diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index d53c7983e5b1e..ed283d196e69c 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -214,7 +214,7 @@ public function outOfStockSubProductDataProvider() ], ], [ - 'virtual-product' => 2, // This is a default quantity. + 'virtual-product' => 2.5, // This is a default quantity. ], ], 'Out of stock product are hidden #1' => [ @@ -242,7 +242,7 @@ public function outOfStockSubProductDataProvider() ], ], [ - 'virtual-product' => 2, // This is a default quantity. + 'virtual-product' => 2.5, // This is a default quantity. ], ], ]; diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php index b9f9046c4244c..7aa62b149b8c0 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php @@ -59,7 +59,7 @@ ->setLinkedProductSku($sku) ->setLinkedProductType($linkedProduct->getTypeId()) ->getExtensionAttributes() - ->setQty(2); + ->setQty(2.5); $newLinks[] = $productLink; } $product->setProductLinks($newLinks); From 128df13d303579e6f77189586c5e918894bba949 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 30 Oct 2018 17:10:43 +0200 Subject: [PATCH 214/240] MAGETWO-82530: Redirect fix when Unsubscribe execute --- .../Controller/UnSubscriberTest.php | 64 +++++++++++++++++++ .../Magento/Newsletter/_files/subscribers.php | 1 + 2 files changed, 65 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php new file mode 100644 index 0000000000000..d137813b0549d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Newsletter\Controller; + +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test Unsubscriber controller + * + * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + * @magentoAppArea frontend + */ +class UnSubscriberTest extends AbstractController +{ + + /** + * @var Subscriber + */ + private $model; + + protected function setUp() + { + $this->model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Newsletter\Model\Subscriber::class + ); + parent::setUp(); + } + + /** + * @return void + */ + public function testSuccessUnsubscribeSubscribedUser() + { + $subscriber = $this->model->loadByCustomerId(1); + $this->getRequest() + ->setParam('id', $subscriber->getId()) + ->setParam('code', 'zxayquyajua23iq29gxwu2eax2qb6gvy'); + + $this->dispatch('newsletter/subscriber/unsubscribe'); + + $this->assertSessionMessages($this->equalTo(['You unsubscribed.'])); + $this->assertRedirect($this->anything()); + } + + /** + * @return void + */ + public function testFailureUnsubscribeSubscribedUser() + { + $subscriber = $this->model->loadByCustomerId(1); + $this->getRequest() + ->setParam('id', $subscriber->getId()) + ->setParam('code', 'randomcode'); + + $this->dispatch('newsletter/subscriber/unsubscribe'); + + $this->assertSessionMessages($this->equalTo(['This is an invalid subscription confirmation code.'])); + $this->assertRedirect($this->anything()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/subscribers.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/subscribers.php index 84e72979cc4c5..b55c471c11235 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/_files/subscribers.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/subscribers.php @@ -22,6 +22,7 @@ $subscriber->setStoreId($currentStore) ->setCustomerId(1) ->setSubscriberEmail('customer@example.com') + ->setSubscriberConfirmCode('zxayquyajua23iq29gxwu2eax2qb6gvy') ->setSubscriberStatus(\Magento\Newsletter\Model\Subscriber::STATUS_SUBSCRIBED) ->save(); $firstSubscriberId = $subscriber->getId(); From e9d1d4779d8d13f380784c8bfec690177d259e3f Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 31 Oct 2018 11:37:22 +0200 Subject: [PATCH 215/240] MAGETWO-86419: Some products use Categories Path for Product URLs --- .../Magento/Catalog/Model/Product/Url.php | 2 +- .../_files/config_use_category_in_url.php | 26 +++---------------- .../config_use_category_in_url_rollback.php | 26 +++---------------- 3 files changed, 9 insertions(+), 45 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Url.php b/app/code/Magento/Catalog/Model/Product/Url.php index 2f2fa1fa8f302..a295f3fbe1a0f 100644 --- a/app/code/Magento/Catalog/Model/Product/Url.php +++ b/app/code/Magento/Catalog/Model/Product/Url.php @@ -47,7 +47,7 @@ class Url extends \Magento\Framework\DataObject protected $urlFinder; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ private $scopeConfig; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url.php index 785bab340de33..b1c3aa755fc04 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url.php @@ -5,29 +5,11 @@ */ use Magento\Catalog\Helper\Product; -use Magento\Config\Model\Config\Factory; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\TestFramework\Helper\Bootstrap; -$configData = [ - ScopeConfigInterface::SCOPE_TYPE_DEFAULT => [ - '' => [ - Product::XML_PATH_PRODUCT_URL_USE_CATEGORY => '1', - ], - ], -]; - $objectManager = Bootstrap::getObjectManager(); -/** @var Factory $configFactory */ -$configFactory = $objectManager->create(Factory::class); -foreach ($configData as $scope => $data) { - foreach ($data as $scopeCode => $scopeData) { - foreach ($scopeData as $path => $value) { - $config = $configFactory->create(); - $config->setScope($scope); - $config->setDataByPath($path, $value); - $config->save(); - } - } -} +/** @var \Magento\Config\Model\Config $config */ +$config = $objectManager->get(\Magento\Config\Model\Config::class); +$config->setDataByPath(Product::XML_PATH_PRODUCT_URL_USE_CATEGORY, 1); +$config->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url_rollback.php index d1a5fec5bd133..ff0e0784987dd 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/config_use_category_in_url_rollback.php @@ -5,29 +5,11 @@ */ use Magento\Catalog\Helper\Product; -use Magento\Config\Model\Config\Factory; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\TestFramework\Helper\Bootstrap; -$configData = [ - ScopeConfigInterface::SCOPE_TYPE_DEFAULT => [ - '' => [ - Product::XML_PATH_PRODUCT_URL_USE_CATEGORY => '0', - ], - ], -]; - $objectManager = Bootstrap::getObjectManager(); -/** @var Factory $configFactory */ -$configFactory = $objectManager->create(Factory::class); -foreach ($configData as $scope => $data) { - foreach ($data as $scopeCode => $scopeData) { - foreach ($scopeData as $path => $value) { - $config = $configFactory->create(); - $config->setScope($scope); - $config->setDataByPath($path, $value); - $config->save(); - } - } -} +/** @var \Magento\Config\Model\Config $config */ +$config = $objectManager->get(\Magento\Config\Model\Config::class); +$config->setDataByPath(Product::XML_PATH_PRODUCT_URL_USE_CATEGORY, 0); +$config->save(); From 2bb03296773ab56a8cc5ff46fb46dfad27c3270f Mon Sep 17 00:00:00 2001 From: ratnesh <ratnesh@webkul.com> Date: Wed, 31 Oct 2018 15:16:10 +0530 Subject: [PATCH 216/240] updating issue raise din define([ --- .../ConfigurableProduct/view/frontend/web/js/options-updater.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/options-updater.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/options-updater.js index 558a1fdf31085..6f18798303151 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/options-updater.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/options-updater.js @@ -61,6 +61,7 @@ define([ this.setProductOptions(cartData()); this.updateOptions(); }.bind(this)); + this.updateOptions(); }, /** From 7fe3376511f94da1c899bfb6a1c0a0ac283a4a88 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 31 Oct 2018 12:47:57 +0200 Subject: [PATCH 217/240] MAGETWO-82530: Redirect fix when Unsubscribe execute --- .../Controller/UnSubscriberTest.php | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php index d137813b0549d..e3d5a4f0a1f3c 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php @@ -7,21 +7,24 @@ namespace Magento\Newsletter\Controller; use Magento\TestFramework\TestCase\AbstractController; +use Magento\Framework\App\Config\Value; /** - * Test Unsubscriber controller + * Test Unsubscriber controller. * * @magentoDataFixture Magento/Newsletter/_files/subscribers.php * @magentoAppArea frontend */ class UnSubscriberTest extends AbstractController { - /** * @var Subscriber */ private $model; + /** + * @inheritdoc + */ protected function setUp() { $this->model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -43,7 +46,7 @@ public function testSuccessUnsubscribeSubscribedUser() $this->dispatch('newsletter/subscriber/unsubscribe'); $this->assertSessionMessages($this->equalTo(['You unsubscribed.'])); - $this->assertRedirect($this->anything()); + $this->assertRedirect($this->stringStartsWith($this->getBaseUrl())); } /** @@ -59,6 +62,17 @@ public function testFailureUnsubscribeSubscribedUser() $this->dispatch('newsletter/subscriber/unsubscribe'); $this->assertSessionMessages($this->equalTo(['This is an invalid subscription confirmation code.'])); - $this->assertRedirect($this->anything()); + $this->assertRedirect($this->stringStartsWith($this->getBaseUrl())); + } + + /** + * @return string + */ + private function getBaseUrl() + { + $configValue = $this->_objectManager->create(Value::class); + $configValue->load('web/unsecure/base_url', 'path'); + + return $configValue->getValue() ?: 'http://localhost/'; } } From 72f83efd6ab194a5034cda1f74fc19736fb73cf3 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 31 Oct 2018 13:54:39 +0200 Subject: [PATCH 218/240] MAGETWO-82530: Redirect fix when Unsubscribe execute --- .../Magento/Newsletter/Controller/UnSubscriberTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php index e3d5a4f0a1f3c..9cb526624bb11 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/UnSubscriberTest.php @@ -46,7 +46,7 @@ public function testSuccessUnsubscribeSubscribedUser() $this->dispatch('newsletter/subscriber/unsubscribe'); $this->assertSessionMessages($this->equalTo(['You unsubscribed.'])); - $this->assertRedirect($this->stringStartsWith($this->getBaseUrl())); + $this->assertRedirect($this->equalTo($this->getBaseUrl() . 'index.php/')); } /** @@ -62,7 +62,7 @@ public function testFailureUnsubscribeSubscribedUser() $this->dispatch('newsletter/subscriber/unsubscribe'); $this->assertSessionMessages($this->equalTo(['This is an invalid subscription confirmation code.'])); - $this->assertRedirect($this->stringStartsWith($this->getBaseUrl())); + $this->assertRedirect($this->equalTo($this->getBaseUrl() . 'index.php/')); } /** From 070a6c15e5b8d344ea1213d95e42ecbaba0cd54a Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Wed, 31 Oct 2018 15:27:56 +0200 Subject: [PATCH 219/240] MAGETWO-95846: Customer Cart Checkout error --- .../Item/ItemProductResolver.php | 50 +++--- .../Item/ItemProductResolverTest.php | 161 ++++++++++++------ 2 files changed, 140 insertions(+), 71 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index d63ff06b7a483..b32921a436ec8 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -13,9 +13,10 @@ use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Catalog\Model\Product; +use Magento\Store\Model\ScopeInterface; /** - * {@inheritdoc} + * Resolves the product from a configured item. */ class ItemProductResolver implements ItemResolverInterface { @@ -38,7 +39,10 @@ public function __construct(ScopeConfigInterface $scopeConfig) } /** - * {@inheritdoc} + * Get the final product from a configured item by product type and selection. + * + * @param ItemInterface $item + * @return ProductInterface */ public function getFinalProduct(ItemInterface $item): ProductInterface { @@ -46,20 +50,11 @@ public function getFinalProduct(ItemInterface $item): ProductInterface * Show parent product thumbnail if it must be always shown according to the related setting in system config * or if child thumbnail is not available. */ - $parentProduct = $item->getProduct(); - $finalProduct = $parentProduct; + $finalProduct = $item->getProduct(); $childProduct = $this->getChildProduct($item); - if ($childProduct !== $parentProduct) { - $configValue = $this->scopeConfig->getValue( - self::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - $childThumb = $childProduct->getData('thumbnail'); - $finalProduct = - ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') - ? $parentProduct - : $childProduct; + if ($childProduct !== null && $this->isUseChildProduct($childProduct)) { + $finalProduct = $childProduct; } return $finalProduct; @@ -69,17 +64,30 @@ public function getFinalProduct(ItemInterface $item): ProductInterface * Get item configurable child product. * * @param ItemInterface $item - * @return Product + * @return Product|null */ - private function getChildProduct(ItemInterface $item): Product + private function getChildProduct(ItemInterface $item) { $option = $item->getOptionByCode('simple_product'); - $product = $item->getProduct(); - if ($option) { - $product = $option->getProduct(); - } + return $option ? $option->getProduct() : null; + } - return $product; + /** + * Is need to use child product + * + * @param Product $childProduct + * @return bool + */ + private function isUseChildProduct(Product $childProduct): bool + { + $configValue = $this->scopeConfig->getValue( + self::CONFIG_THUMBNAIL_SOURCE, + ScopeInterface::SCOPE_STORE + ); + $childThumb = $childProduct->getData('thumbnail'); + return $configValue !== Thumbnail::OPTION_USE_PARENT_IMAGE + && $childThumb !== NULL + && $childThumb !== 'no_selection'; } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php index 28e3e5e4cb5c6..8df6dddc3eb5a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\ConfigurableProduct\Test\Unit\Model\Product\Configuration\Item; @@ -14,86 +13,148 @@ use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; use Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Store\Model\ScopeInterface; +use Magento\Quote\Model\Quote\Item\Option; +use PHPUnit\Framework\TestCase; /** - * Tests \Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver + * ItemProductResolver test */ -class ItemProductResolverTest extends \PHPUnit\Framework\TestCase +class ItemProductResolverTest extends TestCase { /** * @var ItemProductResolver */ - private $resolver; + private $model; /** - * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ItemInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $item; + + /** + * @var Product | \PHPUnit_Framework_MockObject_MockObject + */ + private $parentProduct; + + /** + * @var ScopeConfigInterface | \PHPUnit_Framework_MockObject_MockObject */ private $scopeConfig; /** - * @inheritdoc + * @var OptionInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $option; + + /** + * @var Product | \PHPUnit_Framework_MockObject_MockObject + */ + private $childProduct; + + /** + * Set up method + * + * @return void */ protected function setUp() { - $objectManagerHelper = new ObjectManager($this); - $this->scopeConfig = $this->createPartialMock(ScopeConfigInterface::class, ['getValue', 'isSetFlag']); - $this->resolver = $objectManagerHelper->getObject( - ItemProductResolver::class, - ['scopeConfig' => $this->scopeConfig] - ); + parent::setUp(); + + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->parentProduct = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->parentProduct + ->method('getSku') + ->willReturn('parent_product'); + + $this->childProduct = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->childProduct + ->method('getSku') + ->willReturn('child_product'); + + $this->option = $this->getMockBuilder(Option::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->option + ->method('getProduct') + ->willReturn($this->childProduct); + + $this->item = $this->getMockBuilder(ItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->item + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->parentProduct); + + $this->model = new ItemProductResolver($this->scopeConfig); } /** - * @param bool $existOption - * @param string $configImageSource + * Test for deleted child product from configurable product + * * @return void - * @dataProvider getFinalProductDataProvider */ - public function testGetFinalProduct(bool $existOption, string $configImageSource) + public function testGetFinalProductChildIsNull() { - $option = null; - $parentProduct = $this->createMock(Product::class); - $finalProduct = $parentProduct; - - if ($existOption) { - $childProduct = $this->createPartialMock(Product::class, ['getData']); - $childProduct->expects($this->once())->method('getData')->with('thumbnail')->willReturn('someImage'); - - $option = $this->createPartialMock( - OptionInterface::class, - ['getProduct', 'getValue'] - ); - $option->expects($this->once())->method('getProduct')->willReturn($childProduct); - - $this->scopeConfig->expects($this->once()) - ->method('getValue') - ->with(ItemProductResolver::CONFIG_THUMBNAIL_SOURCE, ScopeInterface::SCOPE_STORE) - ->willReturn($configImageSource); - - $finalProduct = ($configImageSource == Thumbnail::OPTION_USE_PARENT_IMAGE) ? $parentProduct : $childProduct; - } - - $item = $this->createPartialMock( - ItemInterface::class, - ['getProduct', 'getOptionByCode', 'getFileDownloadParams'] + $this->item->method('getOptionByCode') + ->willReturn(null); + + $finalProduct = $this->model->getFinalProduct($this->item); + $this->assertEquals( + $this->parentProduct->getSku(), + $finalProduct->getSku() ); - $item->expects($this->exactly(2))->method('getProduct')->willReturn($parentProduct); - $item->expects($this->once())->method('getOptionByCode')->with('simple_product')->willReturn($option); + } - $this->assertEquals($finalProduct, $this->resolver->getFinalProduct($item)); + /** + * Tests child product from configurable product + * + * @dataProvider provideScopeConfig + * @param string $expectedSku + * @param string $scopeValue + * @param string | null $thumbnail + * @return void + */ + public function testGetFinalProductChild($expectedSku, $scopeValue, $thumbnail) + { + $this->item->method('getOptionByCode') + ->willReturn($this->option); + + $this->childProduct->method('getData') + ->willReturn($thumbnail); + + $this->scopeConfig->method('getValue') + ->willReturn($scopeValue); + + $finalProduct = $this->model->getFinalProduct($this->item); + $this->assertEquals($expectedSku, $finalProduct->getSku()); } /** + * Data provider for scope test + * * @return array */ - public function getFinalProductDataProvider(): array + public function provideScopeConfig(): array { return [ - [false, Thumbnail::OPTION_USE_PARENT_IMAGE], - [true, Thumbnail::OPTION_USE_PARENT_IMAGE], - [true, Thumbnail::OPTION_USE_OWN_IMAGE], + ['child_product', Thumbnail::OPTION_USE_OWN_IMAGE, 'thumbnail'], + ['parent_product', Thumbnail::OPTION_USE_PARENT_IMAGE, 'thumbnail'], + + ['parent_product', Thumbnail::OPTION_USE_OWN_IMAGE, null], + ['parent_product', Thumbnail::OPTION_USE_OWN_IMAGE, 'no_selection'], + + ['parent_product', Thumbnail::OPTION_USE_PARENT_IMAGE, null], + ['parent_product', Thumbnail::OPTION_USE_PARENT_IMAGE, 'no_selection'], ]; } } From 4f602ccae14e1d32a789176fb3ea5dbefaddf7bc Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Wed, 31 Oct 2018 15:42:20 +0200 Subject: [PATCH 220/240] MAGETWO-95846: Customer Cart Checkout error --- .../Model/Product/Configuration/Item/ItemProductResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php index b32921a436ec8..ef757902097e4 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -87,7 +87,7 @@ private function isUseChildProduct(Product $childProduct): bool ); $childThumb = $childProduct->getData('thumbnail'); return $configValue !== Thumbnail::OPTION_USE_PARENT_IMAGE - && $childThumb !== NULL + && $childThumb !== null && $childThumb !== 'no_selection'; } } From 46c16d6b5fbe233a836c66c83aaad5cafff1c39b Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 19 Oct 2018 11:55:18 +0200 Subject: [PATCH 221/240] Restructure estimate-service.js --- .../web/js/model/cart/estimate-service.js | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js index 76e3d911e7d3f..f1da0ed7916e7 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js @@ -14,55 +14,70 @@ define([ 'use strict'; var rateProcessors = [], - totalsProcessors = []; + totalsProcessors = [], - quote.shippingAddress.subscribe(function () { - var type = quote.shippingAddress().getType(); + /** + * Estimate totals for shipping address and update shipping rates. + */ + estimateTotalsAndUpdateRates = function() { + var type = quote.shippingAddress().getType(); - if ( - quote.isVirtual() || - window.checkoutConfig.activeCarriers && window.checkoutConfig.activeCarriers.length === 0 - ) { - // update totals block when estimated address was set - totalsProcessors['default'] = totalsDefaultProvider; - totalsProcessors[type] ? - totalsProcessors[type].estimateTotals(quote.shippingAddress()) : - totalsProcessors['default'].estimateTotals(quote.shippingAddress()); - } else { - // check if user data not changed -> load rates from cache - if (!cartCache.isChanged('address', quote.shippingAddress()) && - !cartCache.isChanged('cartVersion', customerData.get('cart')()['data_id']) && - cartCache.get('rates') + if ( + quote.isVirtual() || + window.checkoutConfig.activeCarriers && window.checkoutConfig.activeCarriers.length === 0 ) { - shippingService.setShippingRates(cartCache.get('rates')); + // update totals block when estimated address was set + totalsProcessors['default'] = totalsDefaultProvider; + totalsProcessors[type] ? + totalsProcessors[type].estimateTotals(quote.shippingAddress()) : + totalsProcessors['default'].estimateTotals(quote.shippingAddress()); + } else { + // check if user data not changed -> load rates from cache + if (!cartCache.isChanged('address', quote.shippingAddress()) && + !cartCache.isChanged('cartVersion', customerData.get('cart')()['data_id']) && + cartCache.get('rates') + ) { + shippingService.setShippingRates(cartCache.get('rates')); - return; + return; + } + + // update rates list when estimated address was set + rateProcessors['default'] = defaultProcessor; + rateProcessors[type] ? + rateProcessors[type].getRates(quote.shippingAddress()) : + rateProcessors['default'].getRates(quote.shippingAddress()); + + // save rates to cache after load + shippingService.getShippingRates().subscribe(function (rates) { + cartCache.set('rates', rates); + }); } + }, - // update rates list when estimated address was set - rateProcessors['default'] = defaultProcessor; - rateProcessors[type] ? - rateProcessors[type].getRates(quote.shippingAddress()) : - rateProcessors['default'].getRates(quote.shippingAddress()); + /** + * Estimate totals for shipping address. + */ + estimateTotalsShipping = function() { + totalsDefaultProvider.estimateTotals(quote.shippingAddress()); + }, - // save rates to cache after load - shippingService.getShippingRates().subscribe(function (rates) { - cartCache.set('rates', rates); - }); - } - }); - quote.shippingMethod.subscribe(function () { - totalsDefaultProvider.estimateTotals(quote.shippingAddress()); - }); - quote.billingAddress.subscribe(function () { - var type = quote.billingAddress().getType(); + /** + * Estimate totals for billing address. + */ + estimateTotalsBilling = function() { + var type = quote.billingAddress().getType(); + + if (quote.isVirtual()) { + // update totals block when estimated address was set + totalsProcessors['default'] = totalsDefaultProvider; + totalsProcessors[type] ? + totalsProcessors[type].estimateTotals(quote.billingAddress()) : + totalsProcessors['default'].estimateTotals(quote.billingAddress()); + } + }; - if (quote.isVirtual()) { - // update totals block when estimated address was set - totalsProcessors['default'] = totalsDefaultProvider; - totalsProcessors[type] ? - totalsProcessors[type].estimateTotals(quote.billingAddress()) : - totalsProcessors['default'].estimateTotals(quote.billingAddress()); - } - }); + quote.shippingAddress.subscribe(estimateTotalsAndUpdateRates); + quote.shippingMethod.subscribe(estimateTotalsShipping); + quote.billingAddress.subscribe(estimateTotalsBilling); }); From 4e2ab44128c7b951d7efce0e1c8816082c340870 Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 19 Oct 2018 11:55:55 +0200 Subject: [PATCH 222/240] Estimate totals when cart data changes --- .../Checkout/view/frontend/web/js/model/cart/estimate-service.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js index f1da0ed7916e7..f85a1385e0815 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js @@ -80,4 +80,5 @@ define([ quote.shippingAddress.subscribe(estimateTotalsAndUpdateRates); quote.shippingMethod.subscribe(estimateTotalsShipping); quote.billingAddress.subscribe(estimateTotalsBilling); + customerData.get('cart').subscribe(estimateTotalsShipping); }); From b23c7907af09be31fa0bfbee188748f1307e148e Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 19 Oct 2018 14:02:15 +0200 Subject: [PATCH 223/240] Fix js static error --- .../view/frontend/web/js/model/cart/estimate-service.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js index f85a1385e0815..54e496131972e 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/estimate-service.js @@ -19,7 +19,7 @@ define([ /** * Estimate totals for shipping address and update shipping rates. */ - estimateTotalsAndUpdateRates = function() { + estimateTotalsAndUpdateRates = function () { var type = quote.shippingAddress().getType(); if ( @@ -58,14 +58,14 @@ define([ /** * Estimate totals for shipping address. */ - estimateTotalsShipping = function() { + estimateTotalsShipping = function () { totalsDefaultProvider.estimateTotals(quote.shippingAddress()); }, /** * Estimate totals for billing address. */ - estimateTotalsBilling = function() { + estimateTotalsBilling = function () { var type = quote.billingAddress().getType(); if (quote.isVirtual()) { From 4787cf15af6a5edee574e543f550cf8cffabad04 Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 19 Oct 2018 14:16:39 +0200 Subject: [PATCH 224/240] Add test for cart data mutations/estimation call --- .../Checkout/frontend/js/model/cart/estimate-service.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js index 216ad28cf496a..ec1bf30fc4e8e 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js @@ -143,5 +143,10 @@ define([ }); expect(mocks['Magento_Checkout/js/model/cart/totals-processor/default'].estimateTotals).toHaveBeenCalled(); }); + + it('test subscribe when cart data was changed', function () { + mocks['Magento_Customer/js/customer-data'].get('cart')({ data_id: 2 }); + expect(mocks['Magento_Checkout/js/model/cart/totals-processor/default'].estimateTotals).toHaveBeenCalled(); + }); }); }); From 66294452bd0fc6bcadc7360c438da79ca1f6c314 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Thu, 25 Oct 2018 11:44:36 +0300 Subject: [PATCH 225/240] Added form fieldset before html to \Magento\Framework\Data\Form\Element\Fieldset in getElementHtml() method --- lib/internal/Magento/Framework/Data/Form/Element/Fieldset.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Fieldset.php b/lib/internal/Magento/Framework/Data/Form/Element/Fieldset.php index 66b1299b98696..90482ab55fc71 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Fieldset.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Fieldset.php @@ -43,7 +43,8 @@ public function __construct( */ public function getElementHtml() { - $html = '<fieldset id="' . $this->getHtmlId() . '"' . $this->serialize( + $html = $this->getBeforeElementHtml(); + $html .= '<fieldset area-hidden="false" id="' . $this->getHtmlId() . '"' . $this->serialize( ['class'] ) . $this->_getUiId() . '>' . "\n"; if ($this->getLegend()) { From 8c98e82bbc0a6300d83dcf9269ca16a25edf46de Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 28 Oct 2018 09:40:23 +0200 Subject: [PATCH 226/240] Remove duplicated selector --- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index 10b917635bd40..be5863f726d33 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -632,9 +632,6 @@ &-item-details { padding-bottom: 35px; - } - - &-item-details { display: table-cell; vertical-align: top; white-space: normal; From 1c7b6ccd4db558539da93509828230fd4992fbdf Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 2 Nov 2018 09:30:55 +0200 Subject: [PATCH 227/240] MAGETWO-95461: Cannot save product with Tier Prices --- app/code/Magento/Catalog/Helper/Data.php | 19 ++- .../Backend/TierPrice/UpdateHandler.php | 17 ++- .../Controller/Adminhtml/ProductTest.php | 110 +++++++++++++++++- 3 files changed, 127 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Data.php b/app/code/Magento/Catalog/Helper/Data.php index 7d153399f8ec0..495973176be12 100644 --- a/app/code/Magento/Catalog/Helper/Data.php +++ b/app/code/Magento/Catalog/Helper/Data.php @@ -7,6 +7,7 @@ use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Store\Model\ScopeInterface; use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Pricing\PriceCurrencyInterface; @@ -269,7 +270,8 @@ public function setStoreId($store) /** * Return current category path or get it from current category - * and creating array of categories|product paths for breadcrumbs + * + * Creating array of categories|product paths for breadcrumbs * * @return array */ @@ -378,6 +380,7 @@ public function getLastViewedUrl() /** * Split SKU of an item by dashes and spaces + * * Words will not be broken, unless this length is greater than $length * * @param string $sku @@ -406,14 +409,15 @@ public function getAttributeHiddenFields() /** * Retrieve Catalog Price Scope * - * @return int + * @return int|null */ public function getPriceScope() { - return $this->scopeConfig->getValue( + $priceScope = $this->scopeConfig->getValue( self::XML_PATH_PRICE_SCOPE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE ); + return isset($priceScope) ? (int)$priceScope : null; } /** @@ -449,7 +453,7 @@ public function isUrlDirectivesParsingAllowed() { return $this->scopeConfig->isSetFlag( self::CONFIG_PARSE_URL_DIRECTIVES, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $this->_storeId ); } @@ -466,6 +470,7 @@ public function getPageTemplateProcessor() /** * Whether to display items count for each filter option + * * @param int $storeId Store view ID * @return bool */ @@ -473,12 +478,14 @@ public function shouldDisplayProductCountOnLayer($storeId = null) { return $this->scopeConfig->isSetFlag( self::XML_PATH_DISPLAY_PRODUCT_COUNT, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); } /** + * Convert tax address array to address data object with country id and postcode + * * @param array $taxAddress * @return \Magento\Customer\Api\Data\AddressInterface|null */ diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php index b23dc6f30f8fa..9b9b007245b97 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php @@ -82,14 +82,14 @@ public function execute($entity, $arguments = []) __('Tier prices data should be array, but actually other type is received') ); } - $websiteId = $this->storeManager->getStore($entity->getStoreId())->getWebsiteId(); + $websiteId = (int)$this->storeManager->getStore($entity->getStoreId())->getWebsiteId(); $isGlobal = $attribute->isScopeGlobal() || $websiteId === 0; $identifierField = $this->metadataPoll->getMetadata(ProductInterface::class)->getLinkField(); - $productId = $entity->getData($identifierField); + $productId = (int)$entity->getData($identifierField); // prepare original data to compare $origPrices = $entity->getOrigData($attribute->getName()); - $old = $this->prepareOriginalDataToCompare($origPrices, $isGlobal); + $old = $this->prepareOldTierPriceToCompare($origPrices); // prepare data for save $new = $this->prepareNewDataForSave($priceRows, $isGlobal); @@ -262,19 +262,18 @@ private function isWebsiteGlobal(int $websiteId): bool } /** + * Prepare old data to compare. + * * @param array|null $origPrices - * @param bool $isGlobal * @return array */ - private function prepareOriginalDataToCompare($origPrices, $isGlobal = true): array + private function prepareOldTierPriceToCompare($origPrices): array { $old = []; if (is_array($origPrices)) { foreach ($origPrices as $data) { - if ($isGlobal === $this->isWebsiteGlobal((int)$data['website_id'])) { - $key = $this->getPriceKey($data); - $old[$key] = $data; - } + $key = $this->getPriceKey($data); + $old[$key] = $data; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index e01f4aec401a2..ddd5edb21eb26 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -9,6 +9,8 @@ use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Message\Manager; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Message\MessageInterface; /** * @magentoAppArea adminhtml @@ -21,7 +23,7 @@ public function testSaveActionWithDangerRequest() $this->dispatch('backend/catalog/product/save'); $this->assertSessionMessages( $this->equalTo(['Unable to save product']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR + MessageInterface::TYPE_ERROR ); $this->assertRedirect($this->stringContains('/backend/catalog/product/new')); } @@ -38,7 +40,7 @@ public function testSaveActionAndNew() $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/new/')); $this->assertSessionMessages( $this->contains('You saved the product.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); } @@ -61,11 +63,11 @@ public function testSaveActionAndDuplicate() ); $this->assertSessionMessages( $this->contains('You saved the product.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); $this->assertSessionMessages( $this->contains('You duplicated the product.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); } @@ -236,4 +238,104 @@ public function saveActionWithAlreadyExistingUrlKeyDataProvider() ] ]; } + + /** + * Test product save with selected tier price + * + * @dataProvider saveActionTierPriceDataProvider + * @param array $postData + * @param array $tierPrice + * @magentoDataFixture Magento/Catalog/_files/product_has_tier_price_show_as_low_as.php + * @magentoConfigFixture current_store catalog/price/scope 1 + */ + public function testSaveActionTierPrice(array $postData, array $tierPrice) + { + $postData['product'] = $this->getProductData($tierPrice); + $this->getRequest()->setPostValue($postData); + $this->dispatch('backend/catalog/product/save/id/'); + $this->assertSessionMessages( + $this->contains('You saved the product.'), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Provide test data for testSaveActionWithAlreadyExistingUrlKey(). + * + * @return array + */ + public function saveActionTierPriceDataProvider() + { + return [ + [ + 'post_data' => [ + 'id' => '1', + 'type' => 'simple', + 'store' => '0', + 'set' => '4', + 'back' => 'edit', + 'product' => [], + 'is_downloadable' => '0', + 'affect_configurable_product_attributes' => '1', + 'new_variation_attribute_set_id' => '4', + 'use_default' => [ + 'gift_message_available' => '0', + 'gift_wrapping_available' => '0' + ], + 'configurable_matrix_serialized' => '[]', + 'associated_product_ids_serialized' => '[]' + ], + 'tier_price_for_request' => [ + [ + 'price_id' => '1', + 'website_id' => '0', + 'cust_group' => '32000', + 'price' => '111.00', + 'price_qty' => '100', + 'website_price' => '111.0000', + 'initialize' => 'true', + 'record_id' => '1', + 'value_type' => 'fixed' + ], + [ + 'price_id' => '2', + 'website_id' => '1', + 'cust_group' => '32000', + 'price' => '222.00', + 'price_qty' => '200', + 'website_price' => '111.0000', + 'initialize' => 'true', + 'record_id' => '2', + 'value_type' => 'fixed' + ], + [ + 'price_id' => '3', + 'website_id' => '1', + 'cust_group' => '32000', + 'price' => '333.00', + 'price_qty' => '300', + 'website_price' => '111.0000', + 'initialize' => 'true', + 'record_id' => '3', + 'value_type' => 'fixed' + ] + ] + ] + ]; + } + + /** + * Return product data for test without entity_id for further save + * + * @param array $tierPrice + * @return array + */ + private function getProductData(array $tierPrice) + { + $productRepositoryInterface = $this->_objectManager->get(ProductRepositoryInterface::class); + $product = $productRepositoryInterface->get('tier_prices')->getData(); + $product['tier_price'] = $tierPrice; + unset($product['entity_id']); + return $product; + } } From 0a1a807c5c3a17b0d80bdb34687b8b06cfd0c04f Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 2 Nov 2018 10:00:02 +0200 Subject: [PATCH 228/240] MAGETWO-95461: Cannot save product with Tier Prices --- .../Magento/Catalog/Controller/Adminhtml/ProductTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index ddd5edb21eb26..4761f13175d81 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -252,7 +252,7 @@ public function testSaveActionTierPrice(array $postData, array $tierPrice) { $postData['product'] = $this->getProductData($tierPrice); $this->getRequest()->setPostValue($postData); - $this->dispatch('backend/catalog/product/save/id/'); + $this->dispatch('backend/catalog/product/save/id/' . $postData['id']); $this->assertSessionMessages( $this->contains('You saved the product.'), MessageInterface::TYPE_SUCCESS From 4c13ad766c85ea4514de90e0c9d0c035d5fc0172 Mon Sep 17 00:00:00 2001 From: Rostyslav Sabishchenko <rostislavs@mageside.com> Date: Fri, 2 Nov 2018 13:35:20 +0200 Subject: [PATCH 229/240] #18348 - In admin, last swatch option set to default upon save --- .../Swatches/view/adminhtml/web/js/product-attributes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js index 4c9df6ef657d7..3a40370fe8194 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js @@ -439,6 +439,9 @@ define([ activePanel.find('table input') .each(function () { + if ($(this).is(':radio') && !$(this).prop("checked")) { + return; + } swatchValues.push(this.name + '=' + $(this).val()); }); From 48cbb79d0c5decb4acc3d93498527c2af3fe4403 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 2 Nov 2018 14:30:03 +0200 Subject: [PATCH 230/240] magento-engcom/magento2ce#2301: Code style fixes --- .../Checkout/frontend/js/model/cart/estimate-service.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js index ec1bf30fc4e8e..0748fb64aca56 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/estimate-service.test.js @@ -145,7 +145,9 @@ define([ }); it('test subscribe when cart data was changed', function () { - mocks['Magento_Customer/js/customer-data'].get('cart')({ data_id: 2 }); + mocks['Magento_Customer/js/customer-data'].get('cart')({ + dataId: 2 + }); expect(mocks['Magento_Checkout/js/model/cart/totals-processor/default'].estimateTotals).toHaveBeenCalled(); }); }); From 429815bee09f999ef3e38b93a5c097363fe2cbbd Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Fri, 2 Nov 2018 16:42:45 +0200 Subject: [PATCH 231/240] magento/magento2#18323: Order confirmation email for guest checkout does not include download links. - move save downloadable items from 'save_commit_after' to 'save_after' event. --- app/code/Magento/Downloadable/etc/events.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/etc/events.xml b/app/code/Magento/Downloadable/etc/events.xml index e4f03ff238d4a..5a985fc33802e 100644 --- a/app/code/Magento/Downloadable/etc/events.xml +++ b/app/code/Magento/Downloadable/etc/events.xml @@ -6,10 +6,10 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="sales_order_item_save_commit_after"> + <event name="sales_order_item_save_after"> <observer name="downloadable_observer" instance="Magento\Downloadable\Observer\SaveDownloadableOrderItemObserver" /> </event> - <event name="sales_order_save_commit_after"> + <event name="sales_order_save_after"> <observer name="downloadable_observer" instance="Magento\Downloadable\Observer\SetLinkStatusObserver" /> </event> <event name="sales_model_service_quote_submit_success"> From 52bfffc2d2f43891b27af2d6144c0b64be572f79 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi <v.podorozhnyi@ism-ukraine.com> Date: Fri, 2 Nov 2018 17:26:27 +0200 Subject: [PATCH 232/240] magento/magento2#14007: "Use in Layered Navigation: Filterable (no results)" not working for `Price` attribute. - adjust comment for "Use in Layered Navigation: Filterable (no results)" property to make it more understandable (cherry picked from commit e18df69) --- .../Tab/Front/ProductAttributeFormBuildFrontTabObserver.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/LayeredNavigation/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserver.php b/app/code/Magento/LayeredNavigation/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserver.php index fc1edeb1e2392..dc4d3579f4628 100644 --- a/app/code/Magento/LayeredNavigation/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserver.php +++ b/app/code/Magento/LayeredNavigation/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserver.php @@ -55,7 +55,11 @@ public function execute(\Magento\Framework\Event\Observer $observer) 'name' => 'is_filterable', 'label' => __("Use in Layered Navigation"), 'title' => __('Can be used only with catalog input type Yes/No, Dropdown, Multiple Select and Price'), - 'note' => __('Can be used only with catalog input type Yes/No, Dropdown, Multiple Select and Price.'), + 'note' => __( + 'Can be used only with catalog input type Yes/No, Dropdown, Multiple Select and Price. + <br>Price is not compatible with <b>\'Filterable (no results)\'</b> option - + it will make no affect on Price filter.' + ), 'values' => [ ['value' => '0', 'label' => __('No')], ['value' => '1', 'label' => __('Filterable (with results)')], From c8ead232063d3f4406cb77950643042643952609 Mon Sep 17 00:00:00 2001 From: Dmytro Salamatov <sal.dima27@gmail.com> Date: Tue, 6 Nov 2018 00:11:18 +0200 Subject: [PATCH 233/240] magento/magento2#19071: Password strength indicator shows No Password even when a password is entered --- .../view/frontend/web/js/password-strength-indicator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/password-strength-indicator.js b/app/code/Magento/Customer/view/frontend/web/js/password-strength-indicator.js index 89d9b320c049d..9742e37f2df57 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/password-strength-indicator.js +++ b/app/code/Magento/Customer/view/frontend/web/js/password-strength-indicator.js @@ -83,7 +83,7 @@ define([ } else { isValid = $.validator.validateSingleElement(this.options.cache.input); zxcvbnScore = zxcvbn(password).score; - displayScore = isValid ? zxcvbnScore : 1; + displayScore = isValid && zxcvbnScore > 0 ? zxcvbnScore : 1; } } From a5a0b0b5674b5fe19921d2cb9b403d0c6b9ecb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Eisenf=C3=BChrer?= <m.eisenfuehrer@techdivision.com> Date: Sat, 6 Oct 2018 16:57:46 +0200 Subject: [PATCH 234/240] fix issue 12399 --- .../Controller/Adminhtml/Promo/Catalog/Save.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index f3046c58a389b..81541b8b2ff32 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -60,6 +60,17 @@ public function execute() ['request' => $this->getRequest()] ); $data = $this->getRequest()->getPostValue(); + + $filterValues = ['from_date' => $this->_dateFilter]; + if ($this->getRequest()->getParam('to_date')) { + $filterValues['to_date'] = $this->_dateFilter; + } + $inputFilter = new \Zend_Filter_Input( + $filterValues, + [], + $data + ); + $data = $inputFilter->getUnescaped(); $id = $this->getRequest()->getParam('rule_id'); if ($id) { $model = $ruleRepository->get($id); From 09016828d776716fa6efac68df4a700477b604dd Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 6 Nov 2018 16:47:07 +0200 Subject: [PATCH 235/240] magento-engcom/magento2ce#2308: Code style fixes --- .../view/frontend/web/js/page-cache.js | 20 +++++++++++++------ .../adminhtml/web/js/product-attributes.js | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js index 2a43ff29a3593..b8f0d14066707 100644 --- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js +++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js @@ -42,7 +42,9 @@ define([ * @param {jQuery} element - Comment holder */ (function lookup(element) { - var iframeHostName; + var iframeHostName, + contents, + elementContents; // prevent cross origin iframe content reading if ($(element).prop('tagName') === 'IFRAME') { @@ -54,11 +56,17 @@ define([ } } - // rewrite jQuery contents() - var contents = function (element) { - return $.map(element, function (elem) { + /** + * Rewrite jQuery contents method + * + * @param {Object} el + * @returns {Object} + * @private + */ + contents = function (el) { + return $.map(el, function (elem) { try { - return $.nodeName(elem, "iframe") ? + return $.nodeName(elem, 'iframe') ? elem.contentDocument || (elem.contentWindow ? elem.contentWindow.document : []) : $.merge([], elem.childNodes); } catch (e) { @@ -68,7 +76,7 @@ define([ }); }; - var elementContents = contents($(element)); + elementContents = contents($(element)); $.each(elementContents, function (index, el) { switch (el.nodeType) { diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js index 3a40370fe8194..9534e039380d4 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js @@ -439,7 +439,7 @@ define([ activePanel.find('table input') .each(function () { - if ($(this).is(':radio') && !$(this).prop("checked")) { + if ($(this).is(':radio') && !$(this).prop('checked')) { return; } swatchValues.push(this.name + '=' + $(this).val()); From 965a52268cfb7f1e82788ee62a1ac8af85baa897 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 6 Nov 2018 17:55:35 +0200 Subject: [PATCH 236/240] magento-engcom/magento2ce#2308: Code style fixes --- app/code/Magento/PageCache/view/frontend/web/js/page-cache.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js index b8f0d14066707..9ae916356d2b9 100644 --- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js +++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js @@ -71,6 +71,7 @@ define([ $.merge([], elem.childNodes); } catch (e) { consoleLogger.error(e); + return []; } }); From a651b31ec990cdd06d4a605a46a602ac1def0048 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Tue, 6 Nov 2018 22:22:07 +0200 Subject: [PATCH 237/240] magento/magento#18901: Forgot password form should not available while customer is logged in. - redirect logged in customers to My Account. --- .../Customer/Controller/Account/ForgotPassword.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Account/ForgotPassword.php b/app/code/Magento/Customer/Controller/Account/ForgotPassword.php index f115b64efebdd..cd2c46ff53043 100644 --- a/app/code/Magento/Customer/Controller/Account/ForgotPassword.php +++ b/app/code/Magento/Customer/Controller/Account/ForgotPassword.php @@ -40,10 +40,17 @@ public function __construct( /** * Forgot customer password page * - * @return \Magento\Framework\View\Result\Page + * @return \Magento\Framework\Controller\Result\Redirect|\Magento\Framework\View\Result\Page */ public function execute() { + if ($this->session->isLoggedIn()) { + /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath('*/*/'); + return $resultRedirect; + } + /** @var \Magento\Framework\View\Result\Page $resultPage */ $resultPage = $this->resultPageFactory->create(); $resultPage->getLayout()->getBlock('forgotPassword')->setEmailValue($this->session->getForgottenEmail()); From 27dc8e82de328af66a82f1402cb5671f87d80115 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 7 Nov 2018 17:11:39 +0200 Subject: [PATCH 238/240] Fixed static failure --- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index be5863f726d33..1015bb584ff7b 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -631,8 +631,8 @@ } &-item-details { - padding-bottom: 35px; display: table-cell; + padding-bottom: 35px; vertical-align: top; white-space: normal; width: 99%; From 252a0ed8225d03ff36e0c4139e46d37db8ad7402 Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Wed, 7 Nov 2018 17:21:09 +0200 Subject: [PATCH 239/240] magento/magento2#18840: Invalid Unit Test Annotations. - fixed invalid unit tests annotations that assert exception messages - backport to 2.2. --- .../Unit/Model/Product/Gallery/GalleryManagementTest.php | 2 +- .../Catalog/Test/Unit/Model/Product/PriceModifierTest.php | 6 +++--- .../Test/Unit/Model/Product/TierPriceManagementTest.php | 2 +- app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php | 2 +- .../Downloadable/Test/Unit/Helper/DownloadTest.php | 2 +- .../GiftMessage/Test/Unit/Model/Plugin/OrderSaveTest.php | 8 ++++---- .../GuestCartManagement/Plugin/AuthorizationTest.php | 2 +- .../Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php | 2 +- .../Test/Unit/Model/ShippingAddressManagementTest.php | 2 +- .../Search/Test/Unit/Model/SynonymGroupRepositoryTest.php | 4 ++-- .../Theme/Test/Unit/Model/Theme/Source/ThemeTest.php | 1 - .../Ui/Test/Unit/Component/Filters/FilterModifierTest.php | 5 +++-- .../Magento/Framework/App/Test/Unit/ErrorHandlerTest.php | 4 ++-- .../App/Test/Unit/Response/Http/FileFactoryTest.php | 2 +- lib/internal/Magento/Framework/App/Test/Unit/ViewTest.php | 2 +- .../Profiler/Test/Unit/Driver/Standard/StatTest.php | 4 ++-- .../Setup/Test/Unit/Module/I18n/Parser/ParserTest.php | 2 +- 17 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php index d80cf3cb9bfa2..53ac1d4d9e4df 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php @@ -268,7 +268,7 @@ public function testGetWithNonExistingProduct() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionText Such image doesn't exist + * @expectedExceptionMessage Such image doesn't exist */ public function testGetWithNonExistingImage() { diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifierTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifierTest.php index 75fbad0aab262..17711a18f7cfa 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifierTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/PriceModifierTest.php @@ -56,7 +56,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedMessage This product doesn't have tier price + * @expectedExceptionMessage Product hasn't group price with such data: customerGroupId = '1', website = 1, qty = 3 */ public function testRemoveWhenTierPricesNotExists() { @@ -72,7 +72,7 @@ public function testRemoveWhenTierPricesNotExists() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedMessage For current customerGroupId = '10' with 'qty' = 15 any tier price exist'. + * @expectedExceptionMessage Product hasn't group price with such data: customerGroupId = '10', website = 1, qty = 5 */ public function testRemoveTierPriceForNonExistingCustomerGroup() { @@ -83,7 +83,7 @@ public function testRemoveTierPriceForNonExistingCustomerGroup() ->will($this->returnValue($this->prices)); $this->productMock->expects($this->never())->method('setData'); $this->productRepositoryMock->expects($this->never())->method('save'); - $this->priceModifier->removeTierPrice($this->productMock, 10, 15, 1); + $this->priceModifier->removeTierPrice($this->productMock, 10, 5, 1); } public function testSuccessfullyRemoveTierPriceSpecifiedForAllGroups() diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php index 6d42a33d1fc73..15d99aee538b0 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/TierPriceManagementTest.php @@ -188,7 +188,7 @@ public function testSuccessDeleteTierPrice() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @message Such product doesn't exist + * @expectedExceptionMessage No such entity. */ public function testDeleteTierPriceFromNonExistingProduct() { diff --git a/app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php b/app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php index 29537e8ec0526..bcc9f1bc94bd7 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/SidebarTest.php @@ -94,7 +94,7 @@ public function testCheckQuoteItem() /** * @expectedException \Magento\Framework\Exception\LocalizedException - * @exceptedExceptionMessage We can't find the quote item. + * @expectedExceptionMessage We can't find the quote item. */ public function testCheckQuoteItemWithException() { diff --git a/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php b/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php index 3142900613296..4cd43ce12f49b 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php @@ -89,7 +89,7 @@ public function testSetResourceInvalidPath() /** * @expectedException \Magento\Framework\Exception\LocalizedException - * @exectedExceptionMessage Please set resource file and link type. + * @expectedExceptionMessage Please set resource file and link type. */ public function testGetFileSizeNoResource() { diff --git a/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/OrderSaveTest.php b/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/OrderSaveTest.php index ec8a8841f6477..608bef3ee091b 100644 --- a/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/OrderSaveTest.php +++ b/app/code/Magento/GiftMessage/Test/Unit/Model/Plugin/OrderSaveTest.php @@ -128,7 +128,7 @@ public function testAfterSaveGiftMessages() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedMessage Could not add gift message to order:Test message + * @expectedExceptionMessage Could not add gift message to order: "Test message" */ public function testAfterSaveIfGiftMessagesNotExist() { @@ -146,7 +146,7 @@ public function testAfterSaveIfGiftMessagesNotExist() $this->giftMessageOrderRepositoryMock ->expects($this->once()) ->method('save') - ->willThrowException(new \Exception('TestMessage')); + ->willThrowException(new \Exception('Test message')); // save Gift Messages on item level $this->orderMock->expects($this->never())->method('getItems'); @@ -155,7 +155,7 @@ public function testAfterSaveIfGiftMessagesNotExist() /** * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedMessage Could not add gift message to order:Test message + * @expectedExceptionMessage Could not add gift message to order's item: "Test message" */ public function testAfterSaveIfItemGiftMessagesNotExist() { @@ -185,7 +185,7 @@ public function testAfterSaveIfItemGiftMessagesNotExist() $this->giftMessageOrderItemRepositoryMock ->expects($this->once())->method('save') ->with($orderId, $orderItemId, $this->giftMessageMock) - ->willThrowException(new \Exception('TestMessage')); + ->willThrowException(new \Exception('Test message')); $this->plugin->afterSave($this->orderRepositoryMock, $this->orderMock); } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/GuestCartManagement/Plugin/AuthorizationTest.php b/app/code/Magento/Quote/Test/Unit/Model/GuestCartManagement/Plugin/AuthorizationTest.php index 9cde4bbc19184..d67ebdd5a0fc2 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/GuestCartManagement/Plugin/AuthorizationTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/GuestCartManagement/Plugin/AuthorizationTest.php @@ -36,7 +36,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\StateException - * @expectedMessage Cannot assign customer to the given cart. You don't have permission for this operation. + * @expectedExceptionMessage Cannot assign customer to the given cart. You don't have permission for this operation. */ public function testBeforeAssignCustomer() { diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php index 03c3e32c20cb9..c83eec9eb51d2 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Item/UpdaterTest.php @@ -92,7 +92,7 @@ protected function setUp() /** * @expectedException \InvalidArgumentException - * @ExceptedExceptionMessage The qty value is required to update quote item. + * @expectedExceptionMessage The qty value is required to update quote item. */ public function testUpdateNoQty() { diff --git a/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php index 1870fa9dc81ba..59445c3999899 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php @@ -100,7 +100,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expected ExceptionMessage error345 + * @expectedExceptionMessage error345 */ public function testSetAddressValidationFailed() { diff --git a/app/code/Magento/Search/Test/Unit/Model/SynonymGroupRepositoryTest.php b/app/code/Magento/Search/Test/Unit/Model/SynonymGroupRepositoryTest.php index f62c07b149c0e..4532479c482b5 100644 --- a/app/code/Magento/Search/Test/Unit/Model/SynonymGroupRepositoryTest.php +++ b/app/code/Magento/Search/Test/Unit/Model/SynonymGroupRepositoryTest.php @@ -53,7 +53,7 @@ public function testSaveCreate() /** * @expectedException \Magento\Search\Model\Synonym\MergeConflictException - * @expecteExceptionMessage (c,d,e) + * @expectedExceptionMessage Merge conflict with existing synonym group(s): (a,b,c) */ public function testSaveCreateMergeConflict() { @@ -138,7 +138,7 @@ public function testSaveUpdate() /** * @expectedException \Magento\Search\Model\Synonym\MergeConflictException - * @expecteExceptionMessage (d,h,i) + * @expectedExceptionMessage (d,h,i) */ public function testSaveUpdateMergeConflict() { diff --git a/app/code/Magento/Theme/Test/Unit/Model/Theme/Source/ThemeTest.php b/app/code/Magento/Theme/Test/Unit/Model/Theme/Source/ThemeTest.php index 5cb265d3d628b..c06e2626034a7 100644 --- a/app/code/Magento/Theme/Test/Unit/Model/Theme/Source/ThemeTest.php +++ b/app/code/Magento/Theme/Test/Unit/Model/Theme/Source/ThemeTest.php @@ -10,7 +10,6 @@ class ThemeTest extends \PHPUnit\Framework\TestCase { /** - * @true * @return void * @covers \Magento\Theme\Model\Theme\Source\Theme::__construct * @covers \Magento\Theme\Model\Theme\Source\Theme::getAllOptions diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/FilterModifierTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/FilterModifierTest.php index f91401e43ea80..50d82b19d1045 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/FilterModifierTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/FilterModifierTest.php @@ -66,7 +66,8 @@ public function testNotApplyFilterModifier() /** * @return void - * @assertException \Magento\Framework\Exception\LocalizedException + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Condition type "not_allowed" is not allowed */ public function testApplyFilterModifierWithNotAllowedCondition() { @@ -78,7 +79,7 @@ public function testApplyFilterModifierWithNotAllowedCondition() ] ]); $this->dataProvider->expects($this->never())->method('addFilter'); - $this->unit->applyFilterModifier($this->dataProvider, 'test'); + $this->unit->applyFilterModifier($this->dataProvider, 'filter'); } /** diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ErrorHandlerTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ErrorHandlerTest.php index daf3a4bdfab0c..fcab6b8e850c4 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/ErrorHandlerTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/ErrorHandlerTest.php @@ -53,9 +53,9 @@ public function testHandlerException($errorNo, $errorPhrase) $errorFile = 'test_file'; $errorLine = 'test_error_line'; - $exceptedExceptionMessage = sprintf('%s: %s in %s on line %s', $errorPhrase, $errorStr, $errorFile, $errorLine); + $expectedExceptionMessage = sprintf('%s: %s in %s on line %s', $errorPhrase, $errorStr, $errorFile, $errorLine); $this->expectException('Exception'); - $this->expectExceptionMessage($exceptedExceptionMessage); + $this->expectExceptionMessage($expectedExceptionMessage); $this->object->handler($errorNo, $errorStr, $errorFile, $errorLine); } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Response/Http/FileFactoryTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Response/Http/FileFactoryTest.php index d6b8f677860cc..3b7aacc1afba1 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Response/Http/FileFactoryTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Response/Http/FileFactoryTest.php @@ -67,7 +67,7 @@ public function testCreateIfContentDoesntHaveRequiredKeys() /** * @expectedException \Exception - * @exceptedExceptionMessage File not found + * @expectedExceptionMessage File not found */ public function testCreateIfFileNotExist() { diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ViewTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ViewTest.php index 807a70ecd7599..f39a91161c1f5 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/ViewTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/ViewTest.php @@ -122,7 +122,7 @@ public function testGetLayout() /** * @expectedException \RuntimeException - * @exceptedExceptionMessage 'Layout must be loaded only once.' + * @expectedExceptionMessage Layout must be loaded only once. */ public function testLoadLayoutWhenLayoutAlreadyLoaded() { diff --git a/lib/internal/Magento/Framework/Profiler/Test/Unit/Driver/Standard/StatTest.php b/lib/internal/Magento/Framework/Profiler/Test/Unit/Driver/Standard/StatTest.php index d694697284f8e..df74eeec972ba 100644 --- a/lib/internal/Magento/Framework/Profiler/Test/Unit/Driver/Standard/StatTest.php +++ b/lib/internal/Magento/Framework/Profiler/Test/Unit/Driver/Standard/StatTest.php @@ -395,7 +395,7 @@ public function fetchDataProvider() /** * @expectedException \InvalidArgumentException - * @expectedMessage Timer "foo" doesn't exist. + * @expectedExceptionMessage Timer "foo" doesn't exist. */ public function testFetchInvalidTimer() { @@ -404,7 +404,7 @@ public function testFetchInvalidTimer() /** * @expectedException \InvalidArgumentException - * @expectedMessage Timer "foo" doesn't have value for "bar". + * @expectedExceptionMessage Timer "foo" doesn't have value for "bar". */ public function testFetchInvalidKey() { diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/ParserTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/ParserTest.php index 36ce2e4ad7c45..e907c39bdd56e 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/ParserTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Parser/ParserTest.php @@ -41,7 +41,7 @@ protected function setUp() * @param array $jsFiles * @param array $phpMap * @param array $jsMap - * @paran array $phraseFactoryMap + * @param array $phraseFactoryMap * @param array $expectedResult * @dataProvider addPhraseDataProvider */ From 35c234560342f1ce5e259679a983d51cec0e2858 Mon Sep 17 00:00:00 2001 From: Yevhenii Dumskyi <yevhenii.dumskyi@gmail.com> Date: Mon, 5 Nov 2018 14:34:03 +0200 Subject: [PATCH 240/240] Add additional check if password hash is empty in auth process --- app/code/Magento/Customer/Model/Authentication.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Model/Authentication.php b/app/code/Magento/Customer/Model/Authentication.php index 0967f1a0189e3..b0729647d7eec 100644 --- a/app/code/Magento/Customer/Model/Authentication.php +++ b/app/code/Magento/Customer/Model/Authentication.php @@ -167,7 +167,7 @@ public function authenticate($customerId, $password) { $customerSecure = $this->customerRegistry->retrieveSecureData($customerId); $hash = $customerSecure->getPasswordHash(); - if (!$this->encryptor->validateHash($password, $hash)) { + if (!$hash || !$this->encryptor->validateHash($password, $hash)) { $this->processAuthenticationFailure($customerId); if ($this->isLocked($customerId)) { throw new UserLockedException(__('The account is locked.'));