diff --git a/app/code/Magento/Catalog/Block/Product/Widget/NewWidget.php b/app/code/Magento/Catalog/Block/Product/Widget/NewWidget.php index 88d487aea32c98e1f15c8129d288006acacafb13..1a8a421f0f2bcad446a6f5d73556836925804122 100644 --- a/app/code/Magento/Catalog/Block/Product/Widget/NewWidget.php +++ b/app/code/Magento/Catalog/Block/Product/Widget/NewWidget.php @@ -32,6 +32,8 @@ class NewWidget extends \Magento\Catalog\Block\Product\NewProduct implements \Ma /** * Name of request parameter for page number value + * + * @deprecated */ const PAGE_VAR_NAME = 'np'; @@ -88,7 +90,7 @@ class NewWidget extends \Magento\Catalog\Block\Product\NewProduct implements \Ma */ public function getCurrentPage() { - return abs((int)$this->getRequest()->getParam(self::PAGE_VAR_NAME)); + return abs((int)$this->getRequest()->getParam($this->getData('page_var_name'))); } /** @@ -103,7 +105,8 @@ class NewWidget extends \Magento\Catalog\Block\Product\NewProduct implements \Ma [ $this->getDisplayType(), $this->getProductsPerPage(), - intval($this->getRequest()->getParam(self::PAGE_VAR_NAME)) + intval($this->getRequest()->getParam($this->getData('page_var_name'), 1)), + serialize($this->getRequest()->getParams()) ] ); } @@ -187,7 +190,7 @@ class NewWidget extends \Magento\Catalog\Block\Product\NewProduct implements \Ma $this->_pager->setUseContainer(true) ->setShowAmounts(true) ->setShowPerPage(false) - ->setPageVarName(self::PAGE_VAR_NAME) + ->setPageVarName($this->getData('page_var_name')) ->setLimit($this->getProductsPerPage()) ->setTotalLimit($this->getProductsCount()) ->setCollection($this->getProductCollection()); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/SuggestAttributeSets.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/SuggestAttributeSets.php index 456a579c6191d9a284e0cf7f5f16a99d38e317d3..9b1a8c28bb17d6b57edd5c04b9923f1a9d28fb44 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/SuggestAttributeSets.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/SuggestAttributeSets.php @@ -8,6 +8,13 @@ namespace Magento\Catalog\Controller\Adminhtml\Product; class SuggestAttributeSets extends \Magento\Backend\App\Action { + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_Catalog::sets'; + /** * @var \Magento\Framework\Controller\Result\JsonFactory */ diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/SuggestAttributes.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/SuggestAttributes.php index 6b60c4f74810fc5cc445384205066ba752efe4a5..7047334f046e08bb7a6f4de737e8be40df76924a 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/SuggestAttributes.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/SuggestAttributes.php @@ -6,8 +6,15 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product; -class SuggestAttributes extends \Magento\Catalog\Controller\Adminhtml\Product +class SuggestAttributes extends \Magento\Backend\App\Action { + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_Catalog::attributes_attributes'; + /** * @var \Magento\Framework\Controller\Result\JsonFactory */ @@ -20,17 +27,15 @@ class SuggestAttributes extends \Magento\Catalog\Controller\Adminhtml\Product /** * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Catalog\Controller\Adminhtml\Product\Builder $productBuilder * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory * @param \Magento\Framework\View\LayoutFactory $layoutFactory */ public function __construct( \Magento\Backend\App\Action\Context $context, - \Magento\Catalog\Controller\Adminhtml\Product\Builder $productBuilder, \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, \Magento\Framework\View\LayoutFactory $layoutFactory ) { - parent::__construct($context, $productBuilder); + parent::__construct($context); $this->resultJsonFactory = $resultJsonFactory; $this->layoutFactory = $layoutFactory; } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/Widget/NewWidgetTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/Widget/NewWidgetTest.php index 9cd7790436de6419fc08f9533d386ab1250870fc..f21945f588abb5c34fbcea2411abc3bed758aca0 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/Widget/NewWidgetTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/Widget/NewWidgetTest.php @@ -144,9 +144,11 @@ class NewWidgetTest extends \PHPUnit_Framework_TestCase */ public function testGetCurrentPage($pageNumber, $expectedResult) { + $this->block->setData('page_var_name', 'page_number'); + $this->requestMock->expects($this->any()) ->method('getParam') - ->with(\Magento\Catalog\Block\Product\Widget\NewWidget::PAGE_VAR_NAME) + ->with('page_number') ->willReturn($pageNumber); $this->assertEquals($expectedResult, $this->block->getCurrentPage()); diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index 8f6792b862d2ce93c9e14e0086977ff83a3dd16f..1274db9598ebc6e7a12d0c3b6d0946dc30ce3ee3 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -234,9 +234,8 @@ <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">Magento\Store\Model\ResourceModel\Website\Collection</item> <item name="config" xsi:type="array"> - <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item> <item name="add_field" xsi:type="boolean">true</item> - <item name="dataType" xsi:type="string">select</item> + <item name="dataType" xsi:type="string">text</item> <item name="label" xsi:type="string" translate="true">Websites</item> <item name="sortOrder" xsi:type="number">100</item> </item> diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 0fbda9d526f0b8d35155a0070a31bd38950c28bb..2eecefb43346f4b303a7d71c54796bd8db162661 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -695,8 +695,11 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity [], [ 'related_skus', + 'related_position', 'crosssell_skus', + 'crosssell_position', 'upsell_skus', + 'upsell_position' ], ['additional_images', 'additional_image_labels', 'hide_from_product_page'] ); @@ -1113,6 +1116,8 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity asort($associations); $dataRow[$colPrefix . 'skus'] = implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_keys($associations)); + $dataRow[$colPrefix . 'position'] = + implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_values($associations)); } } $dataRow = $this->rowCustomizer->addData($dataRow, $productId); diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 140c4f3596ae58cddece531f1bbc5ee7d7410153..4e22f7ed32a8767bd022fef826f886eac3cfb9a4 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -294,8 +294,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity 'max_sale_qty' => 'max_cart_qty', 'notify_stock_qty' => 'notify_on_stock_below', '_related_sku' => 'related_skus', + '_related_position' => 'related_position', '_crosssell_sku' => 'crosssell_skus', + '_crosssell_position' => 'crosssell_position', '_upsell_sku' => 'upsell_skus', + '_upsell_position' => 'upsell_position', 'meta_keyword' => 'meta_keywords', ]; @@ -1102,13 +1105,27 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $sku = $rowData[self::COL_SKU]; + $productId = $this->skuProcessor->getNewSku($sku)[$this->getProductEntityLinkField()]; + $productLinkKeys = []; + $select = $this->_connection->select()->from( + $resource->getTable('catalog_product_link'), + ['id' => 'link_id', 'linked_id' => 'linked_product_id', 'link_type_id' => 'link_type_id'] + )->where( + 'product_id = :product_id' + ); + $bind = [':product_id' => $productId]; + foreach ($this->_connection->fetchAll($select, $bind) as $linkData) { + $linkKey = "{$productId}-{$linkData['linked_id']}-{$linkData['link_type_id']}"; + $productLinkKeys[$linkKey] = $linkData['id']; + } foreach ($this->_linkNameToId as $linkName => $linkId) { - $productId = $this->skuProcessor->getNewSku($sku)[$this->getProductEntityLinkField()]; $productIds[] = $productId; if (isset($rowData[$linkName . 'sku'])) { $linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); - - foreach ($linkSkus as $linkedSku) { + $linkPositions = !empty($rowData[$linkName . 'position']) + ? explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'position']) + : []; + foreach ($linkSkus as $linkedKey => $linkedSku) { $linkedSku = trim($linkedSku); if ((!is_null( $this->skuProcessor->getNewSku($linkedSku) @@ -1143,19 +1160,21 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } $linkKey = "{$productId}-{$linkedId}-{$linkId}"; - + if(empty($productLinkKeys[$linkKey])) { + $productLinkKeys[$linkKey] = $nextLinkId; + } if (!isset($linkRows[$linkKey])) { $linkRows[$linkKey] = [ - 'link_id' => $nextLinkId, + 'link_id' => $productLinkKeys[$linkKey], 'product_id' => $productId, 'linked_product_id' => $linkedId, 'link_type_id' => $linkId, ]; - if (!empty($rowData[$linkName . 'position'])) { + if (!empty($linkPositions[$linkedKey])) { $positionRows[] = [ - 'link_id' => $nextLinkId, + 'link_id' => $productLinkKeys[$linkKey], 'product_link_attribute_id' => $positionAttrId[$linkId], - 'value' => $rowData[$linkName . 'position'], + 'value' => $linkPositions[$linkedKey], ]; } $nextLinkId++; @@ -1176,7 +1195,10 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity } if ($positionRows) { // process linked product positions - $this->_connection->insertOnDuplicate($resource->getAttributeTypeTable('int'), $positionRows, ['value']); + $this->_connection->insertOnDuplicate( + $resource->getAttributeTypeTable('int'), + $positionRows, ['value'] + ); } } return $this; diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php index bcabe87fc0dfd63454eb96dca7fc782cee4876bf..ae734106be63a6395b7fb415a95590d43639e1fa 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php @@ -51,6 +51,9 @@ class AfterImportDataObserver implements ObserverInterface /** @var \Magento\CatalogUrlRewrite\Model\ObjectRegistry */ protected $productCategories; + /** @var UrlFinderInterface */ + protected $urlFinder; + /** @var \Magento\Store\Model\StoreManagerInterface */ protected $storeManager; @@ -353,14 +356,10 @@ class AfterImportDataObserver implements ObserverInterface if ($category === false) { continue; } - if ($currentUrlRewrite->getIsAutogenerated()) { - $urlRewrite = $this->generateForAutogenerated($currentUrlRewrite, $category); - } else { - $urlRewrite = $this->generateForCustom($currentUrlRewrite, $category); - } - if ($urlRewrite) { - $urlRewrites[] = $urlRewrite; - } + $url = $currentUrlRewrite->getIsAutogenerated() + ? $this->generateForAutogenerated($currentUrlRewrite, $category) + : $this->generateForCustom($currentUrlRewrite, $category); + $urlRewrites = array_merge($urlRewrites, $url); } $this->product = null; diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php index 1d4d77943dca1b009403a774e6b3b9a6152d26f7..7087a928b4ced70659bc229bf6a2446d415394e5 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php +++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php @@ -22,6 +22,8 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem /** * Name of request parameter for page number value + * + * @deprecated */ const PAGE_VAR_NAME = 'np'; @@ -142,9 +144,10 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem $this->_storeManager->getStore()->getId(), $this->_design->getDesignTheme()->getId(), $this->httpContext->getValue(\Magento\Customer\Model\Context::CONTEXT_GROUP), - intval($this->getRequest()->getParam(self::PAGE_VAR_NAME, 1)), + intval($this->getRequest()->getParam($this->getData('page_var_name'), 1)), $this->getProductsPerPage(), - $conditions + $conditions, + serialize($this->getRequest()->getParams()) ]; } @@ -208,7 +211,7 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem $collection = $this->_addProductAttributesAndPrices($collection) ->addStoreFilter() ->setPageSize($this->getPageSize()) - ->setCurPage($this->getRequest()->getParam(self::PAGE_VAR_NAME, 1)); + ->setCurPage($this->getRequest()->getParam($this->getData('page_var_name'), 1)); $conditions = $this->getConditions(); $conditions->collectValidatedAttributes($collection); @@ -305,7 +308,7 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem $this->pager->setUseContainer(true) ->setShowAmounts(true) ->setShowPerPage(false) - ->setPageVarName(self::PAGE_VAR_NAME) + ->setPageVarName($this->getData('page_var_name')) ->setLimit($this->getProductsPerPage()) ->setTotalLimit($this->getProductsCount()) ->setCollection($this->getProductCollection()); diff --git a/app/code/Magento/CatalogWidget/Test/Unit/Block/Product/ProductsListTest.php b/app/code/Magento/CatalogWidget/Test/Unit/Block/Product/ProductsListTest.php index 4ba2358bc00cfac60f4f27d790f70c16069c3649..1b7d27a638952c2cc79a2b030c1b87df33e13c3c 100644 --- a/app/code/Magento/CatalogWidget/Test/Unit/Block/Product/ProductsListTest.php +++ b/app/code/Magento/CatalogWidget/Test/Unit/Block/Product/ProductsListTest.php @@ -126,9 +126,21 @@ class ProductsListTest extends \PHPUnit_Framework_TestCase $this->httpContext->expects($this->once())->method('getValue')->willReturn('context_group'); $this->productsList->setData('conditions', 'some_serialized_conditions'); - $this->request->expects($this->once())->method('getParam')->with('np')->willReturn(1); - - $cacheKey = ['CATALOG_PRODUCTS_LIST_WIDGET', 1, 'blank', 'context_group', 1, 5, 'some_serialized_conditions']; + $this->productsList->setData('page_var_name', 'page_number'); + $this->request->expects($this->once())->method('getParam')->with('page_number')->willReturn(1); + + $this->request->expects($this->once())->method('getParams')->willReturn('request_params'); + + $cacheKey = [ + 'CATALOG_PRODUCTS_LIST_WIDGET', + 1, + 'blank', + 'context_group', + 1, + 5, + 'some_serialized_conditions', + serialize('request_params') + ]; $this->assertEquals($cacheKey, $this->productsList->getCacheKeyInfo()); } @@ -264,7 +276,6 @@ class ProductsListTest extends \PHPUnit_Framework_TestCase $this->rule->expects($this->once())->method('loadPost')->willReturnSelf(); $this->rule->expects($this->once())->method('getConditions')->willReturn($conditions); - if ($productsPerPage) { $this->productsList->setData('products_per_page', $productsPerPage); } else { diff --git a/app/code/Magento/Review/Model/Review.php b/app/code/Magento/Review/Model/Review.php index 792babfba4e3203a536054c4555cbc48471edd64..007910d8fbc5b5555fbeb98b11eaee9cc84b5f6b 100644 --- a/app/code/Magento/Review/Model/Review.php +++ b/app/code/Magento/Review/Model/Review.php @@ -374,7 +374,7 @@ class Review extends \Magento\Framework\Model\AbstractModel implements IdentityI public function getIdentities() { $tags = []; - if ($this->isApproved() && $this->getEntityPkValue()) { + if ($this->getEntityPkValue()) { $tags[] = Product::CACHE_TAG . '_' . $this->getEntityPkValue(); } return $tags; diff --git a/app/code/Magento/Review/Test/Unit/Model/ReviewTest.php b/app/code/Magento/Review/Test/Unit/Model/ReviewTest.php index 0f12dda793e03dea7208c7b67524897c9db877db..7eaca296843c74660ebe72eb2776339a334a3522 100644 --- a/app/code/Magento/Review/Test/Unit/Model/ReviewTest.php +++ b/app/code/Magento/Review/Test/Unit/Model/ReviewTest.php @@ -278,8 +278,16 @@ class ReviewTest extends \PHPUnit_Framework_TestCase $this->assertEmpty($this->review->getIdentities()); $productId = 1; + $this->review->setEntityPkValue($productId); + $this->review->setStatusId(Review::STATUS_PENDING); + $this->assertEquals([Product::CACHE_TAG . '_' . $productId], $this->review->getIdentities()); + $this->review->setEntityPkValue($productId); $this->review->setStatusId(Review::STATUS_APPROVED); $this->assertEquals([Product::CACHE_TAG . '_' . $productId], $this->review->getIdentities()); + + $this->review->setEntityPkValue($productId); + $this->review->setStatusId(Review::STATUS_NOT_APPROVED); + $this->assertEquals([Product::CACHE_TAG . '_' . $productId], $this->review->getIdentities()); } } diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php index fc7b1c5d35893ae65bf27ae2f2b81defd05adcc0..b9abf8fcab21bf660e52b6455c5209a839d4d297 100644 --- a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php +++ b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php @@ -39,6 +39,7 @@ class Configurable extends \Magento\Swatches\Block\Product\Renderer\Configurable */ protected function getSwatchAttributesData() { + $result = []; $swatchAttributeData = parent::getSwatchAttributesData(); foreach ($swatchAttributeData as $attributeId => $item) { if (!empty($item['used_in_product_listing'])) { diff --git a/app/code/Magento/Swatches/Model/Plugin/EavAttribute.php b/app/code/Magento/Swatches/Model/Plugin/EavAttribute.php index fddd3ec78ed922631d03be2653ba86a96cfabb79..c56e732cd9bce47770af7f15bead71ac5336d540 100644 --- a/app/code/Magento/Swatches/Model/Plugin/EavAttribute.php +++ b/app/code/Magento/Swatches/Model/Plugin/EavAttribute.php @@ -174,7 +174,7 @@ class EavAttribute { if (isset($optionsArray['value']) && is_array($optionsArray['value'])) { foreach (array_keys($optionsArray['value']) as $optionId) { - if ($optionsArray['delete'][$optionId] == 1) { + if (isset($optionsArray['delete']) && $optionsArray['delete'][$optionId] == 1) { unset($optionsArray['value'][$optionId]); } } diff --git a/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9b24e6b79658b6a117406ae20d6085e2832729d2 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php @@ -0,0 +1,217 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Swatches\Test\Unit\Block\Product\Renderer\Listing; + +use Magento\Swatches\Block\Product\Renderer\Configurable; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ConfigurableTest extends \PHPUnit_Framework_TestCase +{ + /** @var Configurable */ + private $configurable; + + /** @var \Magento\Framework\Stdlib\ArrayUtils|\PHPUnit_Framework_MockObject_MockObject */ + private $arrayUtils; + + /** @var \Magento\Framework\Json\EncoderInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $jsonEncoder; + + /** @var \Magento\ConfigurableProduct\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + private $helper; + + /** @var \Magento\Swatches\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + private $swatchHelper; + + /** @var \Magento\Swatches\Helper\Media|\PHPUnit_Framework_MockObject_MockObject */ + private $swatchMediaHelper; + + /** @var \Magento\Catalog\Helper\Product|\PHPUnit_Framework_MockObject_MockObject */ + private $catalogProduct; + + /** @var \Magento\Customer\Helper\Session\CurrentCustomer|\PHPUnit_Framework_MockObject_MockObject */ + private $currentCustomer; + + /** @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $priceCurrency; + + /** @var \Magento\ConfigurableProduct\Model\ConfigurableAttributeData|\PHPUnit_Framework_MockObject_MockObject */ + private $configurableAttributeData; + + /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ + private $product; + + /** @var \Magento\Catalog\Model\Product\Type\AbstractType|\PHPUnit_Framework_MockObject_MockObject */ + private $typeInstance; + + /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $scopeConfig; + + /** @var \Magento\Catalog\Helper\Image|\PHPUnit_Framework_MockObject_MockObject */ + private $imageHelper; + + /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $urlBuilder; + + public function setUp() + { + $this->arrayUtils = $this->getMock('\Magento\Framework\Stdlib\ArrayUtils', [], [], '', false); + $this->jsonEncoder = $this->getMock('\Magento\Framework\Json\EncoderInterface', [], [], '', false); + $this->helper = $this->getMock('\Magento\ConfigurableProduct\Helper\Data', [], [], '', false); + $this->swatchHelper = $this->getMock('\Magento\Swatches\Helper\Data', [], [], '', false); + $this->swatchMediaHelper = $this->getMock('\Magento\Swatches\Helper\Media', [], [], '', false); + $this->catalogProduct = $this->getMock('\Magento\Catalog\Helper\Product', [], [], '', false); + $this->currentCustomer = $this->getMock('\Magento\Customer\Helper\Session\CurrentCustomer', [], [], '', false); + $this->priceCurrency = $this->getMock('\Magento\Framework\Pricing\PriceCurrencyInterface', [], [], '', false); + $this->configurableAttributeData = $this->getMock( + 'Magento\ConfigurableProduct\Model\ConfigurableAttributeData', + [], + [], + '', + false + ); + $this->product = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false); + $this->typeInstance = $this->getMock('\Magento\Catalog\Model\Product\Type\AbstractType', [], [], '', false); + $this->scopeConfig = $this->getMock('\Magento\Framework\App\Config\ScopeConfigInterface', [], [], '', false); + $this->imageHelper = $this->getMock('\Magento\Catalog\Helper\Image', [], [], '', false); + $this->urlBuilder = $this->getMock('\Magento\Framework\UrlInterface'); + + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->configurable = $objectManagerHelper->getObject( + '\Magento\Swatches\Block\Product\Renderer\Listing\Configurable', + [ + 'scopeConfig' => $this->scopeConfig, + 'imageHelper' => $this->imageHelper, + 'urlBuilder' => $this->urlBuilder, + 'arrayUtils' => $this->arrayUtils, + 'jsonEncoder' => $this->jsonEncoder, + 'helper' => $this->helper, + 'swatchHelper' => $this->swatchHelper, + 'swatchMediaHelper' => $this->swatchMediaHelper, + 'catalogProduct' => $this->catalogProduct, + 'currentCustomer' => $this->currentCustomer, + 'priceCurrency' => $this->priceCurrency, + 'configurableAttributeData' => $this->configurableAttributeData, + 'data' => [], + ] + ); + } + + /** + * @covers Magento\Swatches\Block\Product\Renderer\Listing\Configurable::getSwatchAttributesData + */ + public function testGetJsonSwatchConfigWithoutSwatches() + { + $this->prepareGetJsonSwatchConfig(); + $this->configurable->setProduct($this->product); + $this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray') + ->with($this->product) + ->willReturn([]); + $this->swatchHelper->expects($this->once())->method('getSwatchesByOptionsId') + ->willReturn([]); + $this->jsonEncoder->expects($this->once())->method('encode')->with([]); + $this->configurable->getJsonSwatchConfig(); + } + + /** + * @covers Magento\Swatches\Block\Product\Renderer\Listing\Configurable::getSwatchAttributesData + */ + public function testGetJsonSwatchNotUsedInProductListing() + { + $this->prepareGetJsonSwatchConfig(); + $this->configurable->setProduct($this->product); + $this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray') + ->with($this->product) + ->willReturn([ + 1 => [ + 'options' => [1 => 'testA', 3 => 'testB'], + 'use_product_image_for_swatch' => true, + 'used_in_product_listing' => false, + 'attribute_code' => 'code', + ], + ]); + $this->swatchHelper->expects($this->once())->method('getSwatchesByOptionsId') + ->willReturn([]); + $this->jsonEncoder->expects($this->once())->method('encode')->with([]); + $this->configurable->getJsonSwatchConfig(); + } + + /** + * @covers Magento\Swatches\Block\Product\Renderer\Listing\Configurable::getSwatchAttributesData + */ + public function testGetJsonSwatchUsedInProductListing() + { + $products = [ + 1 => 'testA', + 3 => 'testB' + ]; + $expected = + [ + 'type' => null, + 'value' => 'hello', + 'label' => $products[3] + ]; + $this->prepareGetJsonSwatchConfig(); + $this->configurable->setProduct($this->product); + $this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray') + ->with($this->product) + ->willReturn([ + 1 => [ + 'options' => $products, + 'use_product_image_for_swatch' => true, + 'used_in_product_listing' => true, + 'attribute_code' => 'code', + ], + ]); + $this->swatchHelper->expects($this->once())->method('getSwatchesByOptionsId') + ->with([1, 3]) + ->willReturn([ + 3 => ['type' => $expected['type'], 'value' => $expected['value']] + ]); + $this->jsonEncoder->expects($this->once())->method('encode'); + $this->configurable->getJsonSwatchConfig(); + } + + private function prepareGetJsonSwatchConfig() + { + $product1 = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false); + $product1->expects($this->atLeastOnce())->method('isSaleable')->willReturn(true); + $product1->expects($this->any())->method('getData')->with('code')->willReturn(1); + + $product2 = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false); + $product2->expects($this->atLeastOnce())->method('isSaleable')->willReturn(true); + $product2->expects($this->any())->method('getData')->with('code')->willReturn(3); + + $simpleProducts = [$product1, $product2]; + $configurableType = $this->getMock( + '\Magento\ConfigurableProduct\Model\Product\Type\Configurable', + [], + [], + '', + false + ); + $configurableType->expects($this->atLeastOnce())->method('getUsedProducts')->with($this->product, null) + ->willReturn($simpleProducts); + $this->product->expects($this->any())->method('getTypeInstance')->willReturn($configurableType); + + $productAttribute1 = $this->getMock('\Magento\Eav\Model\Entity\Attribute\AbstractAttribute', [], [], '', false); + $productAttribute1->expects($this->any())->method('getId')->willReturn(1); + $productAttribute1->expects($this->any())->method('getAttributeCode')->willReturn('code'); + + $attribute1 = $this->getMock( + '\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute', + ['getProductAttribute'], + [], + '', + false + ); + $attribute1->expects($this->any())->method('getProductAttribute')->willReturn($productAttribute1); + + $this->helper->expects($this->any())->method('getAllowAttributes')->with($this->product) + ->willReturn([$attribute1]); + } +} diff --git a/app/code/Magento/Swatches/view/frontend/web/js/SwatchRenderer.js b/app/code/Magento/Swatches/view/frontend/web/js/SwatchRenderer.js index 22025f64994d33669c86583b4c89c83d444f35ec..8d1e7a2ecd97ccdd471ef37833887b9900cceabb 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/SwatchRenderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/SwatchRenderer.js @@ -227,6 +227,7 @@ define([ 'img': $main.find('.product-image-photo').attr('src') }]; } + this.productForm = this.element.parents(this.options.selectorProduct).find('form:first'); }, /** @@ -260,6 +261,11 @@ define([ '<span class="' + classes.attributeSelectedOptionLabelClass + '"></span>'; } + if ($widget.productForm) { + $widget.productForm.append(input); + input = ''; + } + // Create new control container.append( '<div class="' + classes.attributeClass + ' ' + item.code + @@ -476,7 +482,10 @@ define([ var $parent = $this.parents('.' + $widget.options.classes.attributeClass), $label = $parent.find('.' + $widget.options.classes.attributeSelectedOptionLabelClass), - $input = $parent.find('.' + $widget.options.classes.attributeInput); + attributeId = $parent.attr('attribute-id'), + $input = $widget.productForm.find( + '.' + $widget.options.classes.attributeInput + '[name="super_attribute[' + attributeId + ']"]' + ); if ($this.hasClass('disabled')) { return; diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index d89b77893eae9491a55b1454202f6a8cc59cc8b1..c25b76f8251e22708be579b71e5afde9b5f9f85f 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -50,6 +50,11 @@ class Widget */ protected $conditionsHelper; + /** + * @var \Magento\Framework\Math\Random + */ + private $mathRandom; + /** * @param \Magento\Framework\Escaper $escaper * @param \Magento\Widget\Model\Config\Data $dataStorage @@ -74,6 +79,20 @@ class Widget $this->conditionsHelper = $conditionsHelper; } + /** + * @return \Magento\Framework\Math\Random + * + * @deprecated + */ + private function getMathRandom() + { + if ($this->mathRandom === null) { + $this->mathRandom = \Magento\Framework\App\ObjectManager::getInstance() + ->get('\Magento\Framework\Math\Random'); + } + return $this->mathRandom; + } + /** * Return widget config based on its class type * @@ -289,7 +308,7 @@ class Widget } elseif (trim($value) == '') { $widget = $this->getConfigAsObject($type); $parameters = $widget->getParameters(); - if (isset($parameters[$name]) && is_object($parameters[$name])) { + if (is_object($parameters[$name])) { $value = $parameters[$name]->getValue(); } } @@ -297,6 +316,15 @@ class Widget $directive .= sprintf(' %s="%s"', $name, $value); } } + + if ((bool)$params['show_pager']) { + $directive .= sprintf( + ' %s="%s"', + 'page_var_name', + 'p' . $this->getMathRandom()->getRandomString(5, \Magento\Framework\Math\Random::CHARS_LOWERS) + ); + } + $directive .= '}}'; if ($asIs) { diff --git a/app/code/Magento/Widget/Model/Widget/Instance.php b/app/code/Magento/Widget/Model/Widget/Instance.php index 7bccc0812d1e7ff79a573e31600a46fd9a269430..f6142f3d7e0ca5cb58d3bbcc00031d720dd3f6d1 100644 --- a/app/code/Magento/Widget/Model/Widget/Instance.php +++ b/app/code/Magento/Widget/Model/Widget/Instance.php @@ -241,8 +241,16 @@ class Instance extends \Magento\Framework\Model\AbstractModel if (is_array($this->getData('store_ids'))) { $this->setData('store_ids', implode(',', $this->getData('store_ids'))); } - if (is_array($this->getData('widget_parameters'))) { - $this->setData('widget_parameters', serialize($this->getData('widget_parameters'))); + + $parameters = $this->getData('widget_parameters'); + if (is_array($parameters)) { + if ((bool)$parameters['show_pager'] && !array_key_exists('page_var_name', $parameters)) { + $parameters['page_var_name'] = 'p' . $this->mathRandom->getRandomString( + 5, + \Magento\Framework\Math\Random::CHARS_LOWERS + ); + } + $this->setData('widget_parameters', serialize($parameters)); } $this->setData('page_groups', $tmpPageGroups); $this->setData('page_group_ids', $pageGroupIds); diff --git a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php index 7ccea8d305c6fff851b9f05e21d4d9c2be97252b..16bcb5453247232b86db8166ac9159700393d480 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php @@ -17,15 +17,24 @@ class WidgetTest extends \PHPUnit_Framework_TestCase */ protected $widget; + /** + * @var \Magento\Widget\Helper\Conditions + */ + private $conditionsHelper; + public function setUp() { $this->dataStorageMock = $this->getMockBuilder('Magento\Widget\Model\Config\Data') ->disableOriginalConstructor() ->getMock(); + $this->conditionsHelper = $this->getMockBuilder('\Magento\Widget\Helper\Conditions') + ->setMethods(['encode']) + ->disableOriginalConstructor() + ->getMock(); $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->widget = $objectManagerHelper->getObject( 'Magento\Widget\Model\Widget', - ['dataStorage' => $this->dataStorageMock] + ['dataStorage' => $this->dataStorageMock, 'conditionsHelper' => $this->conditionsHelper] ); } @@ -119,4 +128,37 @@ class WidgetTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Magento\Framework\DataObject', $resultObject); $this->assertSame([], $resultObject->getData()); } + + public function testGetWidgetDeclaration() + { + $mathRandomMock = $this->getMock('\Magento\Framework\Math\Random', ['getRandomString'], [], '', false); + $mathRandomMock->expects($this->any())->method('getRandomString')->willReturn('asdf'); + $reflection = new \ReflectionClass(get_class($this->widget)); + $reflectionProperty = $reflection->getProperty('mathRandom'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->widget, $mathRandomMock); + + $conditions = [ + [ + 'type' => 'Magento\CatalogWidget\Model\Rule\Condition\Combine', + 'aggregator' => 'all', + 'value' => '1', + 'new_child' => '' + ] + ]; + $params = [ + 'title' => 'my widget', + 'show_pager' => '1', + 'products_per_page' => '5', + 'products_count' => '10', + 'template' => 'product/widget/content/grid.phtml', + 'conditions' => $conditions + ]; + $this->conditionsHelper->expects($this->once())->method('encode')->with($conditions) + ->willReturn('encoded-conditions-string'); + $result = $this->widget->getWidgetDeclaration('Magento\CatalogWidget\Block\Product\ProductsList', $params); + $this->assertContains('{{widget type="Magento\CatalogWidget\Block\Product\ProductsList"', $result); + $this->assertContains('conditions_encoded="encoded-conditions-string"', $result); + $this->assertContains('page_var_name="pasdf"}}', $result); + } } diff --git a/app/code/Magento/Wishlist/Block/AddToWishlist.php b/app/code/Magento/Wishlist/Block/AddToWishlist.php new file mode 100644 index 0000000000000000000000000000000000000000..db6dae099fe21e1dccf97887e7c4dc00ce280177 --- /dev/null +++ b/app/code/Magento/Wishlist/Block/AddToWishlist.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Wishlist\Block; + +/** + * Wishlist js plugin initialization block + */ +class AddToWishlist extends \Magento\Framework\View\Element\Template +{ + /** + * Product types + * + * @var array|null + */ + private $productTypes; + + /** + * @param \Magento\Framework\View\Element\Template\Context $context + * @param array $data + */ + public function __construct( + \Magento\Framework\View\Element\Template\Context $context, + array $data = [] + ) { + parent::__construct( + $context, + $data + ); + } + + /** + * Returns wishlist widget options + * + * @return array + */ + public function getWishlistOptions() + { + return ['productType' => $this->getProductTypes()]; + } + + /** + * Returns an array of product types + * + * @return array|null + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getProductTypes() + { + if ($this->productTypes === null) { + $this->productTypes = []; + $block = $this->getLayout()->getBlock('category.products.list'); + if ($block) { + $productCollection = $block->getLoadedProductCollection(); + /** @var $product \Magento\Catalog\Model\Product */ + foreach ($productCollection as $product) { + $productTypes[] = $product->getTypeId(); + } + $this->productTypes = array_unique($productTypes); + } + } + return $this->productTypes; + } + + /** + * {@inheritdoc} + */ + protected function _toHtml() + { + if (!$this->getProductTypes()) { + return ''; + } + return parent::_toHtml(); + } +} diff --git a/app/code/Magento/Wishlist/view/frontend/layout/catalog_category_view.xml b/app/code/Magento/Wishlist/view/frontend/layout/catalog_category_view.xml index 907e418b935d5d5ae5ba9b3e9a6b69b403f02410..3cdce06ba62b3c89970b36303dcccbbb33e6ea08 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/catalog_category_view.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/catalog_category_view.xml @@ -15,6 +15,9 @@ </argument> </arguments> </block> + <referenceContainer name="category.product.list.additional"> + <block class="Magento\Wishlist\Block\AddToWishlist" template="Magento_Wishlist::addto.phtml" /> + </referenceContainer> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/addto.phtml b/app/code/Magento/Wishlist/view/frontend/templates/addto.phtml new file mode 100644 index 0000000000000000000000000000000000000000..98c7055cb63af1da1d48652772e3fa907ebb061d --- /dev/null +++ b/app/code/Magento/Wishlist/view/frontend/templates/addto.phtml @@ -0,0 +1,15 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +?> +<script type="text/x-magento-init"> + { + "body": { + "addToWishlist": <?php /* @noEscape */ echo $this->helper('Magento\Framework\Json\Helper\Data') + ->jsonEncode($block->getWishlistOptions()); + ?> + } + } +</script> diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index 79ff842fdd91557c34f979e590aa87e152f28d53..15390d6f8e1c9ecd9a97811cd66e6c9d8549ba1b 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -22,13 +22,25 @@ define([ this._bind(); }, _bind: function() { - var changeCustomOption = 'change ' + this.options.customOptionsInfo, - changeQty = 'change ' + this.options.qtyInfo, - changeProductInfo = 'change ' + this.options[this.options.productType + 'Info'], + var options = this.options, + changeCustomOption = 'change ' + options.customOptionsInfo, + changeQty = 'change ' + options.qtyInfo, events = {}; + + if ('productType' in options) { + if (typeof options.productType === 'string') { + options.productType = [options.productType]; + } + } else { + options.productType = []; + } + events[changeCustomOption] = '_updateWishlistData'; - events[changeProductInfo] = '_updateWishlistData'; events[changeQty] = '_updateWishlistData'; + options.productType.forEach(function (type) { + events['change ' + options[type + 'Info']] = '_updateWishlistData'; + }); + this._on(events); }, _updateWishlistData: function(event) { @@ -90,10 +102,11 @@ define([ return result; }, _getElementData: function(element) { + element = $(element); var data = {}, - elementName = $(element).data('selector'), - elementValue = $(element).val(); - if ($(element).is('select[multiple]') && elementValue !== null) { + elementName = element.data('selector') ? element.data('selector') : element.attr('name'), + elementValue = element.val(); + if (element.is('select[multiple]') && elementValue !== null) { if (elementName.substr(elementName.length - 2) == '[]') { elementName = elementName.substring(0, elementName.length - 2); } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Webapi.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Webapi.php index 853d6de34a482a951f2e18e76ec3c517e62ee016..7ea6af446dc2d7f98941b481aeccd16112c74362 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Webapi.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Webapi.php @@ -60,7 +60,8 @@ class Webapi extends AbstractWebApi implements CatalogProductSimpleInterface 'options', 'media_gallery_entries', 'tier_prices', - 'extension_attributes' + 'extension_attributes', + 'custom_attributes' ]; /** diff --git a/dev/tests/functional/tests/app/Magento/Weee/Test/Block/Product/Price.php b/dev/tests/functional/tests/app/Magento/Weee/Test/Block/Product/Price.php index 19b7a0cb01f43c56a6c9586d9c7a3642c093ef74..03636a56104aade2474f5f60d14c99674b28e8d2 100644 --- a/dev/tests/functional/tests/app/Magento/Weee/Test/Block/Product/Price.php +++ b/dev/tests/functional/tests/app/Magento/Weee/Test/Block/Product/Price.php @@ -17,6 +17,9 @@ class Price extends \Magento\Catalog\Test\Block\AbstractPriceBlock * @var array */ protected $mapTypePrices = [ + 'regular_price' => [ + 'selector' => '[data-price-type="finalPrice"] .price', + ], 'fpt_price' => [ 'selector' => '[data-price-type="weee"] .price', ], diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_url_key.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_url_key.php new file mode 100644 index 0000000000000000000000000000000000000000..5ca9fa01554aefb9c495b91bc60def699ccf72c6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_url_key.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product') + ->setSku('simple1') + ->setPrice(10) + ->setDescription('Description with <b>html tag</b>') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setUrlKey('url-key') + ->save(); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product 2') + ->setSku('simple2') + ->setPrice(10) + ->setDescription('Description with <b>html tag</b>') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setUrlKey('url-key2') + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 68f9b09a1afb1965d96a11d312e17b61c98e1e5c..2ed3c31c7dba3f831a3cd89bf49c07ced80a4593 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -82,6 +82,19 @@ class ProductTest extends \PHPUnit_Framework_TestCase $this->assertContains('max_characters=10', $exportData); } + /** + * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_with_product_links_data.php + */ + public function testExportWithProductLinks() + { + $this->model->setWriter( + \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\ImportExport\Model\Export\Adapter\Csv' + ) + ); + $this->assertNotEmpty($this->model->export()); + } + /** * Verify that all stock item attribute values are exported (aren't equal to empty string) * 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 bbc844340bc3da5e5a2c963d806999a98ba6fa88..1613a1c28c50d2f6aa7f10fd0de9935781728ecd 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -1052,4 +1052,113 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase $this->assertTrue($errors->getErrorsCount() == 0); } + + /** + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testProductWithLinks() + { + $linksData = [ + 'upsell' => [ + 'simple1' => '3', + 'simple3' => '1' + ], + 'crosssell' => [ + 'simple2' => '1', + 'simple3' => '2' + ], + 'related' => [ + 'simple1' => '2', + 'simple2' => '1' + ] + ]; + // import data from CSV file + $pathToFile = __DIR__ . '/_files/products_to_import_with_product_links.csv'; + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Framework\Filesystem' + ); + + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + '\Magento\ImportExport\Model\Import\Source\Csv', + [ + 'file' => $pathToFile, + 'directory' => $directory + ] + ); + $errors = $this->_model->setSource( + $source + )->setParameters( + [ + 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product' + ] + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $resource = $objectManager->get('Magento\Catalog\Model\ResourceModel\Product'); + $productId = $resource->getIdBySku('simple4'); + /** @var \Magento\Catalog\Model\Product $product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Product' + ); + $product->load($productId); + $productLinks = [ + 'upsell' => $product->getUpSellProducts(), + 'crosssell' => $product->getCrossSellProducts(), + 'related' => $product->getRelatedProducts() + ]; + $importedProductLinks = []; + foreach ($productLinks as $linkType => $linkedProducts) { + foreach ($linkedProducts as $linkedProductData) { + $importedProductLinks[$linkType][$linkedProductData->getSku()] = $linkedProductData->getPosition(); + } + } + $this->assertEquals($linksData, $importedProductLinks); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_url_key.php + * @magentoAppIsolation enabled + */ + public function testExistingProductWithUrlKeys() + { + $this->markTestSkipped('Test must be unskiped after implementation MAGETWO-48871'); + $products = [ + 'simple1' => 'url-key1', + 'simple2' => 'url-key2', + 'simple3' => 'url-key3' + ]; + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create('Magento\Framework\Filesystem'); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + '\Magento\ImportExport\Model\Import\Source\Csv', + [ + 'file' => __DIR__ . '/_files/products_to_import_with_valid_url_keys.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 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Api\ProductRepositoryInterface' + ); + foreach ($products as $productSku => $productUrlKey) { + $this->assertEquals($productUrlKey, $productRepository->get($productSku)->getUrlKey()); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links.csv new file mode 100644 index 0000000000000000000000000000000000000000..8e5733ebe23cca2c11186a1da990b533522d5b2e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links.csv @@ -0,0 +1,5 @@ +sku,product_type,store_view_code,name,price,qty,attribute_set_code,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position +simple1,simple,,simple 1,25,10,Default,,,,,, +simple2,simple,,simple 2,34,10,Default,,,,,, +simple3,simple,,simple 3,58,10,Default,,,,,, +simple4,simple,,simple 4,67,10,Default,"simple1,simple2","2,1","simple2,simple3","1,2","simple1,simple3","3,1" \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_valid_url_keys.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_valid_url_keys.csv new file mode 100644 index 0000000000000000000000000000000000000000..06e66d945b7fc33324ad0b290838d49022cf3589 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_valid_url_keys.csv @@ -0,0 +1,4 @@ +sku,product_type,store_view_code,name,price,attribute_set_code,url_key +simple1,simple,,"simple 1",25,Default,url-key1 +simple2,simple,,"simple 2",34,Default,url-key2 +simple3,simple,,"simple 3",58,Default,url-key3 diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_product_links_data.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_product_links_data.php new file mode 100644 index 0000000000000000000000000000000000000000..e7503b8a3431e16af487f73f1ca5ee08634eca5e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_with_product_links_data.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +require dirname(dirname(__DIR__)) . '/Catalog/_files/category.php'; +require dirname(dirname(__DIR__)) . '/Store/_files/second_store.php'; +require dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute.php'; + +$productModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); + +$productModel->setTypeId( + \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE +)->setId( + 1 +)->setAttributeSetId( + 4 +)->setName( + 'New Product' +)->setSku( + 'simple' +)->setPrice( + 10 +)->setTierPrice( + [0 => ['website_id' => 0, 'cust_group' => 0, 'price_qty' => 3, 'price' => 8]] +)->setVisibility( + \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH +)->setStatus( + \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED +)->setWebsiteIds( + [1] +)->setCateroryIds( + [] +)->setStockData( + ['qty' => 100, 'is_in_stock' => 1] +)->setCanSaveCustomOptions( + true +)->setCategoryIds( + [333] +)->setUpSellLinkData( + [$product->getId() => ['position' => 1]] +)->setCrossSellLinkData( + [$product->getId() => ['position' => 2]] +)->setRelatedLinkData( + [$product->getId() => ['position' => 3]] +)->save();