diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php index 10cb3b8a846a25f157518c75aa91a3c30e91de35..cef57f610fc2103eb729d7957b1ac5eef6b4d567 100644 --- a/app/code/Magento/Bundle/Model/Product/Type.php +++ b/app/code/Magento/Bundle/Model/Product/Type.php @@ -550,6 +550,7 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType $selectionsCollection = $this->_bundleCollection->create(); $selectionsCollection->addAttributeToSelect('status'); $selectionsCollection->addQuantityFilter(); + $selectionsCollection->setFlag('product_children', true); $selectionsCollection->addFilterByRequiredOptions(); $selectionsCollection->setOptionIdsFilter([$option->getId()]); diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php index 70289f437402e450b3ea2cb7c787f1e713644d08..3dae21a30968d9061d7fbb6892b9aa146e0fae0d 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php @@ -8,59 +8,61 @@ namespace Magento\Bundle\Test\Unit\Model\Product; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** + * Test for Model ProductPrice. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PriceTest extends \PHPUnit_Framework_TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\CatalogRule\Model\ResourceModel\RuleFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $ruleFactoryMock; + private $ruleFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $localeDateMock; + private $localeDateMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $storeManagerMock; + private $storeManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ - protected $customerSessionMock; + private $customerSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $eventManagerMock; + private $eventManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ - protected $catalogHelperMock; + private $catalogHelperMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject */ - protected $storeMock; + private $storeMock; /** * @var \Magento\Bundle\Model\Product\Price */ - protected $model; + private $model; /** * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $priceCurrency; + private $priceCurrency; /** - * @var \Magento\Customer\Api\GroupManagementInterface + * @var \Magento\Customer\Api\GroupManagementInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $groupManagement; + private $groupManagement; /** * Serializer interface instance. @@ -69,6 +71,11 @@ class PriceTest extends \PHPUnit_Framework_TestCase */ private $serializer; + /** + * Set up. + * + * @return void + */ protected function setUp() { $this->ruleFactoryMock = $this->getMock( @@ -128,6 +135,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase } /** + * Test for calculateSpecialPrice(). + * * @param float $finalPrice * @param float $specialPrice * @param int $callsNumber @@ -137,6 +146,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase * @covers \Magento\Bundle\Model\Product\Price::calculateSpecialPrice * @covers \Magento\Bundle\Model\Product\Price::__construct * @dataProvider calculateSpecialPrice + * @return void */ public function testCalculateSpecialPrice($finalPrice, $specialPrice, $callsNumber, $dateInInterval, $expected) { @@ -156,6 +166,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase } /** + * Data provider for calculateSpecialPrice() test. + * * @return array */ public function calculateSpecialPrice() @@ -170,6 +182,11 @@ class PriceTest extends \PHPUnit_Framework_TestCase ]; } + /** + * Test for getTotalBundleItemsPrice() with noCustom options. + * + * @return void + */ public function testGetTotalBundleItemsPriceWithNoCustomOptions() { $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) @@ -184,8 +201,11 @@ class PriceTest extends \PHPUnit_Framework_TestCase } /** + * Test for getTotalBundleItemsPrice() with empty options. + * * @param string|null $value * @dataProvider dataProviderWithEmptyOptions + * @return void */ public function testGetTotalBundleItemsPriceWithEmptyOptions($value) { @@ -213,6 +233,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase } /** + * Data provider for getTotalBundleItemsPrice() with empty options. + * * @return array */ public function dataProviderWithEmptyOptions() @@ -224,6 +246,11 @@ class PriceTest extends \PHPUnit_Framework_TestCase ]; } + /** + * Test for getTotalBundleItemsPrice() with empty options. + * + * @return void + */ public function testGetTotalBundleItemsPriceWithNoItems() { $storeId = 1; diff --git a/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php b/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..013ec1f940047f1e2c9ce74f95c6f4eb679e04b5 --- /dev/null +++ b/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api; + +/** + * Base prices storage. + * @api + */ +interface BasePriceStorageInterface +{ + /** + * Return product prices. + * + * @param string[] $skus + * @return \Magento\Catalog\Api\Data\BasePriceInterface[] + */ + public function get(array $skus); + + /** + * Add or update product prices. + * + * @param \Magento\Catalog\Api\Data\BasePriceInterface[] $prices + * @return bool Will returned True if updated. + */ + public function update(array $prices); +} diff --git a/app/code/Magento/Catalog/Api/CostStorageInterface.php b/app/code/Magento/Catalog/Api/CostStorageInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..0c9fb4d540d5bcbe58b92ffb3d1e3794556d39a6 --- /dev/null +++ b/app/code/Magento/Catalog/Api/CostStorageInterface.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api; + +/** + * Product cost storage. + * @api + */ +interface CostStorageInterface +{ + /** + * Return product prices. + * + * @param string[] $skus + * @return \Magento\Catalog\Api\Data\CostInterface[] + */ + public function get(array $skus); + + /** + * Add or update product cost. + * + * @param \Magento\Catalog\Api\Data\CostInterface[] $prices + * @return bool Will returned True if updated. + */ + public function update(array $prices); + + /** + * Delete product cost. + * + * @param string[] $skus + * @return bool Will returned True if deleted. + * @throws \Magento\Framework\Exception\CouldNotDeleteException + */ + public function delete(array $skus); +} diff --git a/app/code/Magento/Catalog/Api/Data/BasePriceInterface.php b/app/code/Magento/Catalog/Api/Data/BasePriceInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..942de4a63abef74484e7169adbac07c9faa79cdf --- /dev/null +++ b/app/code/Magento/Catalog/Api/Data/BasePriceInterface.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api\Data; + +/** + * Price interface. + * @api + */ +interface BasePriceInterface extends \Magento\Framework\Api\ExtensibleDataInterface +{ + /**#@+ + * Constants + */ + const PRICE = 'price'; + const STORE_ID = 'store_id'; + const SKU = 'sku'; + /**#@-*/ + + /** + * Set price. + * + * @param float $price + * @return $this + */ + public function setPrice($price); + + /** + * Get price. + * + * @return float + */ + public function getPrice(); + + /** + * Set store id. + * + * @param int $storeId + * @return $this + */ + public function setStoreId($storeId); + + /** + * Get store id. + * + * @return int + */ + public function getStoreId(); + + /** + * Set SKU. + * + * @param string $sku + * @return $this + */ + public function setSku($sku); + + /** + * Get SKU. + * + * @return string + */ + public function getSku(); + + /** + * Retrieve existing extension attributes object or create a new one. + * + * @return \Magento\Catalog\Api\Data\BasePriceExtensionInterface|null + */ + public function getExtensionAttributes(); + + /** + * Set an extension attributes object. + * + * @param \Magento\Catalog\Api\Data\BasePriceExtensionInterface $extensionAttributes + * @return $this + */ + public function setExtensionAttributes( + \Magento\Catalog\Api\Data\BasePriceExtensionInterface $extensionAttributes + ); +} diff --git a/app/code/Magento/Catalog/Api/Data/CostInterface.php b/app/code/Magento/Catalog/Api/Data/CostInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..c007c81f1d7bf9181215044701b18205322be49b --- /dev/null +++ b/app/code/Magento/Catalog/Api/Data/CostInterface.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api\Data; + +/** + * Cost interface. + * @api + */ +interface CostInterface extends \Magento\Framework\Api\ExtensibleDataInterface +{ + /**#@+ + * Constants + */ + const COST = 'cost'; + const STORE_ID = 'store_id'; + const SKU = 'sku'; + /**#@-*/ + + /** + * Set cost value. + * + * @param float $cost + * @return $this + */ + public function setCost($cost); + + /** + * Get cost value. + * + * @return float + */ + public function getCost(); + + /** + * Set store id. + * + * @param int $storeId + * @return $this + */ + public function setStoreId($storeId); + + /** + * Get store id. + * + * @return int + */ + public function getStoreId(); + + /** + * Set SKU. + * + * @param string $sku + * @return $this + */ + public function setSku($sku); + + /** + * Get SKU. + * + * @return string + */ + public function getSku(); + + /** + * Retrieve existing extension attributes object or create a new one. + * + * @return \Magento\Catalog\Api\Data\CostExtensionInterface|null + */ + public function getExtensionAttributes(); + + /** + * Set an extension attributes object. + * + * @param \Magento\Catalog\Api\Data\CostExtensionInterface $extensionAttributes + * @return $this + */ + public function setExtensionAttributes( + \Magento\Catalog\Api\Data\CostExtensionInterface $extensionAttributes + ); +} diff --git a/app/code/Magento/Catalog/Api/Data/TierPriceInterface.php b/app/code/Magento/Catalog/Api/Data/TierPriceInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..1b708132c0d0ef95f900690e53b6520dac2fa008 --- /dev/null +++ b/app/code/Magento/Catalog/Api/Data/TierPriceInterface.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api\Data; + +/** + * Tier price interface. + * @api + */ +interface TierPriceInterface extends \Magento\Framework\Api\ExtensibleDataInterface +{ + /**#@+ + * Constants + */ + const PRICE = 'price'; + const PRICE_TYPE = 'price_type'; + const WEBSITE_ID = 'website_id'; + const SKU = 'sku'; + const CUSTOMER_GROUP = 'customer_group'; + const QUANTITY = 'quantity'; + const PRICE_TYPE_FIXED = 'fixed'; + const PRICE_TYPE_DISCOUNT = 'discount'; + /**#@-*/ + + /** + * Set tier price. + * + * @param float $price + * @return $this + */ + public function setPrice($price); + + /** + * Get tier price. + * + * @return float + */ + public function getPrice(); + + /** + * Set tier price type. + * + * @param string $type + * @return $this + */ + public function setPriceType($type); + + /** + * Get tier price type. + * + * @return string + */ + public function getPriceType(); + + /** + * Set website id. + * + * @param int $websiteId + * @return $this + */ + public function setWebsiteId($websiteId); + + /** + * Get website id. + * + * @return int + */ + public function getWebsiteId(); + + /** + * Set SKU. + * + * @param string $sku + * @return $this + */ + public function setSku($sku); + + /** + * Get SKU. + * + * @return string + */ + public function getSku(); + + /** + * Set customer group. + * + * @param string $group + * @return $this + */ + public function setCustomerGroup($group); + + /** + * Get customer group. + * + * @return string + */ + public function getCustomerGroup(); + + /** + * Set quantity. + * + * @param float $quantity + * @return $this + */ + public function setQuantity($quantity); + + /** + * Get quantity. + * + * @return float + */ + public function getQuantity(); + + /** + * Retrieve existing extension attributes object or create a new one. + * + * @return \Magento\Catalog\Api\Data\TierPriceExtensionInterface|null + */ + public function getExtensionAttributes(); + + /** + * Set an extension attributes object. + * + * @param \Magento\Catalog\Api\Data\TierPriceExtensionInterface $extensionAttributes + * @return $this + */ + public function setExtensionAttributes( + \Magento\Catalog\Api\Data\TierPriceExtensionInterface $extensionAttributes + ); +} diff --git a/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php b/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..200cdc1baa411c0ec63b9161f5829c1b29724315 --- /dev/null +++ b/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api; + +/** + * Tier prices storage. + * @api + */ +interface TierPriceStorageInterface +{ + /** + * Return product prices. + * + * @param string[] $skus + * @return \Magento\Catalog\Api\Data\TierPriceInterface[] + */ + public function get(array $skus); + + /** + * Add or update product prices. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices + * @return bool Will returned True if updated. + */ + public function update(array $prices); + + /** + * Remove existing tier prices and replace them with the new ones. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices + * @return bool Will returned True if replaced. + */ + public function replace(array $prices); + + /** + * Delete product tier prices. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices + * @return bool Will returned True if deleted. + */ + public function delete(array $prices); +} diff --git a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php index 5e518df37db1a574f25fe008510e75742cdffa9c..b994c787bee7aa76f8a4baa3648bcbb2c0e4ee27 100644 --- a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php +++ b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php @@ -23,7 +23,7 @@ class Price implements ProductPriceOptionsInterface { return [ ['value' => self::VALUE_FIXED, 'label' => __('Fixed')], - ['value' => self::VALUE_PERCENT, 'label' => __('Discount')], + ['value' => self::VALUE_PERCENT, 'label' => __('Percent')], ]; } } diff --git a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/TierPrice.php b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/TierPrice.php new file mode 100644 index 0000000000000000000000000000000000000000..d630f4890fc95afd894ab88b81aedc552e9bc50c --- /dev/null +++ b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/TierPrice.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Config\Source\Product\Options; + +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; + +/** + * TierPrice types mode source. + */ +class TierPrice implements ProductPriceOptionsInterface +{ + /** + * {@inheritdoc} + * + * @codeCoverageIgnore + */ + public function toOptionArray() + { + return [ + ['value' => self::VALUE_FIXED, 'label' => __('Fixed')], + ['value' => self::VALUE_PERCENT, 'label' => __('Discount')], + ]; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php b/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php new file mode 100644 index 0000000000000000000000000000000000000000..b7c01141de33bb7202ccad4eff2605bb7bdcbae8 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +use Magento\Catalog\Api\Data\BasePriceInterface; + +/** + * Product Base Price DTO. + */ +class BasePrice extends \Magento\Framework\Model\AbstractExtensibleModel implements BasePriceInterface +{ + /** + * {@inheritdoc} + */ + public function setPrice($price) + { + return $this->setData(self::PRICE, $price); + } + + /** + * {@inheritdoc} + */ + public function getPrice() + { + return $this->getData(self::PRICE); + } + + /** + * {@inheritdoc} + */ + public function setStoreId($storeId) + { + return $this->setData(self::STORE_ID, $storeId); + } + + /** + * {@inheritdoc} + */ + public function getStoreId() + { + return $this->getData(self::STORE_ID); + } + + /** + * {@inheritdoc} + */ + public function setSku($sku) + { + return $this->setData(self::SKU, $sku); + } + + /** + * {@inheritdoc} + */ + public function getSku() + { + return $this->getData(self::SKU); + } + + /** + * {@inheritdoc} + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + */ + public function setExtensionAttributes(\Magento\Catalog\Api\Data\BasePriceExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php new file mode 100644 index 0000000000000000000000000000000000000000..e69f89f0bb146d62e7cc65d1022a4d25b6738c4f --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php @@ -0,0 +1,227 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +/** + * Base prices storage. + */ +class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface +{ + /** + * Attribute code. + * + * @var string + */ + private $attributeCode = 'price'; + + /** + * @var PricePersistence + */ + private $pricePersistence; + + /** + * @var \Magento\Catalog\Api\Data\BasePriceInterfaceFactory + */ + private $basePriceInterfaceFactory; + + /** + * @var \Magento\Catalog\Model\ProductIdLocatorInterface + */ + private $productIdLocator; + + /** + * @var \Magento\Store\Api\StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @var \Magento\Catalog\Api\ProductRepositoryInterface + */ + private $productRepository; + + /** + * Price type allowed. + * + * @var int + */ + private $priceTypeAllowed = 1; + + /** + * Allowed product types. + * + * @var array + */ + private $allowedProductTypes = []; + + /** + * @var PricePersistenceFactory + */ + private $pricePersistenceFactory; + + /** + * BasePriceStorage constructor. + * + * @param PricePersistenceFactory $pricePersistenceFactory + * @param \Magento\Catalog\Api\Data\BasePriceInterfaceFactory $basePriceInterfaceFactory + * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator + * @param \Magento\Store\Api\StoreRepositoryInterface $storeRepository + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param array $allowedProductTypes + */ + public function __construct( + PricePersistenceFactory $pricePersistenceFactory, + \Magento\Catalog\Api\Data\BasePriceInterfaceFactory $basePriceInterfaceFactory, + \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator, + \Magento\Store\Api\StoreRepositoryInterface $storeRepository, + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + array $allowedProductTypes = [] + ) { + $this->pricePersistenceFactory = $pricePersistenceFactory; + $this->basePriceInterfaceFactory = $basePriceInterfaceFactory; + $this->productIdLocator = $productIdLocator; + $this->storeRepository = $storeRepository; + $this->productRepository = $productRepository; + $this->allowedProductTypes = $allowedProductTypes; + } + + /** + * {@inheritdoc} + */ + public function get(array $skus) + { + $this->validateSkus($skus); + $rawPrices = $this->getPricePersistence()->get($skus); + $prices = []; + foreach ($rawPrices as $rawPrice) { + $price = $this->basePriceInterfaceFactory->create(); + $sku = $this->getPricePersistence() + ->retrieveSkuById($rawPrice[$this->getPricePersistence()->getEntityLinkField()], $skus); + $price->setSku($sku); + $price->setPrice($rawPrice['value']); + $price->setStoreId($rawPrice['store_id']); + $prices[] = $price; + } + + return $prices; + } + + /** + * {@inheritdoc} + */ + public function update(array $prices) + { + $this->validate($prices); + $formattedPrices = []; + + foreach ($prices as $price) { + $ids = array_keys($this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()])[$price->getSku()]); + foreach ($ids as $id) { + $formattedPrices[] = [ + 'store_id' => $price->getStoreId(), + $this->getPricePersistence()->getEntityLinkField() => $id, + 'value' => $price->getPrice(), + ]; + } + } + + $this->getPricePersistence()->update($formattedPrices); + + return true; + } + + /** + * Get price persistence. + * + * @return PricePersistence + */ + private function getPricePersistence() + { + if (!$this->pricePersistence) { + $this->pricePersistence = $this->pricePersistenceFactory->create(['attributeCode' => $this->attributeCode]); + } + + return $this->pricePersistence; + } + + /** + * Validate SKU, check product types and skip not existing products. + * + * @param array $skus + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + private function validateSkus(array $skus) + { + $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus); + $skuDiff = array_diff($skus, array_keys($idsBySku)); + + foreach ($idsBySku as $sku => $ids) { + foreach ($ids as $type) { + if (!in_array($type, $this->allowedProductTypes) + || ( + $type == \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE + && $this->productRepository->get($sku)->getPriceType() != $this->priceTypeAllowed + ) + ) { + $skuDiff[] = $sku; + break; + } + } + } + + if (!empty($skuDiff)) { + $values = implode(', ', $skuDiff); + $description = count($skuDiff) == 1 + ? __('Requested product doesn\'t exist: %1', $values) + : __('Requested products don\'t exist: %1', $values); + throw new \Magento\Framework\Exception\NoSuchEntityException($description); + } + } + + /** + * Validate that prices have appropriate values. + * + * @param array $prices + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + private function validate(array $prices) + { + $skus = array_unique( + array_map(function ($price) { + if (!$price->getSku()) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'sku', + 'fieldValue' => $price->getSku() + ] + ) + ); + } + return $price->getSku(); + }, $prices) + ); + $this->validateSkus($skus); + + foreach ($prices as $price) { + if (null === $price->getPrice() || $price->getPrice() < 0) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'Price', + 'fieldValue' => $price->getPrice() + ] + ) + ); + } + $this->storeRepository->getById($price->getStoreId()); + } + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/Cost.php b/app/code/Magento/Catalog/Model/Product/Price/Cost.php new file mode 100644 index 0000000000000000000000000000000000000000..8d52c578ea94b431254883d0e815c1f0064c22d2 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/Cost.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +use Magento\Catalog\Api\Data\CostInterface; + +/** + * Product Cost DTO. + */ +class Cost extends \Magento\Framework\Model\AbstractExtensibleModel implements CostInterface +{ + /** + * {@inheritdoc} + */ + public function setCost($cost) + { + return $this->setData(self::COST, $cost); + } + + /** + * {@inheritdoc} + */ + public function getCost() + { + return $this->getData(self::COST); + } + + /** + * {@inheritdoc} + */ + public function setStoreId($storeId) + { + return $this->setData(self::STORE_ID, $storeId); + } + + /** + * {@inheritdoc} + */ + public function getStoreId() + { + return $this->getData(self::STORE_ID); + } + + /** + * {@inheritdoc} + */ + public function setSku($sku) + { + return $this->setData(self::SKU, $sku); + } + + /** + * {@inheritdoc} + */ + public function getSku() + { + return $this->getData(self::SKU); + } + + /** + * {@inheritdoc} + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + */ + public function setExtensionAttributes(\Magento\Catalog\Api\Data\CostExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php new file mode 100644 index 0000000000000000000000000000000000000000..e7fc682514a3fa3c575f13938a0a86048a0e50fb --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php @@ -0,0 +1,218 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +/** + * Product cost storage. + */ +class CostStorage implements \Magento\Catalog\Api\CostStorageInterface +{ + /** + * Attribute code. + * + * @var string + */ + private $attributeCode = 'cost'; + + /** + * @var PricePersistence + */ + private $pricePersistence; + + /** + * @var \Magento\Catalog\Api\Data\CostInterfaceFactory + */ + private $costInterfaceFactory; + + /** + * @var \Magento\Catalog\Model\ProductIdLocatorInterface + */ + private $productIdLocator; + + /** + * Allowed product types. + * + * @var array + */ + private $allowedProductTypes = []; + + /** + * @var PricePersistenceFactory + */ + private $pricePersistenceFactory; + + /** + * @var \Magento\Store\Api\StoreRepositoryInterface + */ + private $storeRepository; + + /** + * CostStorage constructor. + * + * @param PricePersistenceFactory $pricePersistenceFactory + * @param \Magento\Catalog\Api\Data\CostInterfaceFactory $costInterfaceFactory + * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator + * @param \Magento\Store\Api\StoreRepositoryInterface $storeRepository + * @param array $allowedProductTypes + */ + public function __construct( + PricePersistenceFactory $pricePersistenceFactory, + \Magento\Catalog\Api\Data\CostInterfaceFactory $costInterfaceFactory, + \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator, + \Magento\Store\Api\StoreRepositoryInterface $storeRepository, + array $allowedProductTypes = [] + ) { + $this->pricePersistenceFactory = $pricePersistenceFactory; + $this->costInterfaceFactory = $costInterfaceFactory; + $this->productIdLocator = $productIdLocator; + $this->storeRepository = $storeRepository; + $this->allowedProductTypes = $allowedProductTypes; + } + + /** + * {@inheritdoc} + */ + public function get(array $skus) + { + $this->validateSkus($skus); + $rawPrices = $this->getPricePersistence()->get($skus); + $prices = []; + foreach ($rawPrices as $rawPrice) { + $price = $this->costInterfaceFactory->create(); + $sku = $this->getPricePersistence() + ->retrieveSkuById($rawPrice[$this->getPricePersistence()->getEntityLinkField()], $skus); + $price->setSku($sku); + $price->setCost($rawPrice['value']); + $price->setStoreId($rawPrice['store_id']); + $prices[] = $price; + } + + return $prices; + } + + /** + * {@inheritdoc} + */ + public function update(array $prices) + { + $this->validate($prices); + $formattedPrices = []; + + foreach ($prices as $price) { + $ids = array_keys($this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()])[$price->getSku()]); + foreach ($ids as $id) { + $formattedPrices[] = [ + 'store_id' => $price->getStoreId(), + $this->getPricePersistence()->getEntityLinkField() => $id, + 'value' => $price->getCost(), + ]; + } + } + + $this->getPricePersistence()->update($formattedPrices); + + return true; + } + + /** + * {@inheritdoc} + */ + public function delete(array $skus) + { + $this->validateSkus($skus); + $this->getPricePersistence()->delete($skus); + + return true; + } + + /** + * Get price persistence. + * + * @return PricePersistence + */ + private function getPricePersistence() + { + if (!$this->pricePersistence) { + $this->pricePersistence = $this->pricePersistenceFactory->create(['attributeCode' => $this->attributeCode]); + } + + return $this->pricePersistence; + } + + /** + * Validate that prices have appropriate values. + * + * @param array $prices + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + private function validate(array $prices) + { + $skus = array_unique( + array_map(function ($price) { + if (!$price->getSku()) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'sku', + 'fieldValue' => $price->getSku() + ] + ) + ); + } + return $price->getSku(); + }, $prices) + ); + $this->validateSkus($skus); + + foreach ($prices as $price) { + if (null === $price->getCost() || $price->getCost() < 0) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'Cost', + 'fieldValue' => $price->getCost() + ] + ) + ); + } + $this->storeRepository->getById($price->getStoreId()); + } + } + + /** + * Validate SKU, check product types and skip not existing products. + * + * @param array $skus + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + private function validateSkus(array $skus) + { + $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus); + $skuDiff = array_diff($skus, array_keys($idsBySku)); + + foreach ($idsBySku as $sku => $ids) { + foreach (array_values($ids) as $type) { + if (!in_array($type, $this->allowedProductTypes)) { + $skuDiff[] = $sku; + break; + } + } + } + + if (!empty($skuDiff)) { + $values = implode(', ', $skuDiff); + $description = count($skuDiff) == 1 + ? __('Requested product doesn\'t exist: %1', $values) + : __('Requested products don\'t exist: %1', $values); + throw new \Magento\Framework\Exception\NoSuchEntityException($description); + } + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php b/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php new file mode 100644 index 0000000000000000000000000000000000000000..f37fb15cd47e433e4125b0bd51d90d00ae291cf2 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php @@ -0,0 +1,228 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +/** + * Price persistence. + */ +class PricePersistence +{ + /** + * Price storage table. + * + * @var string + */ + private $table = 'catalog_product_entity_decimal'; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Attribute + */ + private $attributeResource; + + /** + * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface + */ + private $attributeRepository; + + /** + * @var \Magento\Catalog\Model\ProductIdLocatorInterface + */ + private $productIdLocator; + + /** + * Metadata pool. + * + * @var \Magento\Framework\EntityManager\MetadataPool + */ + private $metadataPool; + + /** + * Attribute code. + * + * @var string + */ + private $attributeCode; + + /** + * Attribute ID. + * + * @var int + */ + private $attributeId; + + /** + * Items per operation. + * + * @var int + */ + private $itemsPerOperation = 500; + + /** + * PricePersistence constructor. + * + * @param \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource + * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository + * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator + * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool + * @param string $attributeCode + */ + public function __construct( + \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource, + \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository, + \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator, + \Magento\Framework\EntityManager\MetadataPool $metadataPool, + $attributeCode = '' + ) { + $this->attributeResource = $attributeResource; + $this->attributeRepository = $attributeRepository; + $this->attributeCode = $attributeCode; + $this->productIdLocator = $productIdLocator; + $this->metadataPool = $metadataPool; + } + + /** + * Get prices by SKUs. + * + * @param array $skus + * @return array + */ + public function get(array $skus) + { + $ids = $this->retrieveAffectedIds($skus); + $select = $this->attributeResource->getConnection() + ->select() + ->from($this->attributeResource->getTable($this->table)); + return $this->attributeResource->getConnection()->fetchAll( + $select->where($this->getEntityLinkField() . ' IN (?)', $ids) + ->where('attribute_id = ?', $this->getAttributeId()) + ); + } + + /** + * Update prices. + * + * @param array $prices + * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function update(array $prices) + { + array_walk($prices, function (&$price) { + return $price['attribute_id'] = $this->getAttributeId(); + }); + $connection = $this->attributeResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) { + $this->attributeResource->getConnection()->insertOnDuplicate( + $this->attributeResource->getTable($this->table), + $pricesBunch, + ['value'] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotSaveException( + __('Could not save Prices.'), + $e + ); + } + } + + /** + * Delete product attribute by SKU. + * + * @param array $skus + * @return void + * @throws \Magento\Framework\Exception\CouldNotDeleteException + */ + public function delete(array $skus) + { + $ids = $this->retrieveAffectedIds($skus); + $connection = $this->attributeResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) { + $this->attributeResource->getConnection()->delete( + $this->attributeResource->getTable($this->table), + [ + 'attribute_id = ?' => $this->getAttributeId(), + $this->getEntityLinkField() . ' IN (?)' => $idsBunch + ] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotDeleteException( + __('Could not delete Prices'), + $e + ); + } + } + + /** + * Retrieve SKU by product ID. + * + * @param int $id + * @param array $skus + * @return int|null + */ + public function retrieveSkuById($id, $skus) + { + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) { + if (false !== array_key_exists($id, $ids)) { + return $sku; + } + } + + return null; + } + + /** + * Get attribute ID. + * + * @return int + */ + private function getAttributeId() + { + if (!$this->attributeId) { + $this->attributeId = $this->attributeRepository->get($this->attributeCode)->getAttributeId(); + } + + return $this->attributeId; + } + + /** + * Retrieve affected product IDs. + * + * @param array $skus + * @return array + */ + private function retrieveAffectedIds(array $skus) + { + $affectedIds = []; + + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $productIds) { + $affectedIds = array_merge($affectedIds, array_keys($productIds)); + } + + return array_unique($affectedIds); + } + + /** + * Get link field. + * + * @return string + */ + public function getEntityLinkField() + { + return $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php b/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php new file mode 100644 index 0000000000000000000000000000000000000000..c3c30d18fe639df371a6a5b3d51f7f06492be4f5 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +use Magento\Catalog\Api\Data\TierPriceInterface; + +/** + * TierPrice DTO. + */ +class TierPrice extends \Magento\Framework\Model\AbstractExtensibleModel implements TierPriceInterface +{ + /** + * {@inheritdoc} + */ + public function setPrice($price) + { + return $this->setData(self::PRICE, $price); + } + + /** + * {@inheritdoc} + */ + public function getPrice() + { + return $this->getData(self::PRICE); + } + + /** + * {@inheritdoc} + */ + public function setPriceType($type) + { + return $this->setData(self::PRICE_TYPE, $type); + } + + /** + * {@inheritdoc} + */ + public function getPriceType() + { + return $this->getData(self::PRICE_TYPE); + } + + /** + * {@inheritdoc} + */ + public function setWebsiteId($websiteId) + { + return $this->setData(self::WEBSITE_ID, $websiteId); + } + + /** + * {@inheritdoc} + */ + public function getWebsiteId() + { + return $this->getData(self::WEBSITE_ID); + } + + /** + * {@inheritdoc} + */ + public function setSku($sku) + { + return $this->setData(self::SKU, $sku); + } + + /** + * {@inheritdoc} + */ + public function getSku() + { + return $this->getData(self::SKU); + } + + /** + * {@inheritdoc} + */ + public function setCustomerGroup($group) + { + return $this->setData(self::CUSTOMER_GROUP, $group); + } + + /** + * {@inheritdoc} + */ + public function getCustomerGroup() + { + return $this->getData(self::CUSTOMER_GROUP); + } + + /** + * {@inheritdoc} + */ + public function setQuantity($quantity) + { + return $this->setData(self::QUANTITY, $quantity); + } + + /** + * {@inheritdoc} + */ + public function getQuantity() + { + return $this->getData(self::QUANTITY); + } + + /** + * {@inheritdoc} + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + */ + public function setExtensionAttributes(\Magento\Catalog\Api\Data\TierPriceExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1e031649ebdcf2830826e57dccb974a51b7efa71 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php @@ -0,0 +1,169 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +use Magento\Catalog\Api\Data\TierPriceInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Tier price factory. + */ +class TierPriceFactory +{ + /** + * Tier price factory. + * + * @var \Magento\Catalog\Api\Data\TierPriceInterfaceFactory + */ + private $tierPriceFactory; + + /** + * Tier price persistence. + * + * @var TierPricePersistence + */ + private $tierPricePersistence; + + /** + * Customer group repository. + * + * @var \Magento\Customer\Api\GroupRepositoryInterface + */ + private $customerGroupRepository; + + /** + * All groups value. + * + * @var string + */ + private $allGroupsValue = 'all groups'; + + /** + * All groups ID. + * + * @var int + */ + private $allGroupsId = 1; + + /** + * Customer groups by code. + * + * @var array + */ + private $customerGroupsByCode = []; + + /** + * TierPriceBuilder constructor. + * + * @param \Magento\Catalog\Api\Data\TierPriceInterfaceFactory $tierPriceFactory + * @param TierPricePersistence $tierPricePersistence + * @param \Magento\Customer\Api\GroupRepositoryInterface $customerGroupRepository + * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder + * @param \Magento\Framework\Api\FilterBuilder $filterBuilder + */ + public function __construct( + \Magento\Catalog\Api\Data\TierPriceInterfaceFactory $tierPriceFactory, + TierPricePersistence $tierPricePersistence, + \Magento\Customer\Api\GroupRepositoryInterface $customerGroupRepository, + \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, + \Magento\Framework\Api\FilterBuilder $filterBuilder + ) { + $this->tierPriceFactory = $tierPriceFactory; + $this->tierPricePersistence = $tierPricePersistence; + $this->customerGroupRepository = $customerGroupRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->filterBuilder = $filterBuilder; + } + + /** + * Create populated tier price DTO. + * + * @param array $rawPrice + * @param string $sku + * @return \Magento\Catalog\Api\Data\TierPriceInterface + */ + public function create(array $rawPrice, $sku) + { + $price = $this->tierPriceFactory->create(); + $price->setPrice(isset($rawPrice['percentage_value']) ? $rawPrice['percentage_value'] : $rawPrice['value']); + $price->setPriceType( + isset($rawPrice['percentage_value']) + ? TierPriceInterface::PRICE_TYPE_DISCOUNT + : TierPriceInterface::PRICE_TYPE_FIXED + ); + $price->setWebsiteId($rawPrice['website_id']); + $price->setSku($sku); + $price->setCustomerGroup( + $rawPrice['all_groups'] == $this->allGroupsId + ? $this->allGroupsValue + : $this->customerGroupRepository->getById($rawPrice['customer_group_id'])->getCode() + ); + $price->setQuantity($rawPrice['qty']); + + return $price; + } + + /** + * Build tier price skeleton that has DB consistent format. + * + * @param TierPriceInterface $price + * @param int $id + * @return array + */ + public function createSkeleton(TierPriceInterface $price, $id) + { + return [ + $this->tierPricePersistence->getEntityLinkField() => $id, + 'all_groups' => $this->retrievePriceForAllGroupsValue($price), + 'customer_group_id' => $this->retrievePriceForAllGroupsValue($price) === $this->allGroupsId + ? 0 + : $this->retrieveGroupValue(strtolower($price->getCustomerGroup())), + 'qty' => $price->getQuantity(), + 'value' => $price->getPriceType() === TierPriceInterface::PRICE_TYPE_FIXED + ? $price->getPrice() + : 0.00, + 'percentage_value' => $price->getPriceType() === TierPriceInterface::PRICE_TYPE_DISCOUNT + ? $price->getPrice() + : null, + 'website_id' => $price->getWebsiteId() + ]; + } + + /** + * Retrieve price for all groups value. + * + * @param TierPriceInterface $price + * @return int + */ + private function retrievePriceForAllGroupsValue(TierPriceInterface $price) + { + return strcasecmp($price->getCustomerGroup(), $this->allGroupsValue) === 0 ? $this->allGroupsId : 0; + } + + /** + * Retrieve customer group id by code. + * + * @param string $code + * @return int + * @throws NoSuchEntityException + */ + private function retrieveGroupValue($code) + { + if (!isset($this->customerGroupsByCode[$code])) { + $searchCriteria = $this->searchCriteriaBuilder->addFilters( + [ + $this->filterBuilder->setField('customer_group_code')->setValue($code)->create() + ] + ); + $items = $this->customerGroupRepository->getList($searchCriteria->create())->getItems(); + $item = array_shift($items); + $this->customerGroupsByCode[strtolower($item->getCode())] = $item->getId(); + } + + return $this->customerGroupsByCode[$code]; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php b/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php new file mode 100644 index 0000000000000000000000000000000000000000..01293d0532fbfc82ed0893436baad30e2baa87b8 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +/** + * Persists tier prices. + */ +class TierPricePersistence +{ + /** + * Number or items per each operation. + * + * @var int + */ + private $itemsPerOperation = 500; + + /** + * Tier price resource model. + * + * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice + */ + private $tierpriceResource; + + /** + * Metadata pool. + * + * @var \Magento\Framework\EntityManager\MetadataPool + */ + private $metadataPool; + + /** + * TierPricePersister constructor. + * + * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice $tierpriceResource + * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool + */ + public function __construct( + \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice $tierpriceResource, + \Magento\Framework\EntityManager\MetadataPool $metadataPool + ) { + $this->tierpriceResource = $tierpriceResource; + $this->metadataPool = $metadataPool; + } + + /** + * Get tier prices by product IDs. + * + * @param array $ids + * @return array + */ + public function get(array $ids) + { + $select = $this->tierpriceResource->getConnection()->select()->from($this->tierpriceResource->getMainTable()); + return $this->tierpriceResource->getConnection()->fetchAll( + $select->where($this->getEntityLinkField() . ' IN (?)', $ids) + ); + } + + /** + * Update tier prices. + * + * @param array $prices + * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function update(array $prices) + { + $connection = $this->tierpriceResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) { + $this->tierpriceResource->getConnection()->insertOnDuplicate( + $this->tierpriceResource->getMainTable(), + $pricesBunch, + ['value', 'percentage_value'] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotSaveException( + __('Could not save Tier Prices'), + $e + ); + } + } + + /** + * Replace prices. + * + * @param array $prices + * @param array $ids + * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function replace(array $prices, array $ids) + { + $connection = $this->tierpriceResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) { + $this->tierpriceResource->getConnection()->delete( + $this->tierpriceResource->getMainTable(), + [$this->getEntityLinkField() . ' IN (?)' => $idsBunch] + ); + } + + foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) { + $this->tierpriceResource->getConnection()->insertMultiple( + $this->tierpriceResource->getMainTable(), + $pricesBunch + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotSaveException( + __('Could not replace Tier Prices'), + $e + ); + } + } + + /** + * Delete tier prices by IDs. + * + * @param array $ids + * @return void + * @throws \Magento\Framework\Exception\CouldNotDeleteException + */ + public function delete(array $ids) + { + $connection = $this->tierpriceResource->getConnection(); + $connection->beginTransaction(); + try { + foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) { + $this->tierpriceResource->getConnection()->delete( + $this->tierpriceResource->getMainTable(), + ['value_id IN (?)' => $idsBunch] + ); + } + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw new \Magento\Framework\Exception\CouldNotDeleteException( + __('Could not delete Tier Prices'), + $e + ); + } + } + + /** + * Get link field. + * + * @return string + */ + public function getEntityLinkField() + { + return $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php new file mode 100644 index 0000000000000000000000000000000000000000..83262bbfa1cca6432109f17ef537c4a039c64c04 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -0,0 +1,323 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +use Magento\Catalog\Api\Data\TierPriceInterface; + +/** + * Tier price storage. + */ +class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface +{ + /** + * Tier price resource model. + * + * @var TierPricePersistence + */ + private $tierPricePersistence; + + /** + * Tier price validator. + * + * @var \Magento\Catalog\Model\Product\Price\TierPriceValidator + */ + private $tierPriceValidator; + + /** + * Tier price builder. + * + * @var TierPriceFactory + */ + private $tierPriceFactory; + + /** + * Price indexer. + * + * @var \Magento\Catalog\Model\Indexer\Product\Price + */ + private $priceIndexer; + + /** + * Product ID locator. + * + * @var \Magento\Catalog\Model\ProductIdLocatorInterface + */ + private $productIdLocator; + + /** + * Page cache config. + * + * @var \Magento\PageCache\Model\Config + */ + private $config; + + /** + * Cache type list. + * + * @var \Magento\Framework\App\Cache\TypeListInterface + */ + private $typeList; + + /** + * Indexer chunk value. + * + * @var int + */ + private $indexerChunkValue = 500; + + /** + * TierPriceStorage constructor. + * + * @param TierPricePersistence $tierPricePersistence + * @param TierPriceValidator $tierPriceValidator + * @param TierPriceFactory $tierPriceFactory + * @param \Magento\Catalog\Model\Indexer\Product\Price $priceIndexer + * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator + * @param \Magento\PageCache\Model\Config $config + * @param \Magento\Framework\App\Cache\TypeListInterface $typeList + */ + public function __construct( + TierPricePersistence $tierPricePersistence, + TierPriceValidator $tierPriceValidator, + TierPriceFactory $tierPriceFactory, + \Magento\Catalog\Model\Indexer\Product\Price $priceIndexer, + \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator, + \Magento\PageCache\Model\Config $config, + \Magento\Framework\App\Cache\TypeListInterface $typeList + ) { + $this->tierPricePersistence = $tierPricePersistence; + $this->tierPriceValidator = $tierPriceValidator; + $this->tierPriceFactory = $tierPriceFactory; + $this->priceIndexer = $priceIndexer; + $this->productIdLocator = $productIdLocator; + $this->config = $config; + $this->typeList = $typeList; + } + + /** + * {@inheritdoc} + */ + public function get(array $skus) + { + $this->tierPriceValidator->validateSkus($skus); + $ids = $this->retrieveAffectedIds($skus); + $rawPrices = $this->tierPricePersistence->get($ids); + $prices = []; + + foreach ($rawPrices as $rawPrice) { + $sku = $this->retrieveSkuById($rawPrice[$this->tierPricePersistence->getEntityLinkField()], $skus); + $prices[] = $this->tierPriceFactory->create($rawPrice, $sku); + } + + return $prices; + } + + /** + * {@inheritdoc} + */ + public function update(array $prices) + { + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + $this->tierPriceValidator->validatePrices($prices, $this->get($skus)); + $formattedPrices = $this->retrieveFormattedPrices($prices); + $this->tierPricePersistence->update($formattedPrices); + $this->reindexPrices($affectedIds); + $this->invalidateFullPageCache(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function replace(array $prices) + { + $this->tierPriceValidator->validatePrices($prices); + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); + $formattedPrices = $this->retrieveFormattedPrices($prices); + $this->tierPricePersistence->replace($formattedPrices, $affectedIds); + $this->reindexPrices($affectedIds); + $this->invalidateFullPageCache(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function delete(array $prices) + { + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); + $this->tierPriceValidator->validatePrices($prices); + $priceIds = $this->retrieveAffectedPriceIds($prices); + $this->tierPricePersistence->delete($priceIds); + $this->reindexPrices($affectedIds); + $this->invalidateFullPageCache(); + + return true; + } + + /** + * Retrieve formatted prices. + * + * @param array $prices + * @return array + */ + private function retrieveFormattedPrices(array $prices) + { + $formattedPrices = []; + + foreach ($prices as $price) { + $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()]); + $ids = array_keys($idsBySku[$price->getSku()]); + foreach ($ids as $id) { + $formattedPrices[] = $this->tierPriceFactory->createSkeleton($price, $id); + } + } + + return $formattedPrices; + } + + /** + * Retrieve affected product IDs for prices. + * + * @param TierPriceInterface[] $prices + * @return array + */ + private function retrieveAffectedProductIdsForPrices(array $prices) + { + $skus = array_unique( + array_map(function ($price) { + return $price->getSku(); + }, $prices) + ); + + return $this->retrieveAffectedIds($skus); + } + + /** + * Retrieve affected product IDs. + * + * @param array $skus + * @return array + */ + private function retrieveAffectedIds(array $skus) + { + $affectedIds = []; + + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $productId) { + $affectedIds = array_merge($affectedIds, array_keys($productId)); + } + + return array_unique($affectedIds); + } + + /** + * Retrieve affected price IDs. + * + * @param array $prices + * @return array + */ + private function retrieveAffectedPriceIds(array $prices) + { + $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices); + $formattedPrices = $this->retrieveFormattedPrices($prices); + $existingPrices = $this->tierPricePersistence->get($affectedIds); + $priceIds = []; + + foreach ($formattedPrices as $price) { + $priceIds[] = $this->retrievePriceId($price, $existingPrices); + } + + return $priceIds; + } + + /** + * Retrieve price ID. + * + * @param array $price + * @param array $existingPrices + * @return int + */ + private function retrievePriceId(array $price, array $existingPrices) + { + $linkField = $this->tierPricePersistence->getEntityLinkField(); + + foreach ($existingPrices as $existingPrice) { + if ($existingPrice['all_groups'] == $price['all_groups'] + && $existingPrice['customer_group_id'] == $price['customer_group_id'] + && $existingPrice['qty'] == $price['qty'] + && $this->isCorrectPriceValue($existingPrice, $price) + && $existingPrice[$linkField] == $price[$linkField] + ) { + return $existingPrice['value_id']; + } + } + } + + /** + * Check is correct price value + * + * @param array $existingPrice + * @param array $price + * @return bool + */ + private function isCorrectPriceValue(array $existingPrice, array $price) + { + return ($existingPrice['value'] != 0 && $existingPrice['value'] == $price['value']) + || ($existingPrice['percentage_value'] !== null + && $existingPrice['percentage_value'] == $price['percentage_value']); + } + + /** + * Retrieve SKU by product ID. + * + * @param int $id + * @param array $skus + * @return int|null + */ + private function retrieveSkuById($id, $skus) + { + foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) { + if (false !== array_key_exists($id, $ids)) { + return $sku; + } + } + + return null; + } + + /** + * Invalidate full page cache. + * + * @return void + */ + private function invalidateFullPageCache() + { + if ($this->config->isEnabled()) { + $this->typeList->invalidate('full_page'); + } + } + + /** + * Reindex prices. + * + * @param array $ids + * @return void + */ + private function reindexPrices(array $ids) + { + foreach (array_chunk($ids, $this->indexerChunkValue) as $affectedIds) { + $this->priceIndexer->execute($affectedIds); + } + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php new file mode 100644 index 0000000000000000000000000000000000000000..907fd0f66bbdd3d02ff288c141361d56fcfe9694 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php @@ -0,0 +1,351 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model\Product\Price; + +use Magento\Catalog\Api\Data\TierPriceInterface; + +/** + * Tier Price Validator. + */ +class TierPriceValidator +{ + /** + * Groups by code cache. + * + * @var array + */ + private $customerGroupsByCode = []; + + /** + * @var TierPricePersistence + */ + private $tierPricePersistence; + + /** + * All groups value. + * + * @var string + */ + private $allGroupsValue = 'all groups'; + + /** + * All websites value. + * + * @var string + */ + private $allWebsitesValue = "0"; + + /** + * Allowed product types. + * + * @var array + */ + private $allowedProductTypes = []; + + /** + * TierPriceValidator constructor. + * + * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator + * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder + * @param \Magento\Framework\Api\FilterBuilder $filterBuilder + * @param \Magento\Customer\Api\GroupRepositoryInterface $customerGroupRepository + * @param \Magento\Store\Api\WebsiteRepositoryInterface $websiteRepository + * @param TierPricePersistence $tierPricePersistence + * @param array $allowedProductTypes + */ + public function __construct( + \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator, + \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, + \Magento\Framework\Api\FilterBuilder $filterBuilder, + \Magento\Customer\Api\GroupRepositoryInterface $customerGroupRepository, + \Magento\Store\Api\WebsiteRepositoryInterface $websiteRepository, + TierPricePersistence $tierPricePersistence, + array $allowedProductTypes = [] + ) { + $this->productIdLocator = $productIdLocator; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->filterBuilder = $filterBuilder; + $this->customerGroupRepository = $customerGroupRepository; + $this->websiteRepository = $websiteRepository; + $this->tierPricePersistence = $tierPricePersistence; + $this->allowedProductTypes = $allowedProductTypes; + } + + /** + * Validate SKU. + * + * @param array $skus + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + public function validateSkus(array $skus) + { + $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus); + $skuDiff = array_diff($skus, array_keys($idsBySku)); + + foreach ($idsBySku as $sku => $ids) { + foreach (array_values($ids) as $type) { + if (!in_array($type, $this->allowedProductTypes)) { + $skuDiff[] = $sku; + break; + } + } + } + + if (!empty($skuDiff)) { + $values = implode(', ', $skuDiff); + $description = count($skuDiff) == 1 + ? __('Requested product doesn\'t exist: %1', $values) + : __('Requested products don\'t exist: %1', $values); + throw new \Magento\Framework\Exception\NoSuchEntityException($description); + } + } + + /** + * Validate that prices have appropriate values and are unique. + * + * @param array $prices + * @param array $existingPrices + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function validatePrices(array $prices, array $existingPrices = []) + { + $skus = array_unique( + array_map(function ($price) { + if (!$price->getSku()) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'sku', + 'fieldValue' => $price->getSku() + ] + ) + ); + } + return $price->getSku(); + }, $prices) + ); + $this->validateSkus($skus); + $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus); + + $pricesBySku = []; + + foreach ($prices as $price) { + $pricesBySku[$price->getSku()][] = $price; + } + + /** @var TierPriceInterface $price */ + foreach ($prices as $price) { + $this->checkPrice($price); + $this->checkPriceType($price, $idsBySku[$price->getSku()]); + $this->checkQuantity($price); + $this->checkWebsite($price); + if (isset($pricesBySku[$price->getSku()])) { + $this->checkUnique($price, $pricesBySku[$price->getSku()]); + } + $this->checkUnique($price, $existingPrices); + $this->checkGroup($price); + } + } + + /** + * Verify that price value is correct. + * + * @param TierPriceInterface $price + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + private function checkPrice(TierPriceInterface $price) + { + if ( + null === $price->getPrice() + || $price->getPrice() < 0 + || ($price->getPriceType() === TierPriceInterface::PRICE_TYPE_DISCOUNT && $price->getPrice() > 100) + ) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'Price', + 'fieldValue' => $price->getPrice() + ] + ) + ); + } + } + + /** + * Verify that price type is correct. + * + * @param TierPriceInterface $price + * @param array $ids + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + private function checkPriceType(TierPriceInterface $price, array $ids) + { + if ( + !in_array( + $price->getPriceType(), + [TierPriceInterface::PRICE_TYPE_FIXED, TierPriceInterface::PRICE_TYPE_DISCOUNT] + ) + || (array_search(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE, $ids) + && $price->getPriceType() !== TierPriceInterface::PRICE_TYPE_DISCOUNT) + ) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'Price Type', + 'fieldValue' => $price->getPriceType() + ] + ) + ); + } + } + + /** + * Verify that product quantity is correct. + * + * @param TierPriceInterface $price + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + private function checkQuantity(TierPriceInterface $price) + { + if ($price->getQuantity() < 1) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'Quantity', + 'fieldValue' => $price->getQuantity() + ] + ) + ); + } + } + + /** + * Verify that website exists. + * + * @param TierPriceInterface $price + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function checkWebsite(TierPriceInterface $price) + { + try { + $this->websiteRepository->getById($price->getWebsiteId()); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + throw new \Magento\Framework\Exception\NoSuchEntityException( + __( + 'Invalid attribute %fieldName: %fieldValue.', + [ + 'fieldName' => 'website_id', + 'fieldValue' => $price->getWebsiteId() + ] + ) + ); + } + } + + /** + * Check website value is unique. + * + * @param TierPriceInterface $tierPrice + * @param array $prices + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function checkUnique(TierPriceInterface $tierPrice, array $prices) + { + /** @var TierPriceInterface $price */ + foreach ($prices as $price) { + if ( + $price->getSku() === $tierPrice->getSku() + && $price->getCustomerGroup() === $tierPrice->getCustomerGroup() + && $price->getQuantity() == $tierPrice->getQuantity() + && ( + ($price->getWebsiteId() == $this->allWebsitesValue + || $tierPrice->getWebsiteId() == $this->allWebsitesValue) + && $price->getWebsiteId() != $tierPrice->getWebsiteId() + ) + ) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'We found a duplicate website, tier price, customer group and quantity: ' + . '%fieldName1 = %fieldValue1, %fieldName2 = %fieldValue2, %fieldName3 = %fieldValue3.', + [ + 'fieldName1' => 'Customer Group', + 'fieldValue1' => $price->getCustomerGroup(), + 'fieldName2' => 'Website Id', + 'fieldValue2' => $price->getWebsiteId(), + 'fieldName3' => 'Quantity', + 'fieldValue3' => $price->getQuantity() + ] + ) + ); + } + } + } + + /** + * Check customer group exists and has correct value. + * + * @param TierPriceInterface $price + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @return void + */ + private function checkGroup(TierPriceInterface $price) + { + $customerGroup = strtolower($price->getCustomerGroup()); + + if ($customerGroup != $this->allGroupsValue) { + $this->retrieveGroupValue($customerGroup); + } + } + + /** + * Retrieve customer group id by code. + * + * @param string $code + * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function retrieveGroupValue($code) + { + if (!isset($this->customerGroupsByCode[$code])) { + $searchCriteria = $this->searchCriteriaBuilder->addFilters( + [ + $this->filterBuilder->setField('customer_group_code')->setValue($code)->create() + ] + ); + $items = $this->customerGroupRepository->getList($searchCriteria->create())->getItems(); + $item = array_shift($items); + + if (!$item) { + throw new \Magento\Framework\Exception\NoSuchEntityException( + __( + 'No such entity with %fieldName = %fieldValue.', + [ + 'fieldName' => 'Customer Group', + 'fieldValue' => $code + ] + ) + ); + } + + $this->customerGroupsByCode[strtolower($item->getCode())] = $item->getId(); + } + + return $this->customerGroupsByCode[$code]; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Type/Price.php b/app/code/Magento/Catalog/Model/Product/Type/Price.php index af15e049203f3f6aae27ef2c6ff071d49cdab8cd..13dfdfe6e5cdc457e751348991fbb806e02ccb12 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/Price.php +++ b/app/code/Magento/Catalog/Model/Product/Type/Price.php @@ -414,11 +414,12 @@ class Price $prices = []; foreach ($tierPrices as $price) { $extensionAttributes = $price->getExtensionAttributes(); - $websiteId = $extensionAttributes && $extensionAttributes->getWebsiteId() - ? $extensionAttributes->getWebsiteId() - : $websiteId; + $priceWebsiteId = $websiteId; + if (isset($extensionAttributes) && is_numeric($extensionAttributes->getWebsiteId())) { + $priceWebsiteId = (string)$extensionAttributes->getWebsiteId(); + } $prices[] = [ - 'website_id' => $websiteId, + 'website_id' => $priceWebsiteId, 'cust_group' => $price->getCustomerGroupId(), 'website_price' => $price->getValue(), 'price' => $price->getValue(), diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php new file mode 100644 index 0000000000000000000000000000000000000000..1678ecd23c9262229ab063c2ad5af65abad9b49d --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Model; + +/** + * Product ID locator provides all product IDs by SKUs. + * @api + */ +class ProductIdLocator implements \Magento\Catalog\Model\ProductIdLocatorInterface +{ + /** + * Search Criteria builder. + * + * @var \Magento\Framework\Api\SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * Filter builder. + * + * @var \Magento\Framework\Api\FilterBuilder + */ + private $filterBuilder; + + /** + * Metadata pool. + * + * @var \Magento\Framework\EntityManager\MetadataPool + */ + private $metadataPool; + + /** + * Product repository. + * + * @var \Magento\Catalog\Api\ProductRepositoryInterface + */ + private $productRepository; + + /** + * IDs by SKU cache. + * + * @var array + */ + private $idsBySku = []; + + /** + * ProductIdLocatorInterface constructor. + * + * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder + * @param \Magento\Framework\Api\FilterBuilder $filterBuilder + * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + */ + public function __construct( + \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, + \Magento\Framework\Api\FilterBuilder $filterBuilder, + \Magento\Framework\EntityManager\MetadataPool $metadataPool, + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + ) { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->filterBuilder = $filterBuilder; + $this->metadataPool = $metadataPool; + $this->productRepository = $productRepository; + } + + /** + * {@inheritdoc} + */ + public function retrieveProductIdsBySkus(array $skus) + { + $skus = array_map('trim', $skus); + $skusInCache = $this->idsBySku ? array_keys($this->idsBySku) : []; + $neededSkus = array_diff($skus, $skusInCache); + + if (!empty($neededSkus)) { + $searchCriteria = $this->searchCriteriaBuilder->addFilters( + [ + $this->filterBuilder + ->setField(\Magento\Catalog\Api\Data\ProductInterface::SKU) + ->setConditionType('in') + ->setValue($neededSkus) + ->create(), + ] + ); + $items = $this->productRepository->getList($searchCriteria->create())->getItems(); + $linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) + ->getLinkField(); + + foreach ($items as $item) { + $this->idsBySku[$item->getSku()][$item->getData($linkField)] = $item->getTypeId(); + } + } + + return array_intersect_key($this->idsBySku, array_flip($skus)); + } +} diff --git a/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php b/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..f9a0d88df2eac54e741478d803891f9eb1214152 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model; + +/** + * Product ID locator provides all product IDs by SKU. + */ +interface ProductIdLocatorInterface +{ + /** + * Will return associative array of product ids as key and type as value grouped by SKUs. + * + * @param array $skus + * @return array + */ + public function retrieveProductIdsBySkus(array $skus); +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ef99e550cdc21ad00f9a726d149528af24ce59c5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php @@ -0,0 +1,326 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Price; + +/** + * Class BasePriceStorageTest. + */ +class BasePriceStorageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Catalog\Model\Product\Price\PricePersistenceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $pricePersistenceFactory; + + /** + * @var \Magento\Catalog\Model\Product\Price\PricePersistence|\PHPUnit_Framework_MockObject_MockObject + */ + private $pricePersistence; + + /** + * @var \Magento\Catalog\Api\Data\BasePriceInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $basePriceInterfaceFactory; + + /** + * @var \Magento\Catalog\Api\Data\BasePriceInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $basePriceInterface; + + /** + * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productIdLocator; + + /** + * @var \Magento\Store\Api\StoreRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeRepository; + + /** + * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productRepository; + + /** + * @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $product; + + /** + * @var \Magento\Catalog\Model\Product\Price\BasePriceStorage + */ + private $model; + + /** + * Set up. + * + * @return void + */ + protected function setUp() + { + $this->pricePersistenceFactory = $this->getMock( + \Magento\Catalog\Model\Product\Price\PricePersistenceFactory::class, + ['create'], + [], + '', + false + ); + $this->pricePersistence = $this->getMock( + \Magento\Catalog\Model\Product\Price\PricePersistence::class, + ['get', 'retrieveSkuById', 'update', 'getEntityLinkField'], + [], + '', + false + ); + $this->basePriceInterfaceFactory = $this->getMock( + \Magento\Catalog\Api\Data\BasePriceInterfaceFactory::class, + ['create'], + [], + '', + false + ); + $this->basePriceInterface = $this->getMockForAbstractClass( + \Magento\Catalog\Api\Data\BasePriceInterface::class, + [], + '', + false, + true, + true, + ['setSku', 'setPrice', 'setStoreId', 'getSku', 'getPrice', 'getStoreId'] + ); + $this->productIdLocator = $this->getMockForAbstractClass( + \Magento\Catalog\Model\ProductIdLocatorInterface::class, + [], + '', + false, + true, + true, + ['retrieveProductIdsBySkus'] + ); + $this->storeRepository = $this->getMockForAbstractClass( + \Magento\Store\Api\StoreRepositoryInterface::class, + [], + '', + false, + true, + true, + ['getById'] + ); + $this->productRepository = $this->getMockForAbstractClass( + \Magento\Catalog\Api\ProductRepositoryInterface::class, + [], + '', + false, + true, + true, + ['get'] + ); + $this->product = $this->getMockForAbstractClass( + \Magento\Catalog\Api\Data\ProductInterface::class, + [], + '', + false, + true, + true, + ['getPriceType'] + ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\BasePriceStorage::class, + [ + 'pricePersistenceFactory' => $this->pricePersistenceFactory, + 'basePriceInterfaceFactory' => $this->basePriceInterfaceFactory, + 'productIdLocator' => $this->productIdLocator, + 'storeRepository' => $this->storeRepository, + 'productRepository' => $this->productRepository, + 'allowedProductTypes' => ['simple', 'virtual', 'bundle', 'downloadable'], + ] + ); + } + + /** + * Test get method. + * + * @return void + */ + public function testGet() + { + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE + ] + ]; + $rawPrices = [ + [ + 'row_id' => 1, + 'value' => 15, + 'store_id' => 1 + ], + [ + 'row_id' => 2, + 'value' => 35, + 'store_id' => 1 + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->productRepository->expects($this->once())->method('get')->willReturn($this->product); + $this->product->expects($this->once())->method('getPriceType')->willReturn(1); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'price']) + ->willReturn($this->pricePersistence); + $this->pricePersistence->expects($this->once())->method('get')->with($skus)->willReturn($rawPrices); + $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id'); + $this->basePriceInterfaceFactory + ->expects($this->exactly(2)) + ->method('create') + ->willReturn($this->basePriceInterface); + $this->pricePersistence + ->expects($this->exactly(2)) + ->method('retrieveSkuById') + ->willReturnOnConsecutiveCalls('sku_1', 'sku_2'); + $this->basePriceInterface + ->expects($this->exactly(2)) + ->method('setSku') + ->withConsecutive(['sku_1'], ['sku_2']) + ->willReturnSelf(); + $this->basePriceInterface + ->expects($this->exactly(2)) + ->method('setPrice') + ->withConsecutive([15], [35]) + ->willReturnSelf(); + $this->basePriceInterface + ->expects($this->exactly(2)) + ->method('setStoreId') + ->withConsecutive([1], [1]) + ->willReturnSelf(); + $this->model->get($skus); + } + + /** + * Test get method with exception. + * + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Requested products don't exist: sku_1, sku_2 + */ + public function testGetWithException() + { + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => 'configurable' + ], + 'sku_2' => + [ + 2 => 'grouped' + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->model->get($skus); + } + + /** + * Test update method. + * + * @return void + */ + public function testUpdate() + { + $store = $this->getMockForAbstractClass( + \Magento\Store\Api\Data\StoreInterface::class, + [], + '', + false + ); + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE + ] + ]; + $this->basePriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku); + $this->productIdLocator + ->expects($this->exactly(2)) + ->method('retrieveProductIdsBySkus')->with([$sku]) + ->willReturn($idsBySku); + $this->productRepository->expects($this->once())->method('get')->willReturn($this->product); + $this->product->expects($this->once())->method('getPriceType')->willReturn(1); + $this->basePriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn(15); + $this->basePriceInterface->expects($this->exactly(2))->method('getStoreId')->willReturn(1); + $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id'); + $this->storeRepository->expects($this->once())->method('getById')->with(1)->willReturn($store); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'price']) + ->willReturn($this->pricePersistence); + $formattedPrices = [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15 + ] + ]; + $this->pricePersistence->expects($this->once())->method('update')->with($formattedPrices); + $this->assertTrue($this->model->update([$this->basePriceInterface])); + } + + /** + * Test update method without SKU. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute sku: . + */ + public function testUpdateWithoutSku() + { + $this->basePriceInterface->expects($this->exactly(2))->method('getSku')->willReturn(null); + $this->model->update([$this->basePriceInterface]); + } + + /** + * Test update method with negative price. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute Price: -15. + */ + public function testUpdateWithNegativePrice() + { + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE + ] + ]; + $this->basePriceInterface->expects($this->exactly(2))->method('getSku')->willReturn($sku); + $this->productIdLocator + ->expects($this->once(1)) + ->method('retrieveProductIdsBySkus')->with([$sku]) + ->willReturn($idsBySku); + $this->productRepository->expects($this->once())->method('get')->willReturn($this->product); + $this->product->expects($this->once())->method('getPriceType')->willReturn(1); + $this->basePriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn(-15); + $this->model->update([$this->basePriceInterface]); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b1dea66928f1bd98788758420167bde6dcca2329 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php @@ -0,0 +1,322 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Price; + +/** + * Class CostStorageTest. + */ +class CostStorageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Catalog\Model\Product\Price\PricePersistenceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $pricePersistenceFactory; + + /** + * @var \Magento\Catalog\Model\Product\Price\PricePersistence|\PHPUnit_Framework_MockObject_MockObject + */ + private $pricePersistence; + + /** + * @var \Magento\Catalog\Api\Data\CostInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $costInterfaceFactory; + + /** + * @var \Magento\Catalog\Api\Data\CostInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $costInterface; + + /** + * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productIdLocator; + + /** + * @var \Magento\Store\Api\StoreRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeRepository; + + /** + * @var \Magento\Catalog\Model\Product\Price\CostStorage + */ + private $model; + + /** + * Set up. + * + * @return void + */ + protected function setUp() + { + $this->pricePersistenceFactory = $this->getMock( + \Magento\Catalog\Model\Product\Price\PricePersistenceFactory::class, + ['create'], + [], + '', + false + ); + $this->pricePersistence = $this->getMock( + \Magento\Catalog\Model\Product\Price\PricePersistence::class, + ['get', 'retrieveSkuById', 'update', 'delete', 'getEntityLinkField'], + [], + '', + false + ); + $this->costInterfaceFactory = $this->getMock( + \Magento\Catalog\Api\Data\CostInterfaceFactory::class, + ['create'], + [], + '', + false + ); + $this->costInterface = $this->getMockForAbstractClass( + \Magento\Catalog\Api\Data\CostInterface::class, + [], + '', + false, + true, + true, + ['setSku', 'setCost', 'setStoreId', 'getSku', 'getCost', 'getStoreId'] + ); + $this->productIdLocator = $this->getMockForAbstractClass( + \Magento\Catalog\Model\ProductIdLocatorInterface::class, + [], + '', + false, + true, + true, + ['retrieveProductIdsBySkus'] + ); + $this->storeRepository = $this->getMockForAbstractClass( + \Magento\Store\Api\StoreRepositoryInterface::class, + [], + '', + false, + true, + true, + ['getById'] + ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\CostStorage::class, + [ + 'pricePersistenceFactory' => $this->pricePersistenceFactory, + 'costInterfaceFactory' => $this->costInterfaceFactory, + 'productIdLocator' => $this->productIdLocator, + 'storeRepository' => $this->storeRepository, + 'allowedProductTypes' => ['simple', 'virtual', 'downloadable'], + ] + ); + } + + /** + * Test get method. + * + * @return void + */ + public function testGet() + { + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $rawPrices = [ + [ + 'row_id' => 1, + 'value' => 15, + 'store_id' => 1 + ], + [ + 'row_id' => 2, + 'value' => 35, + 'store_id' => 1 + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'cost']) + ->willReturn($this->pricePersistence); + $this->pricePersistence->expects($this->once())->method('get')->with($skus)->willReturn($rawPrices); + $this->costInterfaceFactory + ->expects($this->exactly(2)) + ->method('create') + ->willReturn($this->costInterface); + $this->pricePersistence + ->expects($this->exactly(2)) + ->method('retrieveSkuById') + ->willReturnOnConsecutiveCalls('sku_1', 'sku_2'); + $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id'); + $this->costInterface + ->expects($this->exactly(2)) + ->method('setSku') + ->withConsecutive(['sku_1'], ['sku_2']) + ->willReturnSelf(); + $this->costInterface + ->expects($this->exactly(2)) + ->method('setCost') + ->withConsecutive([15], [35]) + ->willReturnSelf(); + $this->costInterface + ->expects($this->exactly(2)) + ->method('setStoreId') + ->withConsecutive([1], [1]) + ->willReturnSelf(); + $this->model->get($skus); + } + + /** + * Test get method with exception. + * + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Requested products don't exist: sku_1, sku_2 + */ + public function testGetWithException() + { + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => 'configurable' + ], + 'sku_2' => + [ + 2 => 'grouped' + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->model->get($skus); + } + + /** + * Test update method. + * + * @return void + */ + public function testUpdate() + { + $store = $this->getMockForAbstractClass( + \Magento\Store\Api\Data\StoreInterface::class, + [], + '', + false + ); + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $this->costInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku); + $this->productIdLocator + ->expects($this->exactly(2)) + ->method('retrieveProductIdsBySkus')->with([$sku]) + ->willReturn($idsBySku); + $this->costInterface->expects($this->exactly(3))->method('getCost')->willReturn(15); + $this->costInterface->expects($this->exactly(2))->method('getStoreId')->willReturn(1); + $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id'); + $this->storeRepository->expects($this->once())->method('getById')->with(1)->willReturn($store); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'cost']) + ->willReturn($this->pricePersistence); + $formattedPrices = [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15 + ] + ]; + $this->pricePersistence->expects($this->once())->method('update')->with($formattedPrices); + $this->assertTrue($this->model->update([$this->costInterface])); + } + + /** + * Test update method without SKU. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute sku: . + */ + public function testUpdateWithoutSku() + { + $this->costInterface->expects($this->exactly(2))->method('getSku')->willReturn(null); + $this->model->update([$this->costInterface]); + } + + /** + * Test update method with negative cost. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute Cost: -15. + */ + public function testUpdateWithNegativeCost() + { + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $this->costInterface->expects($this->exactly(2))->method('getSku')->willReturn($sku); + $this->productIdLocator + ->expects($this->once(1)) + ->method('retrieveProductIdsBySkus')->with([$sku]) + ->willReturn($idsBySku); + $this->costInterface->expects($this->exactly(3))->method('getCost')->willReturn(-15); + $this->model->update([$this->costInterface]); + } + + /** + * Test delete method. + * + * @return void + */ + public function testDelete() + { + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->pricePersistenceFactory + ->expects($this->once()) + ->method('create') + ->with(['attributeCode' => 'cost']) + ->willReturn($this->pricePersistence); + $this->pricePersistence->expects($this->once())->method('delete')->with($skus); + $this->model->delete($skus); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e9f422245d95967905b577c7cc39626007b16d49 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php @@ -0,0 +1,402 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Price; + +/** + * Class PricePersistenceTest. + */ +class PricePersistenceTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Catalog\Model\ResourceModel\Attribute|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeResource; + + /** + * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeRepository; + + /** + * @var \Magento\Catalog\Api\Data\ProductAttributeInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productAttribute; + + /** + * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productIdLocator; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $connection; + + /** + * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataPool; + + /** + * @var \Magento\Catalog\Model\Product\Price\PricePersistence + */ + private $model; + + /** + * Set up. + * + * @return void + */ + protected function setUp() + { + $this->attributeResource = $this->getMock( + \Magento\Catalog\Model\ResourceModel\Attribute::class, + ['getConnection', 'getTable'], + [], + '', + false + ); + $this->attributeRepository = $this->getMockForAbstractClass( + \Magento\Catalog\Api\ProductAttributeRepositoryInterface::class, + [], + '', + false, + true, + true, + ['get'] + ); + $this->productIdLocator = $this->getMockForAbstractClass( + \Magento\Catalog\Model\ProductIdLocatorInterface::class, + [], + '', + false, + true, + true, + ['retrieveProductIdsBySkus'] + ); + $this->metadataPool = $this->getMock( + \Magento\Framework\EntityManager\MetadataPool::class, + ['getLinkField', 'getMetadata'], + [], + '', + false + ); + $this->connection = $this->getMockForAbstractClass( + \Magento\Framework\DB\Adapter\AdapterInterface::class, + [], + '', + false, + true, + true, + ['select', 'fetchAll', 'beginTransaction', 'insertOnDuplicate', 'commit', 'rollBack', 'delete'] + ); + $this->productAttribute = $this->getMockForAbstractClass( + \Magento\Catalog\Api\Data\ProductAttributeInterface::class, + [], + '', + false, + true, + true, + ['getAttributeId'] + ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\PricePersistence::class, + [ + 'attributeResource' => $this->attributeResource, + 'attributeRepository' => $this->attributeRepository, + 'productIdLocator' => $this->productIdLocator, + 'metadataPool' => $this->metadataPool, + ] + ); + } + + /** + * Test get method. + * + * @return void + */ + public function testGet() + { + $attributeId = 5; + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $select = $this->getMock( + \Magento\Framework\DB\Select::class, + ['from', 'where'], + [], + '', + false + ); + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('select')->willReturn($select); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $select->expects($this->once())->method('from')->with('catalog_product_entity_decimal')->willReturnSelf(); + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $select + ->expects($this->exactly(2)) + ->method('where') + ->withConsecutive(['row_id IN (?)', [1, 2]], ['attribute_id = ?', $attributeId]) + ->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id'); + $this->model->get($skus); + } + + /** + * Test update method. + * + * @return void + */ + public function testUpdate() + { + $attributeId = 5; + $prices = [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15 + ] + ]; + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf(); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $this->connection + ->expects($this->once()) + ->method('insertOnDuplicate') + ->with( + 'catalog_product_entity_decimal', + [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15, + 'attribute_id' => 5, + ] + ], + ['value'] + ) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('commit')->willReturnSelf(); + $this->model->update($prices); + } + + /** + * Test update method throws exception. + * + * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @expectedExceptionMessage Could not save Prices. + */ + public function testUpdateWithException() + { + $attributeId = 5; + $prices = [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15 + ] + ]; + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf(); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $this->connection + ->expects($this->once()) + ->method('insertOnDuplicate') + ->with( + 'catalog_product_entity_decimal', + [ + [ + 'store_id' => 1, + 'row_id' => 1, + 'value' => 15, + 'attribute_id' => 5, + ] + ], + ['value'] + ) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('commit')->willThrowException(new \Exception()); + $this->connection->expects($this->once())->method('rollback')->willReturnSelf(); + $this->model->update($prices); + } + + /** + * Test delete method. + * + * @return void + */ + public function testDelete() + { + $attributeId = 5; + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf(); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $this->connection + ->expects($this->once()) + ->method('delete') + ->with( + 'catalog_product_entity_decimal', + [ + 'attribute_id = ?' => $attributeId, + 'row_id IN (?)' => [1, 2] + ] + ) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('commit')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id'); + $this->model->delete($skus); + } + + /** + * Test delete method throws exception. + * + * @expectedException \Magento\Framework\Exception\CouldNotDeleteException + * @expectedExceptionMessage Could not delete Prices + */ + public function testDeleteWithException() + { + $attributeId = 5; + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => + [ + 1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'sku_2' => + [ + 2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL + ] + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus')->with($skus) + ->willReturn($idsBySku); + $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute); + $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection); + $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf(); + $this->attributeResource + ->expects($this->once()) + ->method('getTable') + ->with('catalog_product_entity_decimal') + ->willReturn('catalog_product_entity_decimal'); + $this->connection + ->expects($this->once()) + ->method('delete') + ->with( + 'catalog_product_entity_decimal', + [ + 'attribute_id = ?' => $attributeId, + 'row_id IN (?)' => [1, 2] + ] + ) + ->willReturnSelf(); + $this->connection->expects($this->once())->method('commit')->willThrowException(new \Exception()); + $this->connection->expects($this->once())->method('rollBack')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf(); + $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id'); + $this->model->delete($skus); + } + + /** + * Test retrieveSkuById method. + * + * @param int|null $expectedResult + * @param int $id + * @param array $skus + * @dataProvider dataProviderRetrieveSkuById + */ + public function testRetrieveSkuById($expectedResult, $id, array $skus) + { + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus') + ->willReturn($skus); + + $this->assertEquals($expectedResult, $this->model->retrieveSkuById($id, $skus)); + } + + /** + * Data provider for retrieveSkuById method. + * + * @return array + */ + public function dataProviderRetrieveSkuById() + { + return [ + [ + null, + 2, + ['sku_1' => [1 => 1]] + ], + [ + 'sku_1', + 1, + ['sku_1' => [1 => 1]] + ], + [ + null, + 1, + ['sku_1' => [2 => 1]] + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2a885dd8b83698bffcb0106992bee076692bffca --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php @@ -0,0 +1,292 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Price; + +/** + * TierPriceStorage test. + */ +class TierPriceStorageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Catalog\Model\Product\Price\TierPricePersistence|\PHPUnit_Framework_MockObject_MockObject + */ + private $tierPricePersistence; + + /** + * @var \Magento\Catalog\Model\Product\Price\TierPriceValidator|\PHPUnit_Framework_MockObject_MockObject + */ + private $tierPriceValidator; + + /** + * @var \Magento\Catalog\Model\Product\Price\TierPriceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $tierPriceFactory; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price|\PHPUnit_Framework_MockObject_MockObject + */ + private $priceIndexer; + + /** + * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productIdLocator; + + /** + * @var \Magento\PageCache\Model\Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $config; + + /** + * @var \Magento\Framework\App\Cache\TypeListInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $typeList; + + /** + * @var \Magento\Catalog\Model\Product\Price\TierPriceStorage + */ + private $tierPriceStorage; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->tierPricePersistence = $this->getMock( + \Magento\Catalog\Model\Product\Price\TierPricePersistence::class, + [], + [], + '', + false + ); + $this->tierPricePersistence->expects($this->any()) + ->method('getEntityLinkField') + ->willReturn('row_id'); + $this->tierPriceValidator = $this->getMock( + \Magento\Catalog\Model\Product\Price\TierPriceValidator::class, + [], + [], + '', + false + ); + $this->tierPriceFactory = $this->getMock( + \Magento\Catalog\Model\Product\Price\TierPriceFactory::class, + [], + [], + '', + false + ); + $this->priceIndexer = $this->getMock( + \Magento\Catalog\Model\Indexer\Product\Price::class, + [], + [], + '', + false + ); + $this->productIdLocator = $this->getMock( + \Magento\Catalog\Model\ProductIdLocatorInterface::class, + [], + [], + '', + false + ); + $this->config = $this->getMock( + \Magento\PageCache\Model\Config::class, + [], + [], + '', + false + ); + $this->typeList = $this->getMock( + \Magento\Framework\App\Cache\TypeListInterface::class, + [], + [], + '', + false + ); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->tierPriceStorage = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\TierPriceStorage::class, + [ + 'tierPricePersistence' => $this->tierPricePersistence, + 'tierPriceValidator' => $this->tierPriceValidator, + 'tierPriceFactory' => $this->tierPriceFactory, + 'priceIndexer' => $this->priceIndexer, + 'productIdLocator' => $this->productIdLocator, + 'config' => $this->config, + 'typeList' => $this->typeList, + ] + ); + } + + /** + * Test get method. + * @return void + */ + public function testGet() + { + $skus = ['simple', 'virtual']; + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->with(['simple', 'virtual']) + ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]); + $this->tierPricePersistence->expects($this->once()) + ->method('get') + ->willReturn( + [ + [ + 'value_id' => 1, + 'row_id' => 2, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 2.0000, + 'value' => 2.0000, + 'percentage_value' => null, + 'website_id' => 0 + ], + [ + 'value_id' => 2, + 'row_id' => 3, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 3.0000, + 'value' => 3.0000, + 'percentage_value' => null, + 'website_id' => 0 + ] + ] + ); + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $this->tierPriceFactory->expects($this->at(0))->method('create')->willReturn($price); + $this->tierPriceFactory->expects($this->at(1))->method('create')->willReturn($price); + $prices = $this->tierPriceStorage->get($skus); + $this->assertNotEmpty($prices); + $this->assertEquals(2, count($prices)); + } + + /** + * Test update method. + * @return void + */ + public function testUpdate() + { + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->willReturn(['bundle' => ['2' => 'bundle']]); + $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices')->willReturn(true); + $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( + [ + 'row_id' => 2, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 2, + 'value' => 3, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); + $this->tierPricePersistence->expects($this->once()) + ->method('get') + ->willReturn( + [ + [ + 'value_id' => 1, + 'row_id' => 2, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 2.0000, + 'value' => 2.0000, + 'percentage_value' => null, + 'website_id' => 0 + ] + ] + ); + $this->tierPricePersistence->expects($this->atLeastOnce())->method('update'); + $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); + $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); + $this->typeList->expects($this->atLeastOnce())->method('invalidate'); + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $price->method('getSku')->willReturn('bundle'); + $this->assertTrue($this->tierPriceStorage->update([$price])); + } + + /** + * Test replace method. + * @return void + */ + public function testReplace() + { + $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices'); + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->willReturn(['virtual' => ['2' => 'virtual']]); + $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( + [ + 'row_id' => 3, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 3, + 'value' => 7, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); + $this->tierPricePersistence->expects($this->atLeastOnce())->method('replace'); + $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $price->method('getSku')->willReturn('virtual'); + $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); + $this->typeList->expects($this->atLeastOnce())->method('invalidate'); + $this->assertTrue($this->tierPriceStorage->replace([$price])); + } + + /** + * Test delete method. + * @return void + */ + public function testDelete() + { + $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices'); + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->willReturn(['simple' => ['2' => 'simple']]); + $this->tierPricePersistence->expects($this->once()) + ->method('get') + ->willReturn( + [ + [ + 'value_id' => 7, + 'row_id' => 7, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 5.0000, + 'value' => 6.0000, + 'percentage_value' => null, + 'website_id' => 0 + ] + ] + ); + $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn( + [ + 'row_id' => 3, + 'all_groups' => 1, + 'customer_group_id' => 0, + 'qty' => 3, + 'value' => 7, + 'percentage_value' => null, + 'website_id' => 0 + ] + ); + $this->tierPricePersistence->expects($this->atLeastOnce())->method('delete'); + $this->priceIndexer->expects($this->atLeastOnce())->method('execute'); + $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true); + $this->typeList->expects($this->atLeastOnce())->method('invalidate'); + $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass(); + $price->method('getSku')->willReturn('simple'); + $this->assertTrue($this->tierPriceStorage->delete([$price])); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceValidatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1f44c2a75d1862ded128a2c35622dc148e3403a2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceValidatorTest.php @@ -0,0 +1,471 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Price; + +use Magento\Catalog\Api\Data\TierPriceInterface; + +/** + * Class TierPriceValidatorTest. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class TierPriceValidatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productIdLocator; + + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $searchCriteriaBuilder; + + /** + * @var \Magento\Framework\Api\FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $filterBuilder; + + /** + * @var \Magento\Customer\Api\GroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerGroupRepository; + + /** + * @var \Magento\Store\Api\WebsiteRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $websiteRepository; + + /** + * @var \Magento\Catalog\Model\Product\Price\TierPricePersistence|\PHPUnit_Framework_MockObject_MockObject + */ + private $tierPricePersistence; + + /** + * @var \Magento\Catalog\Api\Data\TierPriceInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $tierPriceInterface; + + /** + * @var \Magento\Catalog\Model\Product\Price\TierPriceValidator + */ + private $model; + + /** + * Set up. + * + * @return void + */ + protected function setUp() + { + $this->productIdLocator = $this->getMockForAbstractClass( + \Magento\Catalog\Model\ProductIdLocatorInterface::class, + [], + '', + false, + true, + true, + ['retrieveProductIdsBySkus'] + ); + $this->searchCriteriaBuilder = $this->getMock( + \Magento\Framework\Api\SearchCriteriaBuilder::class, + ['addFilters', 'create'], + [], + '', + false + ); + $this->filterBuilder = $this->getMock( + \Magento\Framework\Api\FilterBuilder::class, + ['setField', 'setValue', 'create'], + [], + '', + false + ); + $this->customerGroupRepository = $this->getMockForAbstractClass( + \Magento\Customer\Api\GroupRepositoryInterface::class, + [], + '', + false, + true, + true, + ['getList'] + ); + $this->websiteRepository = $this->getMockForAbstractClass( + \Magento\Store\Api\WebsiteRepositoryInterface::class, + [], + '', + false, + true, + true, + ['getById'] + ); + $this->tierPricePersistence = $this->getMock( + \Magento\Catalog\Model\Product\Price\TierPricePersistence::class, + ['addFilters', 'create'], + [], + '', + false + ); + $this->tierPriceInterface = $this->getMockForAbstractClass( + \Magento\Catalog\Api\Data\TierPriceInterface::class, + [], + '', + false, + true, + true, + ['getSku', 'getPrice', 'getPriceType', 'getQuantity', 'getWebsiteId', 'getCustomerGroup'] + ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Price\TierPriceValidator::class, + [ + 'productIdLocator' => $this->productIdLocator, + 'searchCriteriaBuilder' => $this->searchCriteriaBuilder, + 'filterBuilder' => $this->filterBuilder, + 'customerGroupRepository' => $this->customerGroupRepository, + 'websiteRepository' => $this->websiteRepository, + 'tierPricePersistence' => $this->tierPricePersistence, + 'allowedProductTypes' => ['simple', 'virtual', 'bundle', 'downloadable'], + ] + ); + } + + /** + * Test validateSkus method. + * + * @return void + */ + public function testValidateSkus() + { + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE], + 'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus') + ->with($skus) + ->willReturn($idsBySku); + $this->model->validateSkus($skus); + } + + /** + * Test validateSkus method throws exception. + * + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Requested products don't exist: sku_1, sku_2 + */ + public function testValidateSkusWithException() + { + $skus = ['sku_1', 'sku_2']; + $idsBySku = [ + 'sku_1' => [1 => 'grouped'], + 'sku_2' => [2 => 'configurable'], + ]; + $this->productIdLocator + ->expects($this->once()) + ->method('retrieveProductIdsBySkus') + ->with($skus) + ->willReturn($idsBySku); + $this->model->validateSkus($skus); + } + + /** + * Test validatePrices method. + * + * @return void + */ + public function testValidatePrices() + { + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE], + 'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], + ]; + $productPrice = 15; + $this->tierPriceInterface->expects($this->exactly(8))->method('getSku')->willReturn($sku); + $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku); + $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice); + $this->tierPriceInterface + ->expects($this->exactly(2)) + ->method('getPriceType') + ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED); + $this->tierPriceInterface->expects($this->exactly(3))->method('getQuantity')->willReturn(2); + $this->checkWebsite($this->tierPriceInterface); + $this->checkGroup($this->tierPriceInterface); + $this->model->validatePrices([$this->tierPriceInterface], []); + } + + /** + * Test validatePrices method with downloadable product. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute sku: . + */ + public function testValidatePricesWithDownloadableProduct() + { + $this->tierPriceInterface->expects($this->exactly(2))->method('getSku')->willReturn(null); + $this->model->validatePrices([$this->tierPriceInterface], []); + } + + /** + * Test validatePrices method with negative price. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute Price: -15. + */ + public function testValidatePricesWithNegativePrice() + { + $negativePrice = -15; + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE], + 'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], + ]; + $this->tierPriceInterface->expects($this->exactly(3))->method('getSku')->willReturn($sku); + $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku); + $this->tierPriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn($negativePrice); + $this->model->validatePrices([$this->tierPriceInterface], []); + } + + /** + * Test validatePrices method with bundle product and fixed price. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute Price Type: fixed. + */ + public function testValidatePricesWithBundleProductAndFixedPrice() + { + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE], + ]; + $productPrice = 15; + $this->tierPriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku); + $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku); + $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice); + $this->tierPriceInterface + ->expects($this->exactly(4)) + ->method('getPriceType') + ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED); + $this->model->validatePrices([$this->tierPriceInterface], []); + } + + /** + * Test validatePrices method with zero quantity. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute Quantity: 0. + */ + public function testValidatePricesWithZeroQty() + { + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], + ]; + $productPrice = 15; + $this->tierPriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku); + $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku); + $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice); + $this->tierPriceInterface + ->expects($this->exactly(2)) + ->method('getPriceType') + ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED); + $this->tierPriceInterface->expects($this->exactly(2))->method('getQuantity')->willReturn(0); + $this->model->validatePrices([$this->tierPriceInterface], []); + } + + /** + * Test validatePrices method without website. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid attribute website_id: 15. + */ + public function testValidatePricesWithoutWebsite() + { + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], + ]; + $productPrice = 15; + $exception = new \Magento\Framework\Exception\NoSuchEntityException(); + $this->tierPriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku); + $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku); + $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice); + $this->tierPriceInterface + ->expects($this->exactly(2)) + ->method('getPriceType') + ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED); + $this->tierPriceInterface->expects($this->once())->method('getQuantity')->willReturn(2); + $this->websiteRepository->expects($this->once())->method('getById')->willThrowException($exception); + $this->tierPriceInterface->expects($this->exactly(2))->method('getWebsiteId')->willReturn(15); + $this->model->validatePrices([$this->tierPriceInterface], []); + } + + /** + * Test validatePrices method not unique. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage We found a duplicate website, tier price, customer + * group and quantity: Customer Group = retailer, Website Id = 2, Quantity = 2. + */ + public function testValidatePricesNotUnique() + { + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], + ]; + $productPrice = 15; + $this->tierPriceInterface->expects($this->exactly(8))->method('getSku')->willReturn($sku); + $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku); + $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice); + $this->tierPriceInterface + ->expects($this->exactly(2)) + ->method('getPriceType') + ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED); + $website = $this->getMockForAbstractClass( + \Magento\Store\Api\Data\WebsiteInterface::class, + [], + '', + false + ); + $this->tierPriceInterface + ->expects($this->exactly(5)) + ->method('getWebsiteId') + ->willReturnOnConsecutiveCalls(1, 0, 0, 1, 2); + $this->websiteRepository->expects($this->once())->method('getById')->willReturn($website); + $this->tierPriceInterface->expects($this->exactly(4))->method('getQuantity')->willReturn(2); + $this->tierPriceInterface->expects($this->exactly(3))->method('getCustomerGroup')->willReturn('retailer'); + $this->model->validatePrices([$this->tierPriceInterface], []); + } + + /** + * Test validatePrices method without group. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage No such entity with Customer Group = wholesale. + */ + public function testValidatePricesWithoutGroup() + { + $sku = 'sku_1'; + $idsBySku = [ + 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], + ]; + $productPrice = 15; + $this->tierPriceInterface->expects($this->exactly(8))->method('getSku')->willReturn($sku); + $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku); + $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice); + $this->tierPriceInterface + ->expects($this->exactly(2)) + ->method('getPriceType') + ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED); + $this->tierPriceInterface->expects($this->exactly(3))->method('getQuantity')->willReturn(2); + $this->checkWebsite($this->tierPriceInterface); + $searchCriteria = $this->getMock( + \Magento\Framework\Api\SearchCriteria::class, + [], + [], + '', + false + ); + $searchResults = $this->getMockForAbstractClass( + \Magento\Customer\Api\Data\GroupSearchResultsInterface::class, + [], + '', + false, + true, + true, + ['getItems'] + ); + $this->tierPriceInterface->expects($this->exactly(3))->method('getCustomerGroup')->willReturn('wholesale'); + $this->searchCriteriaBuilder->expects($this->once())->method('addFilters')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('setField')->with('customer_group_code')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('setValue')->with('wholesale')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('create')->willReturnSelf(); + $this->searchCriteriaBuilder + ->expects($this->once()) + ->method('create') + ->willReturn($searchCriteria); + $this->customerGroupRepository + ->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($searchResults); + $searchResults->expects($this->once())->method('getItems')->willReturn([]); + $this->model->validatePrices([$this->tierPriceInterface], []); + } + + /** + * Check website. + * + * @param \PHPUnit_Framework_MockObject_MockObject $price + */ + private function checkWebsite(\PHPUnit_Framework_MockObject_MockObject $price) + { + $website = $this->getMockForAbstractClass( + \Magento\Store\Api\Data\WebsiteInterface::class, + [], + '', + false + ); + $price->expects($this->exactly(3))->method('getWebsiteId')->willReturn(1); + $this->websiteRepository->expects($this->once())->method('getById')->willReturn($website); + } + + /** + * Check group. + * + * @param \PHPUnit_Framework_MockObject_MockObject $price + */ + private function checkGroup(\PHPUnit_Framework_MockObject_MockObject $price) + { + $searchCriteria = $this->getMock( + \Magento\Framework\Api\SearchCriteria::class, + [], + [], + '', + false + ); + $searchResults = $this->getMockForAbstractClass( + \Magento\Customer\Api\Data\GroupSearchResultsInterface::class, + [], + '', + false, + true, + true, + ['getItems'] + ); + $group = $this->getMockForAbstractClass( + \Magento\Customer\Api\Data\GroupInterface::class, + [], + '', + false, + true, + true, + ['getCode', 'getId'] + ); + + $price->expects($this->exactly(3))->method('getCustomerGroup')->willReturn('wholesale'); + $this->searchCriteriaBuilder->expects($this->once())->method('addFilters')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('setField')->with('customer_group_code')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('setValue')->with('wholesale')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('create')->willReturnSelf(); + $this->searchCriteriaBuilder + ->expects($this->once()) + ->method('create') + ->willReturn($searchCriteria); + $this->customerGroupRepository + ->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($searchResults); + $searchResults->expects($this->once())->method('getItems')->willReturn([$group]); + $group->expects($this->once())->method('getCode')->willReturn('wholesale'); + $group->expects($this->once())->method('getId')->willReturn(4); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..75f81195f02e420929edbea8580692c1125752b4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php @@ -0,0 +1,158 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model; + +/** + * Class ProductIdLocatorTest. + */ +class ProductIdLocatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $searchCriteriaBuilder; + + /** + * @var \Magento\Framework\Api\FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $filterBuilder; + + /** + * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataPool; + + /** + * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productRepository; + + /** + * @var \Magento\Catalog\Model\ProductIdLocator + */ + private $model; + + /** + * Set up. + * + * @return void + */ + protected function setUp() + { + $this->searchCriteriaBuilder = $this->getMock( + \Magento\Framework\Api\SearchCriteriaBuilder::class, + ['addFilters', 'create'], + [], + '', + false + ); + $this->filterBuilder = $this->getMock( + \Magento\Framework\Api\FilterBuilder::class, + ['setField', 'setConditionType', 'setValue', 'create'], + [], + '', + false + ); + $this->metadataPool = $this->getMock( + \Magento\Framework\EntityManager\MetadataPool::class, + ['getMetadata'], + [], + '', + false + ); + $this->productRepository = $this->getMockForAbstractClass( + \Magento\Catalog\Api\ProductRepositoryInterface::class, + [], + '', + false, + true, + true, + ['getList'] + ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\ProductIdLocator::class, + [ + 'searchCriteriaBuilder' => $this->searchCriteriaBuilder, + 'filterBuilder' => $this->filterBuilder, + 'metadataPool' => $this->metadataPool, + 'productRepository' => $this->productRepository, + ] + ); + } + + /** + * Test retrieve + */ + public function testRetrieveProductIdsBySkus() + { + $skus = ['sku_1', 'sku_2']; + $searchCriteria = $this->getMock( + \Magento\Framework\Api\SearchCriteria::class, + [], + [], + '', + false + ); + $searchResults = $this->getMockForAbstractClass( + \Magento\Catalog\Api\Data\ProductSearchResultsInterface::class, + [], + '', + false, + true, + true, + ['getItems'] + ); + $product = $this->getMockForAbstractClass( + \Magento\Catalog\Api\Data\ProductInterface::class, + [], + '', + false, + true, + true, + ['getSku', 'getData', 'getTypeId'] + ); + $metaDataInterface = $this->getMockForAbstractClass( + \Magento\Framework\EntityManager\EntityMetadataInterface::class, + [], + '', + false, + true, + true, + ['getLinkField'] + ); + $this->searchCriteriaBuilder->expects($this->once())->method('addFilters')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('setField')->with('sku')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('setConditionType')->with('in')->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('setValue')->with(['sku_1', 'sku_2'])->willReturnSelf(); + $this->filterBuilder->expects($this->once())->method('create')->willReturnSelf(); + $this->searchCriteriaBuilder + ->expects($this->once()) + ->method('create') + ->willReturn($searchCriteria); + $this->productRepository + ->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($searchResults); + $searchResults->expects($this->once())->method('getItems')->willReturn([$product]); + $this->metadataPool + ->expects($this->once()) + ->method('getMetadata') + ->with(\Magento\Catalog\Api\Data\ProductInterface::class) + ->willReturn($metaDataInterface); + $metaDataInterface->expects($this->once())->method('getLinkField')->willReturn('entity_id'); + $product->expects($this->once())->method('getSku')->willReturn('sku_1'); + $product->expects($this->once())->method('getData')->with('entity_id')->willReturn(1); + $product->expects($this->once())->method('getTypeId')->willReturn('simple'); + $this->assertEquals( + ['sku_1' => [1 => 'simple']], + $this->model->retrieveProductIdsBySkus($skus) + ); + } +} diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php index 53360de08b434b4a0314fe341760c55fef476ef4..326e92417670a77bc269625153f3a56f715bb7e5 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php @@ -407,6 +407,7 @@ class AdvancedPricing extends AbstractModifier 'data' => [ 'config' => [ 'componentType' => 'dynamicRows', + 'component' => 'Magento_Catalog/js/components/dynamic-rows-tier-price', 'label' => __('Customer Group Price'), 'renderDefaultRecord' => false, 'recordTemplate' => 'record', diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index 5fc5ec8d44fd0a88f8a5d542d5518fa60ad77629..d6ecaa7c40391de2b12abac33d25b9ecd5620af8 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -169,4 +169,9 @@ <argument name="scopeName" xsi:type="string">product_form.product_form</argument> </arguments> </type> + <type name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\TierPrice"> + <arguments> + <argument name="productPriceOptions" xsi:type="object">Magento\Catalog\Model\Config\Source\Product\Options\TierPrice</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 27b9a19065e99f22fccf4f2f42076c69d0f1e0c2..0142f7c2f261837ae1e0d8f25418486951da3043 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -51,6 +51,13 @@ <preference for="Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface" type="Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver"/> <preference for="Magento\Catalog\Model\Product\Media\ConfigInterface" type="Magento\Catalog\Model\Product\Media\Config"/> <preference for="Magento\Framework\View\Asset\ContextInterface" type="Magento\Catalog\Model\View\Asset\Image\Context"/> + <preference for="Magento\Catalog\Api\TierPriceStorageInterface" type="Magento\Catalog\Model\Product\Price\TierPriceStorage" /> + <preference for="Magento\Catalog\Api\Data\TierPriceInterface" type="Magento\Catalog\Model\Product\Price\TierPrice" /> + <preference for="Magento\Catalog\Api\BasePriceStorageInterface" type="Magento\Catalog\Model\Product\Price\BasePriceStorage" /> + <preference for="Magento\Catalog\Api\Data\BasePriceInterface" type="Magento\Catalog\Model\Product\Price\BasePrice" /> + <preference for="Magento\Catalog\Api\CostStorageInterface" type="Magento\Catalog\Model\Product\Price\CostStorage" /> + <preference for="Magento\Catalog\Api\Data\CostInterface" type="Magento\Catalog\Model\Product\Price\Cost" /> + <preference for="Magento\Catalog\Model\ProductIdLocatorInterface" type="Magento\Catalog\Model\ProductIdLocator" /> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> @@ -848,4 +855,30 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Price\CostStorage"> + <arguments> + <argument name="allowedProductTypes" xsi:type="array"> + <item name="0" xsi:type="string">simple</item> + <item name="1" xsi:type="string">virtual</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Price\BasePriceStorage"> + <arguments> + <argument name="allowedProductTypes" xsi:type="array"> + <item name="0" xsi:type="string">simple</item> + <item name="1" xsi:type="string">virtual</item> + <item name="2" xsi:type="string">bundle</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Price\TierPriceValidator"> + <arguments> + <argument name="allowedProductTypes" xsi:type="array"> + <item name="0" xsi:type="string">simple</item> + <item name="1" xsi:type="string">virtual</item> + <item name="2" xsi:type="string">bundle</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/webapi.xml b/app/code/Magento/Catalog/etc/webapi.xml index 99670c347a89babc45714d5ed0350676f5cf48fe..b53f11b1ff2959a583b1a401a7566ca7eee33589 100644 --- a/app/code/Magento/Catalog/etc/webapi.xml +++ b/app/code/Magento/Catalog/etc/webapi.xml @@ -246,6 +246,60 @@ <resource ref="Magento_Catalog::catalog"/> </resources> </route> + <route url="/V1/products/tier-prices-information" method="POST"> + <service class="Magento\Catalog\Api\TierPriceStorageInterface" method="get"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> + <route url="/V1/products/tier-prices" method="POST"> + <service class="Magento\Catalog\Api\TierPriceStorageInterface" method="update"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> + <route url="/V1/products/tier-prices" method="PUT"> + <service class="Magento\Catalog\Api\TierPriceStorageInterface" method="replace"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> + <route url="/V1/products/tier-prices-delete" method="POST"> + <service class="Magento\Catalog\Api\TierPriceStorageInterface" method="delete"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> + <route url="/V1/products/base-prices-information" method="POST"> + <service class="Magento\Catalog\Api\BasePriceStorageInterface" method="get"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> + <route url="/V1/products/base-prices" method="POST"> + <service class="Magento\Catalog\Api\BasePriceStorageInterface" method="update"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> + <route url="/V1/products/cost-information" method="POST"> + <service class="Magento\Catalog\Api\CostStorageInterface" method="get"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> + <route url="/V1/products/cost" method="POST"> + <service class="Magento\Catalog\Api\CostStorageInterface" method="update"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> + <route url="/V1/products/cost-delete" method="POST"> + <service class="Magento\Catalog\Api\CostStorageInterface" method="delete"/> + <resources> + <resource ref="Magento_Catalog::catalog"/> + </resources> + </route> <route url="/V1/categories/:categoryId" method="DELETE"> <service class="Magento\Catalog\Api\CategoryRepositoryInterface" method="deleteByIdentifier" /> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js new file mode 100644 index 0000000000000000000000000000000000000000..6dc4c747a44514be814ea4d939135070287052d8 --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js @@ -0,0 +1,29 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore', + 'Magento_Ui/js/dynamic-rows/dynamic-rows' +], function (_, DynamicRows) { + 'use strict'; + + return DynamicRows.extend({ + + /** + * Init header elements + */ + initHeader: function () { + var labels; + + this._super(); + labels = _.clone(this.labels()); + labels = _.sortBy(labels, function (label) { + return label.sortOrder; + }); + + this.labels(labels); + } + }); +}); diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php index 08e75ceebecc6160825bc12b0380a03b4a1af0ab..337134c9453c01dad20e4581e4d82019aa956b6d 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php @@ -6,11 +6,16 @@ namespace Magento\CatalogSearch\Model\Indexer; use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\FullFactory; +use Magento\CatalogSearch\Model\Indexer\Scope\State; use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource; -use \Magento\Framework\Search\Request\Config as SearchRequestConfig; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Search\Request\Config as SearchRequestConfig; use Magento\Framework\Search\Request\DimensionFactory; use Magento\Store\Model\StoreManagerInterface; +/** + * Provide functionality for Fulltext Search indexing + */ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface { /** @@ -18,34 +23,51 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F */ const INDEXER_ID = 'catalogsearch_fulltext'; - /** @var array index structure */ + /** + * @var array index structure + */ protected $data; /** * @var IndexerHandlerFactory */ private $indexerHandlerFactory; + /** * @var StoreManagerInterface */ private $storeManager; + /** - * @var DimensionFactory + * @var \Magento\Framework\Search\Request\DimensionFactory */ private $dimensionFactory; + /** - * @var Full + * @var \Magento\CatalogSearch\Model\Indexer\Fulltext\Action\Full */ private $fullAction; + /** * @var FulltextResource */ private $fulltextResource; + /** - * @var SearchRequestConfig + * @var \Magento\Framework\Search\Request\Config */ private $searchRequestConfig; + /** + * @var IndexSwitcherInterface + */ + private $indexSwitcher; + + /** + * @var \Magento\CatalogSearch\Model\Indexer\Scope\State + */ + private $indexScopeState; + /** * @param FullFactory $fullActionFactory * @param IndexerHandlerFactory $indexerHandlerFactory @@ -54,6 +76,8 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F * @param FulltextResource $fulltextResource * @param SearchRequestConfig $searchRequestConfig * @param array $data + * @param IndexSwitcherInterface $indexSwitcher + * @param Scope\State $indexScopeState */ public function __construct( FullFactory $fullActionFactory, @@ -62,7 +86,9 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F DimensionFactory $dimensionFactory, FulltextResource $fulltextResource, SearchRequestConfig $searchRequestConfig, - array $data + array $data, + IndexSwitcherInterface $indexSwitcher = null, + State $indexScopeState = null ) { $this->fullAction = $fullActionFactory->create(['data' => $data]); $this->indexerHandlerFactory = $indexerHandlerFactory; @@ -71,6 +97,14 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F $this->fulltextResource = $fulltextResource; $this->searchRequestConfig = $searchRequestConfig; $this->data = $data; + if (null === $indexSwitcher) { + $indexSwitcher = ObjectManager::getInstance()->get(IndexSwitcherInterface::class); + } + if (null === $indexScopeState) { + $indexScopeState = ObjectManager::getInstance()->get(State::class); + } + $this->indexSwitcher = $indexSwitcher; + $this->indexScopeState = $indexScopeState; } /** @@ -106,10 +140,14 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F 'data' => $this->data ]); foreach ($storeIds as $storeId) { - $dimension = $this->dimensionFactory->create(['name' => 'scope', 'value' => $storeId]); - $saveHandler->cleanIndex([$dimension]); - $saveHandler->saveIndex([$dimension], $this->fullAction->rebuildStoreIndex($storeId)); + $dimensions = [$this->dimensionFactory->create(['name' => 'scope', 'value' => $storeId])]; + $this->indexScopeState->useTemporaryIndex(); + + $saveHandler->cleanIndex($dimensions); + $saveHandler->saveIndex($dimensions, $this->fullAction->rebuildStoreIndex($storeId)); + $this->indexSwitcher->switchIndex($dimensions); + $this->indexScopeState->useRegularIndex(); } $this->fulltextResource->resetSearchResults(); $this->searchRequestConfig->reset(); diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructure.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructure.php index 9a2bb76c04ce76ccfa60e757c2075120ef2d09e2..d2d76bc71ccbf4223036f59e8eebc65fcbd94c4c 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructure.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructure.php @@ -12,6 +12,7 @@ use Magento\Framework\DB\Ddl\Table; use Magento\Framework\Search\Request\Dimension; use Magento\Framework\Indexer\IndexStructureInterface; use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; class IndexStructure implements IndexStructureInterface { @@ -19,6 +20,7 @@ class IndexStructure implements IndexStructureInterface * @var Resource */ private $resource; + /** * @var IndexScopeResolver */ @@ -26,11 +28,11 @@ class IndexStructure implements IndexStructureInterface /** * @param ResourceConnection $resource - * @param IndexScopeResolver $indexScopeResolver + * @param IndexScopeResolverInterface $indexScopeResolver */ public function __construct( ResourceConnection $resource, - IndexScopeResolver $indexScopeResolver + IndexScopeResolverInterface $indexScopeResolver ) { $this->resource = $resource; $this->indexScopeResolver = $indexScopeResolver; diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherInterface.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..ba5fb461dde35aa0a6cdfebc1520b8d1091d841b --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogSearch\Model\Indexer; + +/** + * Provides a functionality to replace main index with its temporary representation + */ +interface IndexSwitcherInterface +{ + /** + * Switch current index with temporary index + * + * It will drop current index table and rename temporary index table to the current index table. + * + * @param array $dimensions + * @return void + */ + public function switchIndex(array $dimensions); +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherProxy.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherProxy.php new file mode 100644 index 0000000000000000000000000000000000000000..2ce093ed99e3a48aebadc2a02c788fd9de018591 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherProxy.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Indexer; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\ScopeInterface; + +/** + * Proxy for adapter-specific index switcher + */ +class IndexSwitcherProxy implements IndexSwitcherInterface +{ + /** + * Object Manager instance + * + * @var ObjectManagerInterface + */ + private $objectManager = null; + + /** + * Instance name to create + * + * @var string + */ + private $handlers; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * Configuration path by which current indexer handler stored + * + * @var string + */ + private $configPath; + + /** + * Factory constructor + * + * @param ObjectManagerInterface $objectManager + * @param ScopeConfigInterface $scopeConfig + * @param string $configPath + * @param string[] $handlers + */ + public function __construct( + ObjectManagerInterface $objectManager, + ScopeConfigInterface $scopeConfig, + $configPath, + array $handlers = [] + ) { + $this->objectManager = $objectManager; + $this->scopeConfig = $scopeConfig; + $this->configPath = $configPath; + $this->handlers = $handlers; + } + + /** + * {@inheritDoc} + * + * As index switcher is an optional part of the search SPI, it may be not defined by a search engine. + * It is especially reasonable for search engines with pre-defined indexes declaration (like old SOLR and Sphinx) + * which cannot create temporary indexes on the fly. + * That's the reason why this method do nothing for the case + * when switcher is not defined for a specific search engine. + */ + public function switchIndex(array $dimensions) + { + $currentHandler = $this->scopeConfig->getValue($this->configPath, ScopeInterface::SCOPE_STORE); + if (!isset($this->handlers[$currentHandler])) { + return; + } + $this->create($currentHandler)->switchIndex($dimensions); + } + + /** + * Create indexer handler + * + * @param string $handler + * @return IndexSwitcherInterface + */ + private function create($handler) + { + $indexSwitcher = $this->objectManager->create($this->handlers[$handler]); + + if (!$indexSwitcher instanceof IndexSwitcherInterface) { + throw new \InvalidArgumentException( + $handler . ' index switcher doesn\'t implement ' . IndexSwitcherInterface::class + ); + } + + return $indexSwitcher; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php index 8d99cd4492bf59cd4c98439cc402ec36d93744f6..2aa9a418bd7258f9046ebdf889f7b69f8a0c8d31 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php @@ -7,13 +7,11 @@ namespace Magento\CatalogSearch\Model\Indexer; use Magento\Eav\Model\Config; use Magento\Framework\App\ResourceConnection; -use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\Indexer\SaveHandler\IndexerInterface; use Magento\Framework\Indexer\IndexStructureInterface; use Magento\Framework\Search\Request\Dimension; use Magento\Framework\Search\Request\IndexScopeResolverInterface; use Magento\Framework\Indexer\SaveHandler\Batch; -use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; class IndexerHandler implements IndexerInterface { @@ -62,7 +60,7 @@ class IndexerHandler implements IndexerInterface * @param ResourceConnection $resource * @param Config $eavConfig * @param Batch $batch - * @param \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver $indexScopeResolver + * @param IndexScopeResolverInterface $indexScopeResolver * @param array $data * @param int $batchSize */ @@ -71,7 +69,7 @@ class IndexerHandler implements IndexerInterface ResourceConnection $resource, Config $eavConfig, Batch $batch, - IndexScopeResolver $indexScopeResolver, + IndexScopeResolverInterface $indexScopeResolver, array $data, $batchSize = 100 ) { diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexSwitcher.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexSwitcher.php new file mode 100644 index 0000000000000000000000000000000000000000..87a7b7110d3aae24999040e62d75bdd01052528e --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexSwitcher.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogSearch\Model\Indexer\Scope; + +use Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; + +/** + * Provides a functionality to replace main index with its temporary representation + */ +class IndexSwitcher implements IndexSwitcherInterface +{ + /** + * @var Resource + */ + private $resource; + + /** + * @var ScopeProxy + */ + private $resolver; + + /** + * @var State + */ + private $state; + + /** + * @param ResourceConnection $resource + * @param IndexScopeResolverInterface $indexScopeResolver + * @param State $state + */ + public function __construct( + ResourceConnection $resource, + IndexScopeResolverInterface $indexScopeResolver, + State $state + ) { + $this->resource = $resource; + $this->resolver = $indexScopeResolver; + $this->state = $state; + } + + /** + * {@inheritdoc} + * @throws IndexTableNotExistException + */ + public function switchIndex(array $dimensions) + { + if (State::USE_TEMPORARY_INDEX === $this->state->getState()) { + $index = \Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID; + + $temporalIndexTable = $this->resolver->resolve($index, $dimensions); + if (!$this->resource->getConnection()->isTableExists($temporalIndexTable)) { + throw new IndexTableNotExistException( + __( + "Temporary table for index $index doesn't exist," + . " which is inconsistent with state of scope resolver" + ) + ); + } + + $this->state->useRegularIndex(); + $tableName = $this->resolver->resolve($index, $dimensions); + if ($this->resource->getConnection()->isTableExists($tableName)) { + $this->resource->getConnection()->dropTable($tableName); + } + + $this->resource->getConnection()->renameTable($temporalIndexTable, $tableName); + $this->state->useTemporaryIndex(); + } + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexTableNotExistException.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexTableNotExistException.php new file mode 100644 index 0000000000000000000000000000000000000000..6974f8c278ab55af0cb41f501124eb3201f5e914 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexTableNotExistException.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Indexer\Scope; + + +use Magento\Framework\Exception\LocalizedException; + +/** + * Exception which represents situation where temporary index table should be used somewhere, + * but it does not exist in a database + */ +class IndexTableNotExistException extends LocalizedException +{ +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/ScopeProxy.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/ScopeProxy.php new file mode 100644 index 0000000000000000000000000000000000000000..14832af303bf4167df380776f28de09482c84e7f --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/ScopeProxy.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Indexer\Scope; + + +use Magento\Framework\Search\Request\Dimension; + +/** + * Implementation of IndexScopeResolverInterface which resolves index scope dynamically + * depending on current scope state + */ +class ScopeProxy implements \Magento\Framework\Search\Request\IndexScopeResolverInterface +{ + /** + * Object Manager instance + * + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var array + */ + private $states = []; + + /** + * @var State + */ + private $scopeState; + + /** + * Factory constructor + * + * @param \Magento\Framework\ObjectManagerInterface $objectManager + * @param State $scopeState + * @param array $states + */ + public function __construct( + \Magento\Framework\ObjectManagerInterface $objectManager, + State $scopeState, + array $states + ) { + $this->objectManager = $objectManager; + $this->scopeState = $scopeState; + $this->states = $states; + } + + /** + * Creates class instance with specified parameters + * + * @param string $state + * @return \Magento\Framework\Search\Request\IndexScopeResolverInterface + * @throws UnknownStateException + */ + private function create($state) + { + if (!array_key_exists($state, $this->states)) { + throw new UnknownStateException(__("Requested resolver for unknown indexer state: $state")); + } + return $this->objectManager->create($this->states[$state]); + } + + /** + * @param string $index + * @param Dimension[] $dimensions + * @return string + */ + public function resolve($index, array $dimensions) + { + return $this->create($this->scopeState->getState())->resolve($index, $dimensions); + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/State.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/State.php new file mode 100644 index 0000000000000000000000000000000000000000..2bba29ae8d842f963146bef411d3a71023740ae8 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/State.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Indexer\Scope; + + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\App\ScopeResolverInterface; +use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; +use Magento\Framework\Search\Request\Dimension; +use Magento\Framework\Search\Request\IndexScopeResolverInterface; + +/** + * This class represents state that defines which table should be used during indexation process + * + * There are two possible states: + * - use_temporary_table + * - use_main_table + * + * The 'use_main_table' state means that default indexer table should be used. + * + * The 'use_temporary_table' state is an opposite for 'use_main_table' + * which means that default indexer table should be left unchanged during indexation + * and temporary table should be used instead. + * + */ +class State +{ + const USE_TEMPORARY_INDEX = 'use_temporary_table'; + const USE_REGULAR_INDEX = 'use_main_table'; + + /** + * @var string + */ + private $state = self::USE_REGULAR_INDEX; + + /** + * Set the state to use temporary Index + * @return void + */ + public function useTemporaryIndex() + { + $this->state = self::USE_TEMPORARY_INDEX; + } + + /** + * Set the state to use regular Index + * @return void + */ + public function useRegularIndex() + { + $this->state = self::USE_REGULAR_INDEX; + } + + /** + * @return string + */ + public function getState() + { + return $this->state; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/TemporaryResolver.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/TemporaryResolver.php new file mode 100644 index 0000000000000000000000000000000000000000..51037eb637cf3c346fdcd7ac64aefc0a50e14949 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/TemporaryResolver.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Indexer\Scope; + + +use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; +use Magento\Framework\Search\Request\Dimension; + +/** + * Resolves name of a temporary table for indexation + */ +class TemporaryResolver implements \Magento\Framework\Search\Request\IndexScopeResolverInterface +{ + /** + * @var IndexScopeResolver + */ + private $indexScopeResolver; + + /** + * @inheritDoc + */ + public function __construct(IndexScopeResolver $indexScopeResolver) + { + $this->indexScopeResolver = $indexScopeResolver; + } + + /** + * @param string $index + * @param Dimension[] $dimensions + * @return string + */ + public function resolve($index, array $dimensions) + { + $tableName = $this->indexScopeResolver->resolve($index, $dimensions); + $tableName .= \Magento\Framework\Indexer\Table\StrategyInterface::TMP_SUFFIX; + + return $tableName; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/UnknownStateException.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/UnknownStateException.php new file mode 100644 index 0000000000000000000000000000000000000000..04803ef27480efd504949ecc43773ebd3db3d2f3 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/UnknownStateException.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Indexer\Scope; + + +use Magento\Framework\Exception\LocalizedException; + +/** + * Exception for situation where used state which is not defined in configuration + */ +class UnknownStateException extends LocalizedException +{ + +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php index f4c0437590ed56c72c5e0076f79be93d181e082c..6ef656d566867dec02cf3a157e1b6d0c3cd24826 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php @@ -8,7 +8,13 @@ namespace Magento\CatalogSearch\Test\Unit\Model\Indexer; use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource; use Magento\Framework\Search\Request\Config as SearchRequestConfig; use Magento\Framework\Search\Request\DimensionFactory; +use Magento\Framework\Search\Request\Dimension; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use \Magento\CatalogSearch\Model\Indexer\Fulltext\IndexSwitcher; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class FulltextTest extends \PHPUnit_Framework_TestCase { /** @@ -26,11 +32,6 @@ class FulltextTest extends \PHPUnit_Framework_TestCase */ protected $storeManager; - /** - * @var \Magento\Framework\Search\Request\Dimension|\PHPUnit_Framework_MockObject_MockObject - */ - protected $dimension; - /** * @var \Magento\CatalogSearch\Model\Indexer\IndexerHandler|\PHPUnit_Framework_MockObject_MockObject */ @@ -47,8 +48,15 @@ class FulltextTest extends \PHPUnit_Framework_TestCase protected $searchRequestConfig; /** - * + * @var \Magento\Framework\Search\Request\DimensionFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $dimensionFactory; + + /** + * @var \Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher|\PHPUnit_Framework_MockObject_MockObject + */ + private $indexSwitcher; + protected function setUp() { $this->fullAction = $this->getClassMock(\Magento\CatalogSearch\Model\Indexer\Fulltext\Action\Full::class); @@ -80,27 +88,29 @@ class FulltextTest extends \PHPUnit_Framework_TestCase [] ); - $this->dimension = $this->getClassMock(\Magento\Framework\Search\Request\Dimension::class); - $dimensionFactory = $this->getMock( - \Magento\Framework\Search\Request\DimensionFactory::class, - ['create'], - [], - '', - false - ); - $dimensionFactory->expects($this->any())->method('create')->willReturn($this->dimension); + $this->dimensionFactory = $this->getMock(DimensionFactory::class, ['create'], [], '', false); $this->fulltextResource = $this->getClassMock(\Magento\CatalogSearch\Model\ResourceModel\Fulltext::class); $this->searchRequestConfig = $this->getClassMock(\Magento\Framework\Search\Request\Config::class); - $this->model = new \Magento\CatalogSearch\Model\Indexer\Fulltext( - $fullActionFactory, - $indexerHandlerFactory, - $this->storeManager, - $dimensionFactory, - $this->fulltextResource, - $this->searchRequestConfig, - [] + $this->indexSwitcher = $this->getMockBuilder(\Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher::class) + ->disableOriginalConstructor() + ->setMethods(['switchIndex']) + ->getMock(); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $objectManagerHelper->getObject( + \Magento\CatalogSearch\Model\Indexer\Fulltext::class, + [ + 'fullActionFactory' => $fullActionFactory, + 'indexerHandlerFactory' => $indexerHandlerFactory, + 'storeManager' => $this->storeManager, + 'dimensionFactory' => $this->dimensionFactory, + 'fulltextResource' => $this->fulltextResource, + 'searchRequestConfig' => $this->searchRequestConfig, + 'data' => [], + 'indexSwitcher' => $this->indexSwitcher, + ] ); } @@ -131,13 +141,38 @@ class FulltextTest extends \PHPUnit_Framework_TestCase public function testExecuteFull() { $stores = [0 => 'Store 1', 1 => 'Store 2']; - $indexData = new \ArrayObject([]); + $indexData = new \ArrayObject([new \ArrayObject([]), new \ArrayObject([])]); $this->storeManager->expects($this->once())->method('getStores')->willReturn($stores); - $this->saveHandler->expects($this->exactly(count($stores)))->method('cleanIndex'); - $this->saveHandler->expects($this->exactly(2))->method('saveIndex'); + + $dimensionScope1 = $this->getMock(Dimension::class, [], ['scope', '1']); + $dimensionScope2 = $this->getMock(Dimension::class, [], ['scope', '2']); + + $this->dimensionFactory->expects($this->any())->method('create')->willReturnOnConsecutiveCalls( + $dimensionScope1, + $dimensionScope2 + ); + $this->indexSwitcher->expects($this->exactly(2))->method('switchIndex') + ->withConsecutive( + [$this->equalTo([$dimensionScope1])], + [$this->equalTo([$dimensionScope2])] + ); + + $this->saveHandler->expects($this->exactly(count($stores)))->method('cleanIndex') + ->withConsecutive( + [$this->equalTo([$dimensionScope1])], + [$this->equalTo([$dimensionScope2])] + ); + + $this->saveHandler->expects($this->exactly(2))->method('saveIndex') + ->withConsecutive( + [$this->equalTo([$dimensionScope1]), $this->equalTo($indexData)], + [$this->equalTo([$dimensionScope2]), $this->equalTo($indexData)] + ); $this->fullAction->expects($this->exactly(2)) ->method('rebuildStoreIndex') - ->willReturn(new \ArrayObject([$indexData, $indexData])); + ->withConsecutive([0], [1]) + ->willReturn($indexData); + $this->fulltextResource->expects($this->once())->method('resetSearchResults'); $this->searchRequestConfig->expects($this->once())->method('reset'); diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Scope/IndexSwitcherTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Scope/IndexSwitcherTest.php new file mode 100644 index 0000000000000000000000000000000000000000..31bec5906ae5a37c34f666af0999b1cafe2f0485 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Scope/IndexSwitcherTest.php @@ -0,0 +1,212 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogSearch\Test\Unit\Model\Indexer\Scope; + +use Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher; +use Magento\CatalogSearch\Model\Indexer\Scope\State; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver; +use Magento\Framework\Indexer\IndexStructureInterface; +use Magento\Framework\Search\Request\Dimension; +use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class IndexSwitcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $connection; + + /** + * @var \Magento\Framework\Search\Request\IndexScopeResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $indexScopeResolver; + + /** + * @var State|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeState; + + /** + * @var IndexSwitcher + */ + private $indexSwitcher; + + /** + * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + */ + private $resource; + + protected function setUp() + { + $this->resource = $this->getMockBuilder(ResourceConnection::class) + ->setMethods(['getConnection']) + ->disableOriginalConstructor() + ->getMock(); + $this->connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->setMethods(['isTableExists', 'dropTable', 'renameTable']) + ->getMockForAbstractClass(); + $this->resource->expects($this->any()) + ->method('getConnection') + ->willReturn($this->connection); + $this->indexScopeResolver = $this->getMockBuilder( + \Magento\Framework\Search\Request\IndexScopeResolverInterface::class + ) + ->setMethods(['resolve']) + ->getMockForAbstractClass(); + $this->scopeState = $this->getMockBuilder(State::class) + ->setMethods(['getState', 'useRegularIndex', 'useTemporaryIndex']) + ->disableOriginalConstructor() + ->getMock(); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->indexSwitcher = $objectManagerHelper->getObject( + \Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher::class, + [ + 'resource' => $this->resource, + 'indexScopeResolver' => $this->indexScopeResolver, + 'state' => $this->scopeState, + ] + ); + } + + public function testSwitchRegularIndex() + { + $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])]; + + $this->scopeState->expects($this->once()) + ->method('getState') + ->willReturn(State::USE_REGULAR_INDEX); + + $this->indexScopeResolver->expects($this->never())->method('resolve'); + $this->connection->expects($this->never())->method('renameTable'); + + $this->indexSwitcher->switchIndex($dimensions); + } + + public function testSwitchIndexWithUnknownState() + { + $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])]; + + $this->scopeState->expects($this->once()) + ->method('getState') + ->willReturn('unknown_state'); + + $this->indexScopeResolver->expects($this->never())->method('resolve'); + $this->connection->expects($this->never())->method('renameTable'); + + $this->indexSwitcher->switchIndex($dimensions); + } + + public function testSwitchTemporaryIndexWhenRegularIndexExist() + { + $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])]; + + $this->scopeState->expects($this->once()) + ->method('getState') + ->willReturn(State::USE_TEMPORARY_INDEX); + + $this->scopeState->expects($this->at(1))->method('useRegularIndex'); + $this->scopeState->expects($this->at(2))->method('useTemporaryIndex'); + + $this->indexScopeResolver->expects($this->exactly(2))->method('resolve') + ->withConsecutive( + [$this->equalTo(FulltextIndexer::INDEXER_ID), $this->equalTo($dimensions)], + [$this->equalTo(FulltextIndexer::INDEXER_ID), $this->equalTo($dimensions)] + ) + ->willReturnOnConsecutiveCalls( + 'catalogsearch_fulltext_scope1_tmp1', + 'catalogsearch_fulltext_scope1' + ); + + $this->connection->expects($this->exactly(2))->method('isTableExists') + ->withConsecutive( + [$this->equalTo('catalogsearch_fulltext_scope1_tmp1'), $this->equalTo(null)], + [$this->equalTo('catalogsearch_fulltext_scope1'), $this->equalTo(null)] + ) + ->willReturnOnConsecutiveCalls( + true, + true + ); + + $this->connection->expects($this->once())->method('dropTable')->with('catalogsearch_fulltext_scope1', null); + $this->connection->expects($this->once()) + ->method('renameTable') + ->with('catalogsearch_fulltext_scope1_tmp1', 'catalogsearch_fulltext_scope1'); + + $this->indexSwitcher->switchIndex($dimensions); + } + + public function testSwitchTemporaryIndexWhenRegularIndexNotExist() + { + $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])]; + + $this->scopeState->expects($this->once()) + ->method('getState') + ->willReturn(State::USE_TEMPORARY_INDEX); + + $this->scopeState->expects($this->at(1))->method('useRegularIndex'); + $this->scopeState->expects($this->at(2))->method('useTemporaryIndex'); + + $this->indexScopeResolver->expects($this->exactly(2))->method('resolve') + ->withConsecutive( + [$this->equalTo(FulltextIndexer::INDEXER_ID), $this->equalTo($dimensions)], + [$this->equalTo(FulltextIndexer::INDEXER_ID), $this->equalTo($dimensions)] + ) + ->willReturnOnConsecutiveCalls( + 'catalogsearch_fulltext_scope1_tmp1', + 'catalogsearch_fulltext_scope1' + ); + + $this->connection->expects($this->exactly(2))->method('isTableExists') + ->withConsecutive( + [$this->equalTo('catalogsearch_fulltext_scope1_tmp1'), $this->equalTo(null)], + [$this->equalTo('catalogsearch_fulltext_scope1'), $this->equalTo(null)] + ) + ->willReturnOnConsecutiveCalls( + true, + false + ); + + $this->connection->expects($this->never())->method('dropTable')->with('catalogsearch_fulltext_scope1', null); + $this->connection->expects($this->once()) + ->method('renameTable') + ->with('catalogsearch_fulltext_scope1_tmp1', 'catalogsearch_fulltext_scope1'); + + $this->indexSwitcher->switchIndex($dimensions); + } + + /** + * @expectedException \Magento\CatalogSearch\Model\Indexer\Scope\IndexTableNotExistException + * @expectedExceptionMessage Temporary table for index catalogsearch_fulltext doesn't exist + */ + public function testSwitchWhenTemporaryIndexNotExist() + { + $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])]; + + $this->scopeState->expects($this->once()) + ->method('getState') + ->willReturn(State::USE_TEMPORARY_INDEX); + + $this->scopeState->expects($this->never())->method('useRegularIndex'); + $this->scopeState->expects($this->never())->method('useTemporaryIndex'); + + $this->indexScopeResolver->expects($this->once())->method('resolve') + ->with(FulltextIndexer::INDEXER_ID, $dimensions) + ->willReturn('catalogsearch_fulltext_scope1_tmp1'); + + $this->connection->expects($this->once()) + ->method('isTableExists') + ->with('catalogsearch_fulltext_scope1_tmp1', null) + ->willReturn(false); + + $this->connection->expects($this->never())->method('dropTable')->with('catalogsearch_fulltext_scope1', null); + $this->connection->expects($this->never())->method('renameTable'); + + $this->indexSwitcher->switchIndex($dimensions); + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php index 4b04e7d66ab569ab3d2e6219cef4e2b69268d8e0..d44fef54683fc9e3c91c79d8844c2d620c750079 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php @@ -74,7 +74,10 @@ class CollectionTest extends BaseCollectionTest $productLimitationMock = $this->getMock( \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class ); - $productLimitationFactoryMock = $this->getMock(ProductLimitationFactory::class, ['create']); + $productLimitationFactoryMock = $this->getMockBuilder(ProductLimitationFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); $productLimitationFactoryMock->method('create') ->willReturn($productLimitationMock); diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php index 5e56a253563592a55b80b00d26432028d9f4c5fa..5732ed2c8aee1dfa2429757a9e8f67b1cc324f1f 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php @@ -80,7 +80,10 @@ class CollectionTest extends BaseCollectionTest $productLimitationMock = $this->getMock( \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class ); - $productLimitationFactoryMock = $this->getMock(ProductLimitationFactory::class, ['create']); + $productLimitationFactoryMock = $this->getMockBuilder(ProductLimitationFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); $productLimitationFactoryMock->method('create') ->willReturn($productLimitationMock); diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/Indexer/IndexStructureTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/Indexer/IndexStructureTest.php index 3bb270e990617d3a9e05ca574e788f5bb3f714aa..8f4bb7736cd37b9bf86d5591e792edea8909ef19 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/Indexer/IndexStructureTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/Indexer/IndexStructureTest.php @@ -48,9 +48,8 @@ class IndexStructureTest extends \PHPUnit_Framework_TestCase ->method('getConnection') ->willReturn($this->connection); $this->indexScopeResolver = $this->getMockBuilder( - \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver::class + \Magento\Framework\Search\Request\IndexScopeResolverInterface::class )->setMethods(['resolve']) - ->disableOriginalConstructor() ->getMock(); $objectManager = new ObjectManager($this); @@ -64,11 +63,6 @@ class IndexStructureTest extends \PHPUnit_Framework_TestCase ); } - /** - * @param string $table - * @param array $dimensions - * @param bool $isTableExist - */ public function testDelete() { $index = 'index_name'; diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index 7e9451f9b83e7ada30455509b8fd686a0c251705..7116abf263b0dde081a0a7f42f585e807e47ee85 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -12,6 +12,7 @@ <preference for="Magento\Framework\Search\Dynamic\DataProviderInterface" type="Magento\CatalogSearch\Model\Adapter\Mysql\Dynamic\DataProvider" /> <preference for="Magento\Framework\Search\Adapter\OptionsInterface" type="Magento\CatalogSearch\Model\Adapter\Options" /> <preference for="Magento\CatalogSearch\Model\Search\FilterMapper\FilterStrategyInterface" type="Magento\CatalogSearch\Model\Search\FilterMapper\FilterContext"/> + <preference for="\Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface" type="\Magento\CatalogSearch\Model\Indexer\IndexSwitcherProxy"/> <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory"> <arguments> <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument> @@ -20,6 +21,14 @@ </argument> </arguments> </type> + <type name="Magento\CatalogSearch\Model\Indexer\IndexSwitcherProxy"> + <arguments> + <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument> + <argument name="handlers" xsi:type="array"> + <item name="mysql" xsi:type="string">\Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher</item> + </argument> + </arguments> + </type> <type name="Magento\CatalogSearch\Model\Indexer\IndexStructureFactory"> <arguments> <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument> @@ -246,4 +255,27 @@ </argument> </arguments> </type> + <type name="\Magento\CatalogSearch\Model\Indexer\IndexerHandler"> + <arguments> + <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument> + </arguments> + </type> + <type name="\Magento\CatalogSearch\Model\Indexer\IndexStructure"> + <arguments> + <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument> + </arguments> + </type> + <type name="\Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher"> + <arguments> + <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument> + </arguments> + </type> + <type name="\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy"> + <arguments> + <argument name="states" xsi:type="array"> + <item name="use_temporary_table" xsi:type="string">\Magento\CatalogSearch\Model\Indexer\Scope\TemporaryResolver</item> + <item name="use_main_table" xsi:type="string">\Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php index 6f2b989e5e9bfaa9fa91643b1958be6a99c0fdc1..794034b446f72b3e1649352f74b74b49345c7a3f 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php @@ -29,18 +29,25 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer $connection = $this->getConnection(); $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable(); $select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable); + $linkField = $metadata->getLinkField(); $select->reset( \Magento\Framework\DB\Select::COLUMNS )->columns( ['e.entity_id', 'cis.website_id', 'cis.stock_id'] )->joinLeft( ['l' => $this->getTable('catalog_product_super_link')], - 'l.parent_id = e.' . $metadata->getLinkField(), + 'l.parent_id = e.' . $linkField, [] )->join( ['le' => $this->getTable('catalog_product_entity')], 'le.entity_id = l.product_id', [] + )->joinInner( + ['cpei' => $this->getTable('catalog_product_entity_int')], + 'le.' . $linkField . ' = cpei.' . $linkField + . ' AND cpei.attribute_id = ' . $this->_getAttribute('status')->getId() + . ' AND cpei.value = ' . ProductStatus::STATUS_ENABLED, + [] )->joinLeft( ['i' => $idxTable], 'i.product_id = l.product_id AND cis.website_id = i.website_id AND cis.stock_id = i.stock_id', diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js index c182d9f8216c09a42868eb487dfa06b0e718b5c7..be44c110c2b6259484066ffc4c7cbe4400c68d9e 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js @@ -32,6 +32,7 @@ define([ identificationProperty: 'id', 'attribute_set_id': '', attributesTmp: [], + changedFlag: 'was_changed', listens: { 'insertDataFromGrid': 'processingInsertDataFromGrid', 'insertDataFromWizard': 'processingInsertDataFromWizard', @@ -391,9 +392,9 @@ define([ 'small_image': row['small_image'], image: row.image, 'thumbnail': row.thumbnail, - 'attributes': attributesText, - 'was_changed': true + 'attributes': attributesText }; + product[this.changedFlag] = true; product[this.canEditField] = row.editable; product[this.newProductField] = row.newProduct; tmpArray.push(product); @@ -515,6 +516,7 @@ define([ tmpArray[rowIndex].status = 1; } + tmpArray[rowIndex][this.changedFlag] = true; this.unionInsertData(tmpArray); } }); diff --git a/app/code/Magento/Customer/Model/Indexer/Source.php b/app/code/Magento/Customer/Model/Indexer/Source.php new file mode 100644 index 0000000000000000000000000000000000000000..60522227eb222fb058c3ddb36681a6276d635e94 --- /dev/null +++ b/app/code/Magento/Customer/Model/Indexer/Source.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model\Indexer; + +use Magento\Customer\Model\ResourceModel\Customer\Indexer\Collection; +use Magento\Framework\App\ResourceConnection\SourceProviderInterface; +use Traversable; + +/** + * Customers data batch generator for customer_grid indexer + */ +class Source implements \IteratorAggregate, \Countable, SourceProviderInterface +{ + /** + * @var Collection + */ + private $customerCollection; + + /** + * @var int + */ + private $batchSize; + + /** + * @param \Magento\Customer\Model\ResourceModel\Customer\Indexer\CollectionFactory $collection + * @param int $batchSize + */ + public function __construct( + \Magento\Customer\Model\ResourceModel\Customer\Indexer\CollectionFactory $collectionFactory, + $batchSize = 10000 + ) { + $this->customerCollection = $collectionFactory->create(); + $this->batchSize = $batchSize; + } + + /** + * {@inheritdoc} + */ + public function getMainTable() + { + return $this->customerCollection->getMainTable(); + } + + /** + * {@inheritdoc} + */ + public function getIdFieldName() + { + return $this->customerCollection->getIdFieldName(); + } + + /** + * {@inheritdoc} + */ + public function addFieldToSelect($fieldName, $alias = null) + { + $this->customerCollection->addFieldToSelect($fieldName, $alias); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getSelect() + { + return $this->customerCollection->getSelect(); + } + + /** + * {@inheritdoc} + */ + public function addFieldToFilter($attribute, $condition = null) + { + $this->customerCollection->addFieldToFilter($attribute, $condition); + return $this; + } + + /** + * @return int + */ + public function count() + { + return $this->customerCollection->getSize(); + } + + /** + * Retrieve an iterator + * + * @return Traversable + */ + public function getIterator() + { + $this->customerCollection->setPageSize($this->batchSize); + $lastPage = $this->customerCollection->getLastPageNumber(); + $pageNumber = 0; + do { + $this->customerCollection->clear(); + $this->customerCollection->setCurPage($pageNumber); + foreach ($this->customerCollection->getItems() as $key => $value) { + yield $key => $value; + } + $pageNumber++; + } while ($pageNumber <= $lastPage); + } +} diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer/Indexer/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Customer/Indexer/Collection.php new file mode 100644 index 0000000000000000000000000000000000000000..5b9716af5393adb9b91de1579533b6369283d795 --- /dev/null +++ b/app/code/Magento/Customer/Model/ResourceModel/Customer/Indexer/Collection.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model\ResourceModel\Customer\Indexer; + +/** + * Customers collection for customer_grid indexer + */ +class Collection extends \Magento\Customer\Model\ResourceModel\Customer\Collection +{ + /** + * @inheritdoc + */ + protected function beforeAddLoadedItem(\Magento\Framework\DataObject $item) + { + return $item; + } +} diff --git a/app/code/Magento/Customer/Setup/UpgradeSchema.php b/app/code/Magento/Customer/Setup/UpgradeSchema.php index 33ec2352e865d0b6fd5cbbde5d9646b5dd92975d..46ccd83fcaae11b3ab4720e5ead60b3625bbda38 100755 --- a/app/code/Magento/Customer/Setup/UpgradeSchema.php +++ b/app/code/Magento/Customer/Setup/UpgradeSchema.php @@ -136,14 +136,12 @@ class UpgradeSchema implements UpgradeSchemaInterface ] ); foreach ($keys as $key) { - $setup->getConnection()->modifyColumn( + $description = $setup->getConnection()->describeTable($key['TABLE_NAME'])[$key['COLUMN_NAME']]; + $description['DATA_TYPE'] = 'int'; + $setup->getConnection()->modifyColumnByDdl( $key['TABLE_NAME'], $key['COLUMN_NAME'], - [ - 'type' => 'integer', - 'unsigned' => true, - 'nullable' => false - ] + $description ); } } diff --git a/app/code/Magento/Customer/etc/indexer.xml b/app/code/Magento/Customer/etc/indexer.xml index b48592cafbb20b6b62ae2e4fd953ecce1e35e0af..5f644426d81696c643324365ee7e2cacd8fb64cb 100644 --- a/app/code/Magento/Customer/etc/indexer.xml +++ b/app/code/Magento/Customer/etc/indexer.xml @@ -11,7 +11,7 @@ <title translate="true">Customer Grid</title> <description translate="true">Rebuild Customer grid index</description> - <fieldset name="customer" source="Magento\Customer\Model\ResourceModel\Customer\Collection" + <fieldset name="customer" source="Magento\Customer\Model\Indexer\Source" provider="Magento\Customer\Model\Indexer\AttributeProvider"> <field name="name" xsi:type="searchable" dataType="text" handler="CustomerNameHandler"/> <field name="email" xsi:type="searchable" dataType="varchar"/> diff --git a/app/code/Magento/Downloadable/etc/di.xml b/app/code/Magento/Downloadable/etc/di.xml index d7e2aafe79582d0f50a419cdc9bcf7f2f090cdcd..8e3d0bb6c5c5ae598a8365770f7e2ae932c78d73 100644 --- a/app/code/Magento/Downloadable/etc/di.xml +++ b/app/code/Magento/Downloadable/etc/di.xml @@ -123,4 +123,25 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Price\CostStorage"> + <arguments> + <argument name="allowedProductTypes" xsi:type="array"> + <item name="2" xsi:type="string">downloadable</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Price\TierPriceValidator"> + <arguments> + <argument name="allowedProductTypes" xsi:type="array"> + <item name="3" xsi:type="string">downloadable</item> + </argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Price\BasePriceStorage"> + <arguments> + <argument name="allowedProductTypes" xsi:type="array"> + <item name="3" xsi:type="string">downloadable</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js index 449418f07c100c91e139231c704bb28fd8c9ae82..bfae1cf87030f45d8bbda74e8eb3d9f741f00201 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js @@ -538,7 +538,8 @@ define([ label: cell.config.label, name: cell.name, required: !!cell.config.validation, - columnsHeaderClasses: cell.config.columnsHeaderClasses + columnsHeaderClasses: cell.config.columnsHeaderClasses, + sortOrder: cell.config.sortOrder }); this.labels.push(data); diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..aad068ce6f0799feb342e93b3f94c6e6899c18c9 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * BasePriceStorage test. + */ +class BasePriceStorageTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogBasePriceStorageV1'; + const SERVICE_VERSION = 'V1'; + const SIMPLE_PRODUCT_SKU = 'simple'; + + /** + * @var \Magento\TestFramework\ObjectManager + */ + private $objectManager; + + /** + * Set up. + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * Test get method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testGet() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/base-prices-information', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + $response = $this->_webApiCall($serviceInfo, ['skus' => [self::SIMPLE_PRODUCT_SKU]]); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU); + + $this->assertNotEmpty($response); + $this->assertEquals($product->getPrice(), $response[0]['price']); + $this->assertEquals($product->getSku(), $response[0]['sku']); + } + + /** + * Test update method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testUpdate() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/base-prices', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Update', + ], + ]; + $newPrice = 9999; + $storeId = 0; + $response = $this->_webApiCall( + $serviceInfo, + [ + 'prices' => [ + [ + 'price' => $newPrice, + 'store_id' => $storeId, + 'sku' => self::SIMPLE_PRODUCT_SKU, + ] + ] + ] + ); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU); + + $this->assertNotEmpty($response); + $this->assertEquals($product->getPrice(), $newPrice); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6af9960a524dfe63910b51dbee08335af109d909 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * CostStorage test. + */ +class CostStorageTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogCostStorageV1'; + const SERVICE_VERSION = 'V1'; + const SIMPLE_PRODUCT_SKU = 'simple'; + + /** + * @var \Magento\TestFramework\ObjectManager + */ + private $objectManager; + + /** + * Set up. + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * Test get method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testGet() + { + $cost = 3057; + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productRepository->save($productRepository->get(self::SIMPLE_PRODUCT_SKU)->setData('cost', $cost)); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/cost-information', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Delete', + ], + ]; + $response = $this->_webApiCall($serviceInfo, ['skus' => [self::SIMPLE_PRODUCT_SKU]]); + + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU); + + $this->assertNotEmpty($response); + $this->assertEquals($product->getCost(), $cost); + } + + /** + * Test update method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testUpdate() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/cost', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Update', + ], + ]; + $storeId = 0; + $newCost = 31337; + $response = $this->_webApiCall( + $serviceInfo, + [ + 'prices' => [ + [ + 'cost' => $newCost, + 'store_id' => $storeId, + 'sku' => self::SIMPLE_PRODUCT_SKU, + ] + ] + ] + ); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU); + $this->assertNotEmpty($response); + $this->assertEquals($product->getCost(), $newCost); + } + + /** + * Test delete method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testDelete() + { + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productRepository->save($productRepository->get(self::SIMPLE_PRODUCT_SKU)->setData('cost', 777)); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/cost-delete', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Delete', + ], + ]; + $response = $this->_webApiCall($serviceInfo, ['skus' => [self::SIMPLE_PRODUCT_SKU]]); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU); + $this->assertTrue($response); + $this->assertNull($product->getCost()); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php index 0b761d8c191ab621bf75792a06e5e7e6ca98c1e1..6a09bea591cb8dca4eca1542f79971cbb92c7017 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php @@ -391,6 +391,7 @@ class ProductCustomOptionRepositoryTest extends WebapiAbstract */ public function testUpdateNegative($optionData, $message) { + $this->_markTestAsRestOnly(); $productSku = 'simple'; /** @var ProductRepository $productRepository */ $productRepository = $this->objectManager->create(ProductRepository::class); @@ -403,18 +404,9 @@ class ProductCustomOptionRepositoryTest extends WebapiAbstract 'resourcePath' => '/V1/products/options/' . $optionId, 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT, ], - 'soap' => [ - 'service' => self::SERVICE_NAME, - 'serviceVersion' => 'V1', - 'operation' => self::SERVICE_NAME . 'Save', - ], ]; - if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { - $this->setExpectedException('SoapFault'); - } else { - $this->setExpectedException('Exception', $message, 400); - } + $this->setExpectedException('Exception', $message, 400); $this->_webApiCall($serviceInfo, ['option' => $optionData]); } 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 cd02c6d4d7500ef9adf7d17ac9f866bfc8ef0711..ed641ac0f85b872717b52fbadc46a4d12b7d218e 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -134,15 +134,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract ]; } - private function markAreaAsSecure() - { - /** @var \Magento\Framework\Registry $registry */ - $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get(\Magento\Framework\Registry::class); - $registry->unregister("isSecureArea"); - $registry->register("isSecureArea", true); - } - /** * Test removing association between product and website 1 * @magentoApiDataFixture Magento/Catalog/_files/product_with_two_websites.php @@ -169,9 +160,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract $response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]["website_ids"], $websitesData["website_ids"] ); - $this->deleteProduct($productBuilder[ProductInterface::SKU]); - $this->markAreaAsSecure(); - $website->delete(); } /** @@ -198,9 +186,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract $response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]["website_ids"], $websitesData["website_ids"] ); - $this->deleteProduct($productBuilder[ProductInterface::SKU]); - $this->markAreaAsSecure(); - $website->delete(); } /** @@ -222,7 +207,7 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract $websitesData = [ 'website_ids' => [ 1, - $website->getId(), + (int) $website->getId(), ] ]; $productBuilder[ProductInterface::EXTENSION_ATTRIBUTES_KEY] = $websitesData; @@ -231,9 +216,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract $response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]["website_ids"], $websitesData["website_ids"] ); - $this->deleteProduct($productBuilder[ProductInterface::SKU]); - $this->markAreaAsSecure(); - $website->delete(); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..232c63257215036e414236fba818ff876da97c18 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php @@ -0,0 +1,231 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * TierPriceStorage test. + */ +class TierPriceStorageTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogTierPriceStorageV1'; + const SERVICE_VERSION = 'V1'; + const SIMPLE_PRODUCT_SKU = 'simple'; + + /** + * @var \Magento\TestFramework\ObjectManager + */ + private $objectManager; + + /** + * Set up. + */ + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * Test get method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testGet() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/tier-prices-information', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + $response = $this->_webApiCall($serviceInfo, ['skus' => [self::SIMPLE_PRODUCT_SKU]]); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices(); + $this->assertNotEmpty($response); + $this->assertEquals(count($response), count($tierPrices)); + + foreach ($response as $item) { + $this->assertTrue($this->isPriceCorrect($item, $tierPrices)); + } + } + + /** + * Test update method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testUpdate() + { + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $prices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices(); + $tierPrice = array_shift($prices); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/tier-prices', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Update', + ], + ]; + $newPrice = [ + 'price' => 40, + 'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT, + 'website_id' => 0, + 'sku' => self::SIMPLE_PRODUCT_SKU, + 'customer_group' => 'ALL GROUPS', + 'quantity' => 7778 + ]; + $updatedPrice = [ + 'price' => 778, + 'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED, + 'website_id' => 0, + 'sku' => self::SIMPLE_PRODUCT_SKU, + 'customer_group' => 'ALL GROUPS', + 'quantity' => $tierPrice->getQty() + ]; + $response = $this->_webApiCall($serviceInfo, ['prices' => [$updatedPrice, $newPrice]]); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices(); + $this->assertTrue($response); + $this->assertTrue($this->isPriceCorrect($newPrice, $tierPrices)); + $this->assertTrue($this->isPriceCorrect($updatedPrice, $tierPrices)); + } + + /** + * Test replace method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testReplace() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/tier-prices', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Replace', + ], + ]; + $newPrices = [ + [ + 'price' => 50, + 'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT, + 'website_id' => 0, + 'sku' => self::SIMPLE_PRODUCT_SKU, + 'customer_group' => 'general', + 'quantity' => 7778 + ], + [ + 'price' => 70, + 'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED, + 'website_id' => 0, + 'sku' => self::SIMPLE_PRODUCT_SKU, + 'customer_group' => 'general', + 'quantity' => 33 + ] + ]; + $response = $this->_webApiCall($serviceInfo, ['prices' => $newPrices]); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices(); + $this->assertTrue($response); + $this->assertEquals(count($newPrices), count($tierPrices)); + + foreach ($newPrices as $newPrice) { + $this->assertTrue($this->isPriceCorrect($newPrice, $tierPrices)); + } + } + + /** + * Test delete method. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testDelete() + { + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices(); + $pricesToStore = array_pop($tierPrices); + $pricesToDelete = []; + foreach ($tierPrices as $tierPrice) { + $tierPriceValue = $tierPrice->getExtensionAttributes()->getPercentageValue() + ?: $tierPrice->getValue(); + $priceType = $tierPrice->getExtensionAttributes()->getPercentageValue() + ? \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT + : \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED; + $customerGroup = $tierPrice->getCustomerGroupId() == \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID + ? 'NOT LOGGED IN' + : 'ALL GROUPS'; + $pricesToDelete[] = [ + 'price' => $tierPriceValue, + 'price_type' => $priceType, + 'website_id' => 0, + 'customer_group' => $customerGroup, + 'sku' => self::SIMPLE_PRODUCT_SKU, + 'quantity' => $tierPrice->getQty() + + ]; + } + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/products/tier-prices-delete', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Delete', + ], + ]; + $response = $this->_webApiCall($serviceInfo, ['prices' => $pricesToDelete]); + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices(); + $tierPrice = $tierPrices[0]; + $this->assertTrue($response); + $this->assertEquals(1, count($tierPrices)); + $this->assertEquals($pricesToStore, $tierPrice); + } + + /** + * Check prise exists and is correct. + * + * @param array $price + * @param array $tierPrices + * @return bool + */ + private function isPriceCorrect(array $price, array $tierPrices) + { + $isCorrect = false; + + foreach ($tierPrices as $tierPrice) { + $priceIsCorrect = $price['price_type'] === \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT + ? (float)$tierPrice->getExtensionAttributes()->getPercentageValue() === (float)$price['price'] + : (float)$tierPrice->getValue() === (float)$price['price']; + if ( + $priceIsCorrect + && (int)$tierPrice->getQty() === (int)$price['quantity'] + && $tierPrice->getExtensionAttributes()->getWebsiteId() == $price['website_id'] + ) { + $isCorrect = true; + } + } + + return $isCorrect; + } +} diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SuggestElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SuggestElement.php index 71d4a84b1161292b668899513878d6589e64d9e9..6eebcbc3529c1e191de837c3b03229828fe1b7d4 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SuggestElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SuggestElement.php @@ -60,6 +60,13 @@ class SuggestElement extends SimpleElement */ protected $closeButton = '[data-action="close-advanced-select"]'; + /** + * Searched count. + * + * @var string + */ + protected $searchedCount = '[class*=search-count]'; + /** * Set value. * @@ -77,6 +84,12 @@ class SuggestElement extends SimpleElement } $this->keys([$value]); $searchedItem = $this->find(sprintf($this->resultItem, $value), Locator::SELECTOR_XPATH); + $searchedCountElements = $this->find($this->searchedCount); + $this->waitUntil( + function () use ($searchedCountElements) { + return $searchedCountElements->isVisible() ? true : null; + } + ); $searchedItem->click(); $closeButton = $this->find($this->closeButton); if ($closeButton->isVisible()) { diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Indexer.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Indexer.php new file mode 100644 index 0000000000000000000000000000000000000000..1e066440c7c3d98da1803c7f5b8d793116be66d9 --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Indexer.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Mtf\Util\Command\Cli; + +use Magento\Mtf\Util\Command\Cli; + +/** + * Handle reindexing for tests executions. + */ +class Indexer extends Cli +{ + /** + * Parameter for reindex command. + */ + const PARAM_INDEXER_REINDEX = 'indexer:reindex'; + + /** + * Run reindex. + * + * @param array $indexes [optional] + * @return void + */ + public function reindex(array $indexes = []) + { + $params = ''; + if (!empty($indexes)) { + $params = implode(' ', $indexes); + } + parent::execute(Indexer::PARAM_INDEXER_REINDEX . ' ' . $params); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Menu.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Menu.php index 9a06c2c3c8e313d8e3585c4b03706d781b09eae4..2faac2a19d09c8b35134f1cb46865da111676d81 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Menu.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Menu.php @@ -100,4 +100,31 @@ class Menu extends Block $this->_rootElement->find($subMenuItem, Locator::SELECTOR_XPATH)->click(); $this->waitForElementNotVisible($subMenuSelector, Locator::SELECTOR_XPATH); } + + /** + * Check if menu item is visible. + * + * @param string $menuItem + * @return bool + */ + public function isMenuItemVisible($menuItem) + { + $menuChain = array_map('trim', explode('>', $menuItem)); + $mainMenu = $menuChain[0]; + $subMenu = isset($menuChain[1]) ? $menuChain[1] : null; + + $mainMenuElement = $this->_rootElement->find(sprintf($this->mainMenu, $mainMenu), Locator::SELECTOR_XPATH); + if (!$mainMenuElement->isVisible()) { + return false; + } + if ($subMenu === null) { + return true; + } + $mainMenuElement->click(); + + $subMenuSelector = sprintf($this->subMenu, $mainMenu); + $this->waitForElementVisible($subMenuSelector, Locator::SELECTOR_XPATH); + $subMenuItem = $subMenuSelector . sprintf($this->subMenuItem, $subMenu); + return $this->_rootElement->find($subMenuItem, Locator::SELECTOR_XPATH)->isVisible(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Messages.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Messages.php index 9c8b0beee9162c276f20ad81c15b51a6b4ea523b..4ac95b73d7f67bd1f5bcc75c8f7d58e3c4bc3fb2 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Messages.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Messages.php @@ -6,28 +6,13 @@ namespace Magento\Backend\Test\Block; -use Magento\Mtf\Block\Block; use Magento\Mtf\Client\Locator; /** - * Global messages block. + * Backend Messages block. */ -class Messages extends Block +class Messages extends \Magento\Ui\Test\Block\Messages { - /** - * Success message selector. - * - * @var string - */ - protected $successMessage = '[data-ui-id$=message-success]'; - - /** - * Last success message selector. - * - * @var string - */ - protected $lastSuccessMessage = '[data-ui-id$=message-success]:last-child'; - /** * Message link. * @@ -35,97 +20,6 @@ class Messages extends Block */ protected $messageLink = "//a[contains(.,'%s')]"; - /** - * Error message selector. - * - * @var string - */ - protected $errorMessage = '[data-ui-id$=message-error]'; - - /** - * Notice message selector. - * - * @var string - */ - protected $noticeMessage = '[data-ui-id$=message-notice]'; - - /** - * Warning message selector. - * - * @var string - */ - protected $warningMessage = '[data-ui-id$=message-warning]'; - - /** - * Wait for success message. - * - * @return bool - */ - public function waitSuccessMessage() - { - return $this->waitForElementVisible($this->successMessage, Locator::SELECTOR_CSS); - } - - /** - * Get all success messages which are present on the page. - * - * @return array - */ - public function getSuccessMessages() - { - $this->waitForElementVisible($this->successMessage); - $elements = $this->_rootElement->getElements($this->successMessage); - - $messages = []; - foreach ($elements as $element) { - $messages[] = $element->getText(); - } - - return $messages; - } - - /** - * Get last success message which is present on the page. - * - * @return string - */ - public function getSuccessMessage() - { - $this->waitForElementVisible($this->successMessage); - - return $this->_rootElement->find($this->lastSuccessMessage)->getText(); - } - - /** - * Wait for element is visible in the page. - * - * @param string $selector - * @param string $strategy - * @return bool|null - */ - public function waitForElementVisible($selector, $strategy = Locator::SELECTOR_CSS) - { - $browser = $this->browser; - return $browser->waitUntil( - function () use ($browser, $selector, $strategy) { - $message = $browser->find($selector, $strategy); - return $message->isVisible() ? true : null; - } - ); - } - - /** - * Get all error message which is present on the page. - * - * @return string - */ - public function getErrorMessage() - { - return $this->_rootElement - ->find($this->errorMessage, Locator::SELECTOR_CSS) - ->getText(); - } - /** * Click on link in the message which is present on the page. * @@ -175,26 +69,4 @@ class Messages extends Block { return $this->waitForElementVisible($this->noticeMessage, Locator::SELECTOR_CSS); } - - /** - * Get notice message which is present on the page. - * - * @return string - */ - public function getNoticeMessage() - { - $this->waitForElementVisible($this->noticeMessage); - return $this->_rootElement->find($this->noticeMessage)->getText(); - } - - /** - * Get warning message which is present on the page. - * - * @return string - */ - public function getWarningMessage() - { - $this->waitForElementVisible($this->warningMessage); - return $this->_rootElement->find($this->warningMessage)->getText(); - } } diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertMenuItemNotVisible.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertMenuItemNotVisible.php new file mode 100644 index 0000000000000000000000000000000000000000..181e88d1a98f43003ccb3b9a56793b02c19b4973 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertMenuItemNotVisible.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Backend\Test\Constraint; + +use Magento\Backend\Test\Page\Adminhtml\Dashboard; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert menu item availability. + */ +class AssertMenuItemNotVisible extends AbstractConstraint +{ + /** + * Assert that menu item is not visible in dashboard menu. + * + * @param Dashboard $dashboard + * @param string $menuItem + * @return void + */ + public function processAssert(Dashboard $dashboard, $menuItem) + { + $dashboard->open(); + + \PHPUnit_Framework_Assert::assertFalse( + $dashboard->getMenuBlock()->isMenuItemVisible($menuItem), + 'Menu item ' . $menuItem . ' is supposed to be not visible.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return 'Menu item is not visible in dashboard menu.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml index db800f2c9ea9e89be3218932ec959a3b03339b50..5f5c6036ddcd4eadd0148d09ff2cd6e9b114f925 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml @@ -7,6 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> <page name="Dashboard" area="Adminhtml" mca="admin/dashboard" module="Magento_Backend"> + <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator="#messages" strategy="css selector" /> <block name="adminPanelHeader" class="Magento\Backend\Test\Block\Page\Header" locator=".page-header" strategy="css selector" /> <block name="titleBlock" class="Magento\Theme\Test\Block\Html\Title" locator=".page-title-wrapper" strategy="css selector" /> <block name="mainBlock" class="Magento\Backend\Test\Block\Page\Main" locator=".dashboard-main" strategy="css selector" /> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml index 9274985a74edfa1ccfb7709ee979486733e8fe1b..d319a0c781bd0136b6fbe576d702222cb0dfd59a 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml @@ -230,6 +230,7 @@ </item> </field> </dataset> + <dataset name="custom_allowed_country_rollback"> <field name="general/country/allow" xsi:type="array"> <item name="scope" xsi:type="string">default</item> @@ -237,6 +238,7 @@ <item name="inherit" xsi:type="string">1</item> </field> </dataset> + <dataset name="enable_single_store_mode"> <field name="general/single_store_mode/enabled" xsi:type="array"> <item name="scope" xsi:type="string">default</item> @@ -245,6 +247,7 @@ <item name="value" xsi:type="number">1</item> </field> </dataset> + <dataset name="enable_single_store_mode_rollback"> <field name="general/single_store_mode/enabled" xsi:type="array"> <item name="scope" xsi:type="string">default</item> @@ -253,5 +256,25 @@ <item name="value" xsi:type="number">0</item> </field> </dataset> + + <dataset name="top_destinations_DE_ES_GB"> + <field name="general/country/destinations" xsi:type="array"> + <item name="scope" xsi:type="string">default</item> + <item name="scope_id" xsi:type="number">0</item> + <item name="value" xsi:type="array"> + <item name="Germany" xsi:type="string">DE</item> + <item name="Spain" xsi:type="string">ES</item> + <item name="United Kingdom" xsi:type="string">GB</item> + </item> + </field> + </dataset> + + <dataset name="top_destinations_DE_ES_GB_rollback"> + <field name="general/country/destinations" xsi:type="array"> + <item name="scope" xsi:type="string">default</item> + <item name="scope_id" xsi:type="number">0</item> + <item name="value" xsi:type="string"></item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Type/Bundle.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Type/Bundle.php index ccf47420b11521f9f5bf1a6c4b0f347d86c1baa0..042ae16dd2e53353595e478dad6c2d7ce712946e 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Type/Bundle.php +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Block/Catalog/Product/View/Type/Bundle.php @@ -21,6 +21,20 @@ use Magento\Mtf\Fixture\InjectableFixture; */ class Bundle extends Block { + /** + * Assigned product name. + * + * @var string + */ + protected $assignedProductName = '.product-name'; + + /** + * Assigned product price. + * + * @var string + */ + protected $assignedProductPrice = '.bundle-options-wrapper .price'; + /** * Selector for single option block * @@ -70,6 +84,20 @@ class Bundle extends Block */ protected $bundleOptionBlock = './/div[label[span[contains(text(), "%s")]]]'; + /** + * Product fixture. + * + * @var FixtureInterface + */ + private $product; + + /** + * Option index. + * + * @var null|int + */ + protected $optionIndex; + /** * Fill bundle option on frontend add click "Add to cart" button * @@ -93,17 +121,19 @@ class Bundle extends Block public function getOptions(FixtureInterface $product) { /** @var BundleProduct $product */ + $this->product = $product; $bundleSelections = $product->getBundleSelections(); $bundleOptions = isset($bundleSelections['bundle_options']) ? $bundleSelections['bundle_options'] : []; $listFormOptions = $this->getListOptions(); $formOptions = []; - foreach ($bundleOptions as $option) { + foreach ($bundleOptions as $index => $option) { $title = $option['title']; if (!isset($listFormOptions[$title])) { throw new \Exception("Can't find option: \"{$title}\""); } + $this->optionIndex = $index; /** @var SimpleElement $optionElement */ $optionElement = $listFormOptions[$title]; @@ -118,10 +148,20 @@ class Bundle extends Block $formOptions[] = $optionData; } - return $formOptions; } + /** + * Check if bundle option is visible. + * + * @param string $optionTitle + * @return bool + */ + public function isOptionVisible($optionTitle) + { + return isset($this->getListOptions()[$optionTitle]); + } + /** * Get list options * @@ -151,6 +191,9 @@ class Bundle extends Block */ protected function getDropdownData(SimpleElement $option) { + if ($this->isOneProductInStock($this->product)) { + return ['options' => $this->getFlatTextData()]; + } $select = $option->find($this->selectOption, Locator::SELECTOR_XPATH, 'select'); // Skip "Choose option ..."(option #1) return $this->getSelectOptionsData($select, 2); @@ -263,13 +306,16 @@ class Bundle extends Block { foreach ($bundleOptions as $option) { $selector = sprintf($this->bundleOptionBlock, $option['title']); - /** @var Option $optionBlock */ - $optionBlock = $this->blockFactory->create( - 'Magento\Bundle\Test\Block\Catalog\Product\View\Type\Option\\' - . $this->optionNameConvert($option['frontend_type']), - ['element' => $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)] - ); - $optionBlock->fillOption($option['value']); + $useDefault = isset($option['use_default']) && strtolower($option['use_default']) == 'true' ? true : false; + if (!$useDefault) { + /** @var Option $optionBlock */ + $optionBlock = $this->blockFactory->create( + 'Magento\Bundle\Test\Block\Catalog\Product\View\Type\Option\\' + . $this->optionNameConvert($option['frontend_type']), + ['element' => $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)] + ); + $optionBlock->fillOption($option['value']); + } } } @@ -284,4 +330,43 @@ class Bundle extends Block $trimmedOptionType = preg_replace('/[^a-zA-Z]/', '', $optionType); return ucfirst(strtolower($trimmedOptionType)); } + + /** + * Check count products with 'In Stock' status. + * + * @param BundleProduct $products + * @return bool + */ + private function isOneProductInStock(BundleProduct $products) + { + $result = []; + $products = $products->getBundleSelections()['products'][$this->optionIndex]; + foreach ($products as $product) { + $status = $product->getData()['quantity_and_stock_status']['is_in_stock']; + if ($status == 'In Stock') { + $result[] = $product; + } + } + if (count($result) == 1) { + return true; + } + return false; + } + + /** + * Return list options. + * + * @return array + */ + private function getFlatTextData() + { + $productPrice = $this->_rootElement->find($this->assignedProductPrice)->getText(); + $productPrice = preg_replace("/[^0-9.,]/", '', $productPrice); + $productName = $this->_rootElement->find($this->assignedProductName)->getText(); + $options[$productName] = [ + 'title' => $productName, + 'price' => number_format($productPrice, 2) + ]; + return $options; + } } diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleItemsOnProductPage.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleItemsOnProductPage.php index 411c53d982fcb8a2445e8a5d214383670ec7bf4a..930758c50316a66eef00f3a5bea8c812ce519902 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleItemsOnProductPage.php +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleItemsOnProductPage.php @@ -65,15 +65,18 @@ class AssertBundleItemsOnProductPage extends AbstractAssertForm 'is_require' => $bundleOption['required'], ]; + $key = 0; foreach ($bundleOption['assigned_products'] as $productKey => $assignedProduct) { - $price = isset($assignedProduct['data']['selection_price_value']) - ? $assignedProduct['data']['selection_price_value'] - : $bundleSelections['products'][$optionKey][$productKey]->getPrice(); + if ($this->isInStock($product, $key++)) { + $price = isset($assignedProduct['data']['selection_price_value']) + ? $assignedProduct['data']['selection_price_value'] + : $bundleSelections['products'][$optionKey][$productKey]->getPrice(); - $optionData['options'][$productKey] = [ - 'title' => $assignedProduct['search_data']['name'], - 'price' => number_format($price, 2), - ]; + $optionData['options'][$productKey] = [ + 'title' => $assignedProduct['search_data']['name'], + 'price' => number_format($price, 2), + ]; + } } $result[$optionKey] = $optionData; @@ -82,6 +85,24 @@ class AssertBundleItemsOnProductPage extends AbstractAssertForm return $result; } + /** + * Check product attribute 'is_in_stock'. + * + * @param BundleProduct $product + * @param int $key + * @return bool + */ + private function isInStock(BundleProduct $product, $key) + { + $assignedProducts = $product->getBundleSelections()['products'][0]; + $status = $assignedProducts[$key]->getData()['quantity_and_stock_status']['is_in_stock']; + + if ($status == 'In Stock') { + return true; + } + return false; + } + /** * Return Text if displayed on frontend equals with fixture. * diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleOptionTitleOnStorefront.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleOptionTitleOnStorefront.php new file mode 100644 index 0000000000000000000000000000000000000000..cc89daa9d301fa6cdf3f1687576fd2554c6e46ba --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Constraint/AssertBundleOptionTitleOnStorefront.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Bundle\Test\Constraint; + +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\FixtureInterface; +use Magento\Cms\Test\Page\CmsIndex; + +/** + * Assert that option title is correct in different stores on bundle product page. + */ +class AssertBundleOptionTitleOnStorefront extends AbstractConstraint +{ + /** + * Assert that option title is correct on product view page. + * + * @param CatalogProductView $catalogProductView + * @param CmsIndex $cmsIndex + * @param BrowserInterface $browser + * @param FixtureInterface $originalProduct + * @param array $stores + * @param array $optionTitles + * @return void + */ + public function processAssert( + CatalogProductView $catalogProductView, + CmsIndex $cmsIndex, + BrowserInterface $browser, + FixtureInterface $originalProduct, + array $stores, + array $optionTitles + ) { + $cmsIndex->open(); + $cmsIndex->getLinksBlock()->waitWelcomeMessage(); + foreach ($stores as $store) { + $cmsIndex->getStoreSwitcherBlock()->selectStoreView($store->getName()); + $cmsIndex->getLinksBlock()->waitWelcomeMessage(); + $browser->open($_ENV['app_frontend_url'] . $originalProduct->getUrlKey() . '.html'); + $catalogProductView->getBundleViewBlock()->clickCustomize(); + \PHPUnit_Framework_Assert::assertTrue( + $catalogProductView->getBundleViewBlock()->getBundleBlock()->isOptionVisible( + $optionTitles[$store->getStoreId()] + ), + sprintf( + 'Option with title \'%s\' is missing in \'%s\' store view.', + $optionTitles[$store->getStoreId()], + $store->getName() + ) + ); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Option title is correct on product view page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Fixture/BundleProduct/BundleSelections.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/Fixture/BundleProduct/BundleSelections.php index f72258c45ad35454be4803c83ccadd6c7dd8f9e7..d98486ffe4646d6664e6c76f7d7bd03d5368b5a7 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Fixture/BundleProduct/BundleSelections.php +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Fixture/BundleProduct/BundleSelections.php @@ -98,4 +98,14 @@ class BundleSelections extends DataSource } } } + + /** + * Get products from bundle items. + * + * @return array + */ + public function getProducts() + { + return $this->data['products']; + } } diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct.xml index 25c9e2693258800c04303db31e703247e6507d3f..346c727e6b9f8dfc4963bfc42ee6601587e37ae1 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct.xml @@ -29,6 +29,28 @@ </field> </dataset> + <dataset name="default_with_one_simple_product"> + <field name="name" xsi:type="string">Bundle dynamic product %isolation%</field> + <field name="url_key" xsi:type="string">bundle-dynamic-product-%isolation%</field> + <field name="sku" xsi:type="string">sku_bundle_dynamic_product_%isolation%</field> + <field name="sku_type" xsi:type="string">Yes</field> + <field name="price_type" xsi:type="string">Yes</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="dataset" xsi:type="string">bundle_dynamic_with_category</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="bundle_selections" xsi:type="array"> + <item name="dataset" xsi:type="string">one_simple_product</item> + </field> + </dataset> + <dataset name="bundle_dynamic_product"> <field name="name" xsi:type="string">Bundle dynamic product %isolation%</field> <field name="sku" xsi:type="string">sku_bundle_dynamic_product_%isolation%</field> @@ -254,6 +276,47 @@ </field> </dataset> + <dataset name="bundle_fixed_100_dollar_product_buy_all"> + <field name="name" xsi:type="string">Bundle fixed product %isolation%</field> + <field name="sku" xsi:type="string">sku_bundle_fixed_product_%isolation%</field> + <field name="sku_type" xsi:type="string">No</field> + <field name="price_type" xsi:type="string">No</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">100</item> + <item name="dataset" xsi:type="string">fixed_100_dollar</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="weight" xsi:type="string">1</field> + <field name="weight_type" xsi:type="string">No</field> + <field name="status" xsi:type="string">Yes</field> + <field name="shipment_type" xsi:type="string">Together</field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="stock_data" xsi:type="array"> + <item name="manage_stock" xsi:type="string">Yes</item> + <item name="use_config_enable_qty_increments" xsi:type="string">Yes</item> + <item name="use_config_qty_increments" xsi:type="string">Yes</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="url_key" xsi:type="string">bundle-fixed-product-%isolation%</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="bundle_selections" xsi:type="array"> + <item name="dataset" xsi:type="string">fixed_100_dollar_buy_all</item> + </field> + <field name="attribute_set_id" xsi:type="string">Default</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">bundle_fixed_100_dollar_buy_all</item> + </field> + </dataset> + <dataset name="with_3_bundle_options"> <field name="name" xsi:type="string">Bundle with 3 options %isolation%</field> <field name="url_key" xsi:type="string">with_3_bundle_options-%isolation%</field> @@ -275,5 +338,46 @@ <item name="dataset" xsi:type="string">with_3_options</item> </field> </dataset> + + <dataset name="bundle_low_stock"> + <field name="name" xsi:type="string">Bundle low stock product %isolation%</field> + <field name="sku" xsi:type="string">sku_bundle_low_stock_product_%isolation%</field> + <field name="sku_type" xsi:type="string">No</field> + <field name="price_type" xsi:type="string">No</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">750</item> + <item name="dataset" xsi:type="string">default_fixed</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="weight" xsi:type="string">1</field> + <field name="weight_type" xsi:type="string">No</field> + <field name="status" xsi:type="string">Yes</field> + <field name="shipment_type" xsi:type="string">Together</field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="stock_data" xsi:type="array"> + <item name="manage_stock" xsi:type="string">Yes</item> + <item name="use_config_enable_qty_increments" xsi:type="string">Yes</item> + <item name="use_config_qty_increments" xsi:type="string">Yes</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="url_key" xsi:type="string">bundle-fixed-product-%isolation%</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="bundle_selections" xsi:type="array"> + <item name="dataset" xsi:type="string">low_stock_fixed</item> + </field> + <field name="attribute_set_id" xsi:type="string">Default</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">bundle_low_stock_fixed</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/BundleSelections.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/BundleSelections.xml index a297891ba085ce99ea3391176efa38a8d325e775..8112237bfc31183e9d772dd4df85fdd3ca1e6639 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/BundleSelections.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/BundleSelections.xml @@ -42,6 +42,31 @@ </field> </dataset> + <dataset name="one_simple_product"> + <field name="bundle_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Drop-down Option</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="required" xsi:type="string">Yes</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_qty" xsi:type="string">1</item> + </item> + </item> + </item> + </item> + </field> + <field name="products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + </item> + </field> + </dataset> + <dataset name="default_fixed"> <field name="bundle_options" xsi:type="array"> <item name="0" xsi:type="array"> @@ -81,6 +106,44 @@ </field> </dataset> + <dataset name="fixed_with_custom_title"> + <field name="bundle_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Custom Option %isolation%</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="required" xsi:type="string">Yes</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_price_value" xsi:type="string">5.00</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + <item name="selection_qty" xsi:type="string">1</item> + </item> + </item> + <item name="1" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_price_value" xsi:type="string">6.00</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + <item name="selection_qty" xsi:type="string">1</item> + </item> + </item> + </item> + </item> + </field> + <field name="products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + <item name="1" xsi:type="string">catalogProductSimple::product_100_dollar</item> + </item> + </field> + </dataset> + <dataset name="fixed_100_dollar"> <field name="bundle_options" xsi:type="array"> <item name="0" xsi:type="array"> @@ -120,6 +183,44 @@ </field> </dataset> + <dataset name="fixed_100_dollar_buy_all"> + <field name="bundle_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Drop-down Option</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="required" xsi:type="string">Yes</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_price_value" xsi:type="string">10.00</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + <item name="selection_qty" xsi:type="string">1</item> + </item> + </item> + <item name="1" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_price_value" xsi:type="string">560.00</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + <item name="selection_qty" xsi:type="string">1</item> + </item> + </item> + </item> + </item> + </field> + <field name="products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default_qty_1</item> + <item name="1" xsi:type="string">catalogProductSimple::out_of_stock</item> + </item> + </field> + </dataset> + <dataset name="second"> <field name="bundle_options" xsi:type="array"> <item name="0" xsi:type="array"> @@ -927,5 +1028,43 @@ </item> </field> </dataset> + + <dataset name="low_stock_fixed"> + <field name="bundle_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Drop-down Option</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="required" xsi:type="string">Yes</item> + <item name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_price_value" xsi:type="string">5.00</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + <item name="selection_qty" xsi:type="string">1</item> + </item> + </item> + <item name="1" xsi:type="array"> + <item name="search_data" xsi:type="array"> + <item name="name" xsi:type="string">%product_name%</item> + </item> + <item name="data" xsi:type="array"> + <item name="selection_price_value" xsi:type="string">6.00</item> + <item name="selection_price_type" xsi:type="string">Fixed</item> + <item name="selection_qty" xsi:type="string">1</item> + </item> + </item> + </item> + </item> + </field> + <field name="products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + <item name="1" xsi:type="string">catalogProductSimple::low_stock_product</item> + </item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/CheckoutData.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/CheckoutData.xml index e694b65110e89cd1840ad1249e6c14a61302a877..8a17e1b9de596b0f5063a4785f90abeebea22e5a 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/CheckoutData.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/Repository/BundleProduct/CheckoutData.xml @@ -85,6 +85,27 @@ </field> </dataset> + <dataset name="bundle_fixed_100_dollar_buy_all"> + <field name="options" xsi:type="array"> + <item name="bundle_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Drop-down Option</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="use_default" xsi:type="string">true</item> + <item name="value" xsi:type="array"> + <item name="name" xsi:type="string">default_qty_1</item> + </item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">1</field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">110</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">110</item> + </field> + </dataset> + <dataset name="bundle_update_mini_shopping_cart"> <field name="options" xsi:type="array"> <item name="bundle_options" xsi:type="array"> @@ -396,6 +417,26 @@ </field> </dataset> + <dataset name="bundle_low_stock_fixed"> + <field name="options" xsi:type="array"> + <item name="bundle_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">Drop-down Option</item> + <item name="type" xsi:type="string">Drop-down</item> + <item name="value" xsi:type="array"> + <item name="name" xsi:type="string">low_stock_product</item> + </item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">1</field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">756</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">756</item> + </field> + </dataset> + <dataset name="bundle_required_three_fixed_options_with_qty"> <field name="options" xsi:type="array"> <item name="bundle_options" xsi:type="array"> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/CreateBundleProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/CreateBundleProductEntityTest.xml index 56b2ceb917fe6d46296e408954bdfc88380af5a3..98f7ac4b0cdbd9a25e33f6e7ef0a450df34bbdae 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/CreateBundleProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/CreateBundleProductEntityTest.xml @@ -74,8 +74,7 @@ <constraint name="Magento\Bundle\Test\Constraint\AssertBundlePriceView" /> <constraint name="Magento\Bundle\Test\Constraint\AssertBundlePriceType" /> </variation> - <variation name="CreateBundleProductEntityTestVariation4"> - <data name="tag" xsi:type="string">stable:no</data> + <variation name="CreateBundleProductEntityTestVariation4" summary="Create bundle product with out of stock simple product" ticketId="MAGETWO-12801"> <data name="description" xsi:type="string">Create fixed bundle</data> <data name="product/data/url_key" xsi:type="string">bundle-product-%isolation%</data> <data name="product/data/name" xsi:type="string">BundleProduct %isolation%</data> @@ -89,7 +88,7 @@ <data name="product/data/weight" xsi:type="string">10</data> <data name="product/data/description" xsi:type="string">Bundle Product Fixed Required</data> <data name="product/data/bundle_selections/dataset" xsi:type="string">default_fixed</data> - <data name="product/data/bundle_selections/products" xsi:type="string">catalogProductSimple::product_100_dollar,catalogProductVirtual::product_50_dollar</data> + <data name="product/data/bundle_selections/products" xsi:type="string">catalogProductSimple::product_100_dollar,catalogProductSimple::out_of_stock</data> <data name="product/data/checkout_data/dataset" xsi:type="string">bundle_default</data> <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductInGrid" /> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleProductEntityTest.php b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleProductEntityTest.php index 8326b1ba4d9ae9b0589b1d55bd973f9db6efe970..a8b0f49f030c1bccc5ce6c0aa5dfe38aff785b1d 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleProductEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleProductEntityTest.php @@ -7,9 +7,11 @@ namespace Magento\Bundle\Test\TestCase; use Magento\Bundle\Test\Fixture\BundleProduct; +use Magento\Store\Test\Fixture\Store; use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit; use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\Fixture\FixtureFactory; /** * Test Flow: @@ -50,47 +52,84 @@ class UpdateBundleProductEntityTest extends Injectable protected $catalogProductEdit; /** - * Injection data + * Fixture Factory. + * + * @var FixtureFactory + */ + private $fixtureFactory; + + /** + * Injection data. * * @param CatalogProductIndex $catalogProductIndexNewPage * @param CatalogProductEdit $catalogProductEditPage + * @param FixtureFactory $fixtureFactory * @return void */ public function __inject( CatalogProductIndex $catalogProductIndexNewPage, - CatalogProductEdit $catalogProductEditPage + CatalogProductEdit $catalogProductEditPage, + FixtureFactory $fixtureFactory ) { $this->catalogProductIndex = $catalogProductIndexNewPage; $this->catalogProductEdit = $catalogProductEditPage; + $this->fixtureFactory = $fixtureFactory; } /** - * Test update bundle product + * Test update bundle product. * * @param BundleProduct $product * @param BundleProduct $originalProduct + * @param Store|null $store * @return array */ - public function test(BundleProduct $product, BundleProduct $originalProduct) - { - $this->markTestIncomplete('MAGETWO-56584: [FT] Custom options are not created for product in test'); + public function test( + BundleProduct $product, + BundleProduct $originalProduct, + Store $store = null + ) { // Preconditions $originalProduct->persist(); - $originalCategory = $originalProduct->hasData('category_ids') - ? $originalProduct->getDataFieldConfig('category_ids')['source']->getCategories() - : null; - $category = $product->hasData('category_ids') - ? $product->getDataFieldConfig('category_ids')['source']->getCategories() - : $originalCategory; + $category = $this->getCategories($originalProduct, $product); + + if ($store) { + $store->persist(); + $optionTitle[$store->getStoreId()] = $product->getBundleSelections()['bundle_options'][0]['title']; + } // Steps $filter = ['sku' => $originalProduct->getSku()]; $this->catalogProductIndex->open(); $this->catalogProductIndex->getProductGrid()->searchAndOpen($filter); + if ($store) { + $this->catalogProductEdit->getFormPageActions()->changeStoreViewScope($store); + } $this->catalogProductEdit->getProductForm()->fill($product); $this->catalogProductEdit->getFormPageActions()->save(); - return ['category' => $category]; + return [ + 'category' => $category, + 'stores' => isset($store) ? [$store] : [], + 'optionTitles' => isset($optionTitle) ? $optionTitle : [] + ]; + } + + /** + * Get Category instances. + * + * @param BundleProduct $originalProduct + * @param BundleProduct $product + * @return array + */ + protected function getCategories(BundleProduct $originalProduct, BundleProduct $product) + { + $originalCategory = $originalProduct->hasData('category_ids') + ? $originalProduct->getDataFieldConfig('category_ids')['source']->getCategories() + : null; + return $product->hasData('category_ids') + ? $product->getDataFieldConfig('category_ids')['source']->getCategories() + : $originalCategory; } } diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleProductEntityTest.xml index e4f567de39481249ca083398152f237c35b3c2cf..958e074b91b8694153caa9df7f56cad4df87f310 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/UpdateBundleProductEntityTest.xml @@ -83,5 +83,12 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> <constraint name="Magento\Bundle\Test\Constraint\AssertBundleProductPage" /> </variation> + <variation name="UpdateBundleProductEntityTestVariation6" summary="Verify data overrirding on Store View level" ticketId="MAGETWO-50642"> + <data name="originalProduct/dataset" xsi:type="string">bundle_fixed_product</data> + <data name="store/dataset" xsi:type="string">custom</data> + <data name="product/data/bundle_selections/dataset" xsi:type="string">fixed_with_custom_title</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> + <constraint name="Magento\Bundle\Test\Constraint\AssertBundleOptionTitleOnStorefront" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/PageActions.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/PageActions.php index 808da58c7dd4995a20f88ef2059df2ecc9eb574f..73a5f73a47e29df8dcc175a62d875a79d2d72b7d 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/PageActions.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/PageActions.php @@ -17,7 +17,7 @@ class PageActions extends FormPageActions /** * Top page element to implement a scrolling in case of floating blocks overlay. */ - const TOP_ELEMENT_TO_SCROLL = '.page-title'; + const TOP_ELEMENT_TO_SCROLL = 'header.page-header'; /** * Locator for "OK" button in warning block @@ -62,7 +62,7 @@ class PageActions extends FormPageActions */ public function selectStoreView($name) { - $this->browser->find(self::TOP_ELEMENT_TO_SCROLL)->click(); + $this->browser->find(self::TOP_ELEMENT_TO_SCROLL)->hover(); $this->_rootElement->find($this->storeChangeButton)->click(); $this->waitForElementVisible($name, Locator::SELECTOR_LINK_TEXT); $this->_rootElement->find($name, Locator::SELECTOR_LINK_TEXT)->click(); diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php index 447e1319898084dfecbcb50e3f801ae04ae9f156..6a89b1ab5fdd81dbed7f956070cd5d7c6888be79 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php @@ -176,6 +176,7 @@ class Tree extends Block sprintf($this->categoryInTree, $parentCategoryName), Locator::SELECTOR_XPATH ); + $targetElement->hover(); $this->_rootElement->find(sprintf($this->categoryInTree, $childCategoryName), Locator::SELECTOR_XPATH) ->dragAndDrop($targetElement); } @@ -185,7 +186,7 @@ class Tree extends Block * * @return void */ - protected function expandAllCategories() + public function expandAllCategories() { $this->_rootElement->find($this->expandAll)->click(); } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Attribute/Grid.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Attribute/Grid.php index 8f64af8ac0900460a77ce158ea26e24bfe022911..db078d4500d47776fba597bef437ccd4093d58cb 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Attribute/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Attribute/Grid.php @@ -15,14 +15,14 @@ use Magento\Backend\Test\Block\Widget\Grid as AbstractGrid; class Grid extends AbstractGrid { /** - * Locator value for link in action column + * Locator value for link in action column. * * @var string */ protected $editLink = 'td.col-frontend_label'; /** - * Filters array mapping + * Filters array mapping. * * @var array */ diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/ProductDetails/CategoryIds.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/ProductDetails/CategoryIds.php index 371186cea6c54147e1c5105397c04a1c714df9b3..b9c4e30605cb8bdf2616e0393d4388a6bfbd7ceb 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/ProductDetails/CategoryIds.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/ProductDetails/CategoryIds.php @@ -68,16 +68,32 @@ class CategoryIds extends MultisuggestElement /** * Set category value. * - * @param array|string $value + * @param array|string $values * @return void */ - public function setValue($value) + public function setValue($values) { // Align Category ids select element to the center of the browser for created categories if ($this->browser->find($this->pageFooter)->isVisible()) { $this->browser->find($this->pageFooter)->hover(); $this->browser->find($this->advancedInventoryButton)->hover(); } - parent::setValue($value); + $this->eventManager->dispatchEvent(['set_value'], [__METHOD__, $this->getAbsoluteSelector()]); + + $this->clear(); + foreach ((array)$values as $value) { + if (!$this->isChoice($value)) { + if ($value == '') { + continue; + } + $this->keys([$value]); + $searchedItem = $this->find(sprintf($this->resultItem, $value), Locator::SELECTOR_XPATH); + $searchedItem->click(); + $closeButton = $this->find($this->closeButton); + if ($closeButton->isVisible()) { + $closeButton->click(); + } + } + } } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/FormPageActions.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/FormPageActions.php index decc7996a7e1e7e97c46fb5d711deb7005dd4bcc..2b83cd83009787f5994ba53eaa5dd5e78eb46d1b 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/FormPageActions.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/FormPageActions.php @@ -58,6 +58,27 @@ class FormPageActions extends ParentFormPageActions */ private $addAttribute = '[data-ui-id="addattribute-button"]'; + /** + * Default store switcher block locator. + * + * @var string + */ + private $storeSwitcherBlock = '.store-switcher'; + + /** + * Dropdown block locator. + * + * @var string + */ + private $dropdownBlock = '.dropdown'; + + /** + * Selector for confirm. + * + * @var string + */ + private $confirmModal = '.confirm._show[data-role=modal]'; + /** * Click on "Save" button. * @@ -122,4 +143,26 @@ class FormPageActions extends ParentFormPageActions { $this->_rootElement->find($this->addAttribute)->click(); } + + /** + * Change Store View scope. + * + * @param FixtureInterface $store + * @return void + */ + public function changeStoreViewScope(FixtureInterface $store) + { + $this->waitForElementNotVisible($this->spinner); + $this->waitForElementVisible($this->storeSwitcherBlock); + $this->_rootElement->find($this->storeSwitcherBlock) + ->find($this->dropdownBlock, Locator::SELECTOR_CSS, 'liselectstore') + ->setValue(sprintf('%s/%s', $store->getGroupId(), $store->getName())); + $modalElement = $this->browser->find($this->confirmModal); + /** @var \Magento\Ui\Test\Block\Adminhtml\Modal $modal */ + $modal = $this->blockFactory->create( + \Magento\Ui\Test\Block\Adminhtml\Modal::class, + ['element' => $modalElement] + ); + $modal->acceptAlert(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php index 08d38f45c4ff99a4eae99a1be3c3972bdba7f790..4ef9203827bbc6180b07c4d6f85814dd5f33581f 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php @@ -96,13 +96,17 @@ class ProductForm extends FormSections $this->callRender($typeId, 'fill', $renderArguments); } else { $sections = $this->getFixtureFieldsByContainers($product); - $category = $product->hasData('category_ids') - ? $product->getDataFieldConfig('category_ids')['source']->getCategories()[0] : $category; - if ($category) { - if ((int)$category->getId()) { - $sections['product-details']['category_ids']['value'] = $category->getName(); - } else { - $this->getNewCategoryModalForm()->addNewCategory($category); + if ($product->hasData('category_ids') || $category) { + $sections['product-details']['category_ids']['value'] = []; + $categories = $product->hasData('category_ids') + ? $product->getDataFieldConfig('category_ids')['source']->getCategories() + : [$category]; + foreach ($categories as $category) { + if ((int)$category->getId()) { + $sections['product-details']['category_ids']['value'][] = $category->getName(); + } else { + $this->getNewCategoryModalForm()->addNewCategory($category); + } } } $this->fillContainers($sections, $element); diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.xml index 3b66164ebe658097e05fdba2e8b53c6cb3d643d2..7e5070e4f154f5fc284b8b6805c19689a5fc2337 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.xml @@ -53,6 +53,10 @@ <country_of_manufacture> <input>select</input> </country_of_manufacture> + <use_default_name> + <selector>[name="use_default[name]"]</selector> + <input>checkbox</input> + </use_default_name> </fields> </product-details> <advanced-pricing> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ListProduct.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ListProduct.php index 2584f437bff755322c5033b50545d4c290457087..7c957a6d584b5630176fcc508598087e1f078ac9 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ListProduct.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ListProduct.php @@ -77,6 +77,11 @@ class ListProduct extends Block */ public function getSortByValues() { - return explode("\n", $this->_rootElement->find($this->sorter)->getText()); + $values = explode("\n", $this->_rootElement->find($this->sorter)->getText()); + $result = []; + foreach ($values as $value) { + $result[] = trim($value); + } + return $result; } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Search.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Search.php index 2fe1fecc99333986ef5a9146ff86f34d3e627f67..2e66a1c15f833e2bb07680460f685cdd59e81c11 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Search.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Search.php @@ -68,12 +68,14 @@ class Search extends Block * Perform search by a keyword. * * @param string $keyword + * @param string|null $length * @return void - * - * @SuppressWarnings(PHPMD.ConstructorWithNameAsEnclosingClass) */ - public function search($keyword) + public function search($keyword, $length = null) { + if ($length) { + $keyword = substr($keyword, 0, $length); + } $this->fillSearch($keyword); $this->_rootElement->find($this->searchButton)->click(); } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryMovedMessage.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryMovedMessage.php new file mode 100644 index 0000000000000000000000000000000000000000..3f2a84a91a7f5cc88259ed9912d8619e336884b6 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryMovedMessage.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Constraint; + +use Magento\Catalog\Test\Page\Adminhtml\CatalogCategoryEdit; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that success message is displayed after category moving. + */ +class AssertCategoryMovedMessage extends AbstractConstraint +{ + /** + * Success category save message. + */ + const SUCCESS_MESSAGE = 'You moved the category.'; + + /** + * Assert that success message is displayed after category moving. + * + * @param CatalogCategoryEdit $catalogCategoryEdit + * @return void + */ + public function processAssert(CatalogCategoryEdit $catalogCategoryEdit) + { + $actualMessage = $catalogCategoryEdit->getMessagesBlock()->getSuccessMessage(); + \PHPUnit_Framework_Assert::assertEquals( + self::SUCCESS_MESSAGE, + $actualMessage, + 'Wrong success message is displayed.' + . "\nExpected: " . self::SUCCESS_MESSAGE + . "\nActual: " . $actualMessage + ); + } + + /** + * Success message is displayed. + * + * @return string + */ + public function toString() + { + return 'Success message is displayed.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryOnCustomWebsite.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryOnCustomWebsite.php new file mode 100644 index 0000000000000000000000000000000000000000..e3868b86372d6d3add02b06a29d0056171f67221 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryOnCustomWebsite.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Constraint; + +use Magento\Catalog\Test\Fixture\Category; +use Magento\Catalog\Test\Page\Category\CatalogCategoryView; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\FixtureFactory; + +/** + * Assert category name on custom website store. + */ +class AssertCategoryOnCustomWebsite extends AbstractConstraint +{ + /** + * Assert that category name is correct on custom website store. + * + * @param FixtureFactory $fixtureFactory + * @param BrowserInterface $browser + * @param CatalogCategoryView $categoryView + * @param Category $category + * @return void + */ + public function processAssert( + FixtureFactory $fixtureFactory, + BrowserInterface $browser, + CatalogCategoryView $categoryView, + Category $category + ) { + $storeGroup = $fixtureFactory->createByCode( + 'storeGroup', + [ + 'dataset' => 'custom_new_group', + 'data' => [ + 'root_category_id' => [ + 'category' => $category->getDataFieldConfig('parent_id')['source']->getParentCategory() + ] + ] + ] + ); + $storeGroup->persist(); + $store = $fixtureFactory->createByCode( + 'store', + [ + 'dataset' => 'custom_store', + 'data' => [ + 'group_id' => [ + 'storeGroup' => $storeGroup + ] + ] + ] + ); + $store->persist(); + + $websiteCode = $storeGroup->getDataFieldConfig('website_id')['source']->getWebsite()->getData('code'); + $browser->open($_ENV['app_frontend_url'] . 'websites/' . $websiteCode . '/' . $category->getName() . '.html'); + \PHPUnit_Framework_Assert::assertEquals( + $category->getName(), + $categoryView->getTitleBlock()->getTitle(), + 'Wrong category name is displayed on custom website store.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Category name is correct on custom website store.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductNameOnDifferentStoreViews.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductNameOnDifferentStoreViews.php new file mode 100644 index 0000000000000000000000000000000000000000..bf3fe9911d331037f0501b77778451d08deb0cbf --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductNameOnDifferentStoreViews.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Constraint; + +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\FixtureInterface; +use Magento\Cms\Test\Page\CmsIndex; + +/** + * Assert product name in different store views on product view page. + */ +class AssertProductNameOnDifferentStoreViews extends AbstractConstraint +{ + /** + * Assert that product name is correct on the storefront in different store views. + * + * @param CatalogProductView $catalogProductView + * @param CmsIndex $cmsIndex + * @param BrowserInterface $browser + * @param FixtureInterface $initialProduct + * @param array $stores + * @param array $productNames + * @return void + */ + public function processAssert( + CatalogProductView $catalogProductView, + CmsIndex $cmsIndex, + BrowserInterface $browser, + FixtureInterface $initialProduct, + array $stores, + array $productNames + ) { + $browser->open($_ENV['app_frontend_url'] . $initialProduct->getUrlKey() . '.html'); + foreach ($stores as $store) { + $cmsIndex->getStoreSwitcherBlock()->selectStoreView($store->getName()); + $cmsIndex->getLinksBlock()->waitWelcomeMessage(); + \PHPUnit_Framework_Assert::assertEquals( + $productNames[$store->getStoreId()], + $catalogProductView->getViewBlock()->getProductName(), + sprintf('Wrong product name is displayed for %s store view.', $store->getName()) + ); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Product name is correct on the storefront'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductSaveMessage.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductSaveMessage.php index 5813850ef45d49ece29518a13f4a9f1d0f9059cf..8bfcb394a1fdfa761592dd803925be9b8ce44b86 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductSaveMessage.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductSaveMessage.php @@ -15,12 +15,12 @@ use Magento\Mtf\Constraint\AbstractConstraint; class AssertProductSaveMessage extends AbstractConstraint { /** - * Text value to be checked + * Text value to be checked. */ const SUCCESS_MESSAGE = 'You saved the product.'; /** - * Assert that success message is displayed after product save + * Assert that success message is displayed after product save. * * @param CatalogProductEdit $productPage * @return void diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/CatalogProductSimple.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/CatalogProductSimple.xml index e8ceb1d0889247805746a401f6c60bb5a0556010..f37ba686ae6889b0e16dbf17a64f812ae5a32b0d 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/CatalogProductSimple.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/CatalogProductSimple.xml @@ -47,6 +47,7 @@ <field name="msrp" is_required="0" /> <field name="msrp_display_actual_price_type" is_required="0" /> <field name="name" is_required="1" group="product-details" /> + <field name="use_default_name" group="product-details" /> <field name="old_id" is_required="0" /> <field name="options_container" is_required="0" /> <field name="page_layout" is_required="0" /> @@ -70,6 +71,7 @@ <field name="url_path" is_required="0" /> <field name="visibility" is_required="0" group="product-details" /> <field name="weight" is_required="0" group="product-details" /> + <field name="additional_attributes" is_required="0" group="product-details" /> <field name="id" group="null" /> <field name="type_id" /> <field name="attribute_set_id" group="product-details" source="Magento\Catalog\Test\Fixture\Product\AttributeSetId" /> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category/CategoryProducts.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category/CategoryProducts.php index aa3f2138fbe417bd40cfe78750349631359884b0..5c1093ad953e49a38fd125d3a84dd543e8377d7b 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category/CategoryProducts.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category/CategoryProducts.php @@ -33,18 +33,24 @@ class CategoryProducts extends DataSource if (!empty($data['dataset']) && $data['dataset'] !== '-') { $dataset = array_map('trim', explode(',', $data['dataset'])); foreach ($dataset as $value) { - $explodeValue = explode('::', $value); - $product = $fixtureFactory->createByCode($explodeValue[0], ['dataset' => $explodeValue[1]]); - if (!$product->getId()) { - $product->persist(); - } - $this->data[] = $product->getName(); + list($fixtureCode, $dataset) = explode('::', $value); + $this->products[] = $fixtureFactory->createByCode($fixtureCode, ['dataset' => $dataset]); + } + } + if (isset($data['products'])) { + foreach ($data['products'] as $product) { $this->products[] = $product; } } + foreach ($this->products as $product) { + if (!$product->hasData('id')) { + $product->persist(); + } + $this->data[] = $product->getName(); + } } - /** + /**\ * Return products. * * @return array diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/CategoryIds.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/CategoryIds.php index d7984dceb7bced436f2c0cf2939b301226adb60a..bb011b3bc26c9fed9ae8967f27a068c76048deb7 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/CategoryIds.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/CategoryIds.php @@ -51,11 +51,7 @@ class CategoryIds extends DataSource } elseif (isset($data['dataset'])) { $datasets = explode(',', $data['dataset']); foreach ($datasets as $dataset) { - if (trim($dataset) == '-') { - $this->data[] = ''; - continue; - } - $category = $fixtureFactory->createByCode('category', ['dataset' => $dataset]); + $category = $fixtureFactory->createByCode('category', ['dataset' => trim($dataset)]); if (!isset($data['new_category']) || $data['new_category'] !== 'yes') { $category->persist(); } @@ -64,6 +60,16 @@ class CategoryIds extends DataSource $this->data[] = $category->getName(); $this->categories[] = $category; } + } else { + foreach ($data as $category) { + if ($category instanceof Category) { + if (!$category->hasData('id')) { + $category->persist(); + } + $this->data[] = $category->getName(); + $this->categories[] = $category; + } + } } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/WebsiteIds.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/WebsiteIds.php index 04c06e5eade843529b5767034dc0db18bc8fdb21..56c9499ea1a93aac1d9e680dd5e9ceb2ce39b6bc 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/WebsiteIds.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/WebsiteIds.php @@ -8,6 +8,7 @@ namespace Magento\Catalog\Test\Fixture\Product; use Magento\Mtf\Fixture\DataSource; use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Store\Test\Fixture\Store; /** * Prepare websites. @@ -72,25 +73,39 @@ class WebsiteIds extends DataSource } foreach ($this->fixtureData as $dataset) { - if (isset($dataset['dataset'])) { + if (is_array($dataset) && isset($dataset['dataset'])) { $store = $this->fixtureFactory->createByCode('store', $dataset); - - if (!$store->getStoreId()) { - $store->persist(); + $this->processStore($store); + } elseif ($dataset instanceof Store) { + $this->processStore($dataset); + } elseif (isset($dataset['websites'])) { + foreach ($dataset['websites'] as $website) { + $this->websites[] = $website; } - - $website = $store->getDataFieldConfig('group_id')['source'] - ->getStoreGroup()->getDataFieldConfig('website_id')['source']->getWebsite(); - - $this->data[] = $website->getName(); - $this->websites[] = $website; - $this->stores[] = $store; } } return parent::getData($key); } + /** + * Add website by store fixture. + * + * @param Store $store + * @return void + */ + private function processStore(Store $store) + { + if (!$store->getStoreId()) { + $store->persist(); + } + $website = $store->getDataFieldConfig('group_id')['source'] + ->getStoreGroup()->getDataFieldConfig('website_id')['source']->getWebsite(); + $this->data[] = $website->getName(); + $this->websites[] = $website; + $this->stores[] = $store; + } + /** * Return stores. * diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Curl.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Curl.php index 2ccb804b073f90255f378a6e3ef6e8af5d427332..9267d041d6a1800352acc7cdf6b76392c21d4ce8 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Curl.php @@ -488,7 +488,6 @@ class Curl extends AbstractCurl implements CatalogProductSimpleInterface if (!isset($this->fields['product']['custom_options'])) { return; } - $options = []; foreach ($this->fields['product']['custom_options'] as $key => $customOption) { $options[$key] = [ @@ -510,7 +509,7 @@ class Curl extends AbstractCurl implements CatalogProductSimpleInterface } $this->fields['product']['options'] = $options; - $this->fields['affect_product_custom_options'] = 1; + $this->fields['product']['affect_product_custom_options'] = 1; unset($this->fields['product']['custom_options']); } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Adminhtml/CatalogCategoryEdit.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Adminhtml/CatalogCategoryEdit.xml index c44aa5e05de56affdc3bac97eed1a2df0ad2762c..9a4500ed5b0d7f3c625c4fc855919ccb80700df8 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Adminhtml/CatalogCategoryEdit.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Adminhtml/CatalogCategoryEdit.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> <page name="CatalogCategoryEdit" area="Adminhtml" mca="catalog/category/edit" module="Magento_Catalog"> - <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator="#messages" strategy="css selector"/> + <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator="#messages, .messages" strategy="css selector"/> <block name="formPageActions" class="Magento\Catalog\Test\Block\Adminhtml\Category\Edit\PageActions" locator=".page-main-actions" strategy="css selector"/> <block name="editForm" class="Magento\Catalog\Test\Block\Adminhtml\Category\Edit\CategoryForm" locator="#container" strategy="css selector"/> <block name="modalBlock" class="Magento\Ui\Test\Block\Adminhtml\Modal" locator="._show[data-role=modal]" strategy="css selector"/> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Category/CatalogCategoryView.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Category/CatalogCategoryView.xml index 2b27b6ac3c108e6c1bd694627a1b20195c845b24..8c7180da376d361c16ba279416b91c6bcd868da4 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Category/CatalogCategoryView.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Category/CatalogCategoryView.xml @@ -15,5 +15,6 @@ <block name="listProductBlock" class="Magento\Catalog\Test\Block\Product\ListProduct" locator=".products.wrapper.grid" strategy="css selector"/> <block name="topToolbar" class="Magento\Catalog\Test\Block\Product\ProductList\TopToolbar" locator=".//*[contains(@class,'toolbar-products')][1]" strategy="xpath"/> <block name="bottomToolbar" class="Magento\Catalog\Test\Block\Product\ProductList\BottomToolbar" locator=".//*[contains(@class,'toolbar-products')][2]" strategy="xpath"/> + <block name="messagesBlock" class="Magento\Cms\Test\Block\Messages" locator=".page.messages" strategy="css selector" /> </page> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogAttributeSet.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogAttributeSet.xml index a62bef0680a9eaf1eff6adf99f809e3f9c538353..82fd8283da0e161845f0500ea3c9a623663bbd93 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogAttributeSet.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogAttributeSet.xml @@ -38,5 +38,15 @@ <item name="dataset" xsi:type="string">color_for_promo_rules</item> </field> </dataset> + + <dataset name="custom_attribute_set_with_sizes"> + <field name="attribute_set_name" xsi:type="string">Custom_attribute_set_with_sizes_%isolation%</field> + <field name="skeleton_set" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="assigned_attributes" xsi:type="array"> + <item name="dataset" xsi:type="string">sizes_for_promo_rules</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductAttribute.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductAttribute.xml index e94af12754af5615b48c81f37fe2a3df21470eff..8182177ed56db24329b38e9b0342bfdeb374732a 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductAttribute.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductAttribute.xml @@ -204,5 +204,55 @@ </item> </field> </dataset> + + <dataset name="sizes_S_M_L"> + <field name="frontend_label" xsi:type="string">size_%isolation%</field> + <field name="attribute_code" xsi:type="string">size_%isolation%</field> + <field name="frontend_input" xsi:type="string">Dropdown</field> + <field name="is_required" xsi:type="string">No</field> + <field name="options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="is_default" xsi:type="string">No</item> + <item name="admin" xsi:type="string">SIZE_S</item> + <item name="view" xsi:type="string">SIZE_S</item> + </item> + <item name="1" xsi:type="array"> + <item name="is_default" xsi:type="string">No</item> + <item name="admin" xsi:type="string">SIZE_M</item> + <item name="view" xsi:type="string">SIZE_M</item> + </item> + <item name="2" xsi:type="array"> + <item name="is_default" xsi:type="string">No</item> + <item name="admin" xsi:type="string">SIZE_L</item> + <item name="view" xsi:type="string">SIZE_L</item> + </item> + </field> + </dataset> + + <dataset name="sizes_for_promo_rules"> + <field name="frontend_label" xsi:type="string">size_%isolation%</field> + <field name="attribute_code" xsi:type="string">size_%isolation%</field> + <field name="frontend_input" xsi:type="string">Dropdown</field> + <field name="is_used_for_promo_rules" xsi:type="string">Yes</field> + <field name="is_required" xsi:type="string">No</field> + <field name="options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="is_default" xsi:type="string">Yes</item> + <item name="admin" xsi:type="string">SIZE_S_%isolation%</item> + <item name="view" xsi:type="string">SIZE_S</item> + </item> + <item name="1" xsi:type="array"> + <item name="is_default" xsi:type="string">No</item> + <item name="admin" xsi:type="string">SIZE_M_%isolation%</item> + <item name="view" xsi:type="string">SIZE_M</item> + </item> + <item name="2" xsi:type="array"> + <item name="is_default" xsi:type="string">No</item> + <item name="admin" xsi:type="string">SIZE_L_%isolation%</item> + <item name="view" xsi:type="string">SIZE_L</item> + <item name="promo" xsi:type="string">Yes</item> + </item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml index 997d0ab1d7f11c64ce33ea386870055ea2e77798..b42757b07b2d250cfc82f74e475c7ca683e1f7ee 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml @@ -68,6 +68,37 @@ </field> </dataset> + <dataset name="product_with_long_name"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Product with long name %isolation%%isolation%%isolation%%isolation%%isolation%%isolation%%isolation%%isolation%%isolation%%isolation%%isolation%%isolation%%isolation%%isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="is_virtual" xsi:type="string">No</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">25</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">560</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> + </field> + </dataset> + <dataset name="product_with_custom_color_attribute"> <field name="name" xsi:type="string">Simple Product %isolation%</field> <field name="sku" xsi:type="string">simple_product_%isolation%</field> @@ -1307,6 +1338,39 @@ </field> </dataset> + <dataset name="simple_10_dollar_buy_all"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">10</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + <dataset name="simple_with_tier_price_and_order_qty_3"> <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> @@ -1368,44 +1432,46 @@ <field name="url_key" xsi:type="string">overnight-duffle</field> </dataset> - <dataset name="simple_with_weight_10_for_salesrule"> + <dataset name="with_percent_and_fixed_custom_option"> <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> </field> <field name="name" xsi:type="string">Simple Product %isolation%</field> <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">56.78</item> + </field> <field name="product_has_weight" xsi:type="string">This item has weight</field> - <field name="weight" xsi:type="string">10</field> + <field name="weight" xsi:type="string">1</field> <field name="quantity_and_stock_status" xsi:type="array"> - <item name="qty" xsi:type="string">25</item> + <item name="qty" xsi:type="string">1</item> <item name="is_in_stock" xsi:type="string">In Stock</item> </field> - <field name="price" xsi:type="array"> - <item name="value" xsi:type="string">560</item> - </field> - <field name="tax_class_id" xsi:type="array"> - <item name="dataset" xsi:type="string">taxable_goods</item> - </field> <field name="website_ids" xsi:type="array"> <item name="0" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> </item> </field> - <field name="visibility" xsi:type="string">Catalog, Search</field> <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + <field name="custom_options" xsi:type="array"> + <item name="dataset" xsi:type="string">percent_and_fixed_radio_options</item> + </field> <field name="checkout_data" xsi:type="array"> - <item name="dataset" xsi:type="string">simple_order_default</item> + <item name="dataset" xsi:type="string">with_fixed_custom_option</item> </field> </dataset> - <dataset name="with_default_custom_option"> + <dataset name="with_fixed_custom_option_price_100"> <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> </field> <field name="name" xsi:type="string">Simple Product %isolation%</field> <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> <field name="price" xsi:type="array"> - <item name="value" xsi:type="string">56.78</item> + <item name="value" xsi:type="string">100</item> </field> <field name="product_has_weight" xsi:type="string">This item has weight</field> <field name="weight" xsi:type="string">1</field> @@ -1423,14 +1489,14 @@ <item name="dataset" xsi:type="string">default_subcategory</item> </field> <field name="custom_options" xsi:type="array"> - <item name="dataset" xsi:type="string">percent_and_fixed_radio_options</item> + <item name="dataset" xsi:type="string">text_and_two_custom_options</item> </field> <field name="checkout_data" xsi:type="array"> - <item name="dataset" xsi:type="string">simple_order_qty_1_price_56</item> + <item name="dataset" xsi:type="string">text_and_two_custom_options</item> </field> </dataset> - <dataset name="with_fixed_custom_option"> + <dataset name="with_custom_options_and_price_56_78"> <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> </field> @@ -1458,11 +1524,131 @@ <item name="dataset" xsi:type="string">percent_and_fixed_radio_options</item> </field> <field name="checkout_data" xsi:type="array"> - <item name="dataset" xsi:type="string">with_fixed_custom_option</item> + <item name="dataset" xsi:type="string">with_percent_custom_option</item> + </field> + </dataset> + + <dataset name="product_with_price_20_01"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">25</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">20.01</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> + </field> + </dataset> + + <dataset name="product_with_price_79_99"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">25</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">79.99</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> </field> </dataset> - <dataset name="with_percent_custom_option"> + <dataset name="simple_with_weight_10"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">10</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">25</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">560</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> + </field> + </dataset> + + <dataset name="default_with_weight_2"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">2</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">25</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">560</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> + </field> + </dataset> + + <dataset name="with_default_custom_option"> <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> </field> @@ -1490,9 +1676,128 @@ <item name="dataset" xsi:type="string">percent_and_fixed_radio_options</item> </field> <field name="checkout_data" xsi:type="array"> - <item name="dataset" xsi:type="string">with_percent_custom_option</item> + <item name="dataset" xsi:type="string">simple_order_qty_1_price_56</item> + </field> + </dataset> + + <dataset name="default_qty_3"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">3</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">560</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> </field> </dataset> + <dataset name="default_qty_1"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">560</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> + </field> + </dataset> + + <dataset name="default_qty_2"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">2</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">560</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> + </field> + </dataset> + + <dataset name="default_in_custom_website"> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Simple Product %isolation%</field> + <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">25</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">560</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">custom_store</item> + </item> + </field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">simple_order_default</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple/CheckoutData.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple/CheckoutData.xml index a1f34f46b0a7cb89342a17a382a0ba1c86de9059..dc570b0bf7f2780ba878bdae30fa61f22ca4133a 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple/CheckoutData.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple/CheckoutData.xml @@ -165,6 +165,44 @@ <field name="qty" xsi:type="string">3</field> </dataset> + <dataset name="with_fixed_custom_option"> + <field name="options" xsi:type="array"> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">1</field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">560.78</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">560.78</item> + </field> + </dataset> + + <dataset name="text_and_two_custom_options"> + <field name="options" xsi:type="array"> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">Ok</item> + </item> + <item name="1" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_1</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">1</field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">5</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">125</item> + </field> + </dataset> + <dataset name="simple_order_qty_1_price_56"> <field name="qty" xsi:type="string">1</field> <field name="cartItem" xsi:type="array"> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductVirtual.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductVirtual.xml index 7f56333969a0de17d607192ad5b0f77741f45626..7effa080dc6819d026deb85032c4f93ca0c1b05f 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductVirtual.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductVirtual.xml @@ -75,6 +75,36 @@ </field> </dataset> + <dataset name="virtual_low_stock"> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="url_key" xsi:type="string">virtual-product%isolation%</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Virtual product %isolation%</field> + <field name="sku" xsi:type="string">sku_virtual_product_%isolation%</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="product_has_weight" xsi:type="string">This item has no weight</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">10</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">virtual_order_default</item> + </field> + </dataset> + <dataset name="virtual_big_qty"> <field name="tax_class_id" xsi:type="array"> <item name="dataset" xsi:type="string">taxable_goods</item> @@ -188,5 +218,35 @@ <item name="dataset" xsi:type="string">virtual_order_default</item> </field> </dataset> + + <dataset name="buy_all"> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="url_key" xsi:type="string">virtual-product%isolation%</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="name" xsi:type="string">Virtual product %isolation%</field> + <field name="sku" xsi:type="string">sku_virtual_product_%isolation%</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="product_has_weight" xsi:type="string">This item has no weight</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">10</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">virtual_order_default</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml index dc5e9c12ebcaebc1e49c8394f4d4308a11762309..82e7e8e6edeae17de48a6f62af1539464910620e 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml @@ -73,6 +73,36 @@ <field name="include_in_menu" xsi:type="string">Yes</field> </dataset> + <dataset name="three_nested_categories"> + <field name="name" xsi:type="string">Category%isolation%</field> + <field name="url_key" xsi:type="string">category-%isolation%</field> + <field name="parent_id" xsi:type="array"> + <item name="dataset" xsi:type="string">two_nested_categories</item> + </field> + <field name="is_active" xsi:type="string">Yes</field> + <field name="include_in_menu" xsi:type="string">Yes</field> + </dataset> + + <dataset name="four_nested_categories"> + <field name="name" xsi:type="string">Category%isolation%</field> + <field name="url_key" xsi:type="string">category-%isolation%</field> + <field name="parent_id" xsi:type="array"> + <item name="dataset" xsi:type="string">three_nested_categories</item> + </field> + <field name="is_active" xsi:type="string">Yes</field> + <field name="include_in_menu" xsi:type="string">Yes</field> + </dataset> + + <dataset name="five_nested_categories"> + <field name="name" xsi:type="string">Category%isolation%</field> + <field name="url_key" xsi:type="string">category-%isolation%</field> + <field name="parent_id" xsi:type="array"> + <item name="dataset" xsi:type="string">four_nested_categories</item> + </field> + <field name="is_active" xsi:type="string">Yes</field> + <field name="include_in_menu" xsi:type="string">Yes</field> + </dataset> + <dataset name="default_subcategory_with_assigned_simple_product"> <field name="name" xsi:type="string">Category%isolation%</field> <field name="url_key" xsi:type="string">category%isolation%</field> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/CustomOptions.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/CustomOptions.xml index b977e23166f45ec9c471ea08c5e7dc7516a728c0..89e1f07ee60222471e05e682bd0d44d25c90551c 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/CustomOptions.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/CustomOptions.xml @@ -383,6 +383,98 @@ </field> </dataset> + <dataset name="text_and_two_custom_options"> + <field name="0" xsi:type="array"> + <item name="title" xsi:type="string">30 bucks</item> + <item name="is_require" xsi:type="string">Yes</item> + <item name="type" xsi:type="string">Text/Field</item> + <item name="options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="price" xsi:type="string">30</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku_field_option_%isolation%</item> + <item name="max_characters" xsi:type="string">1024</item> + </item> + </item> + </field> + <field name="1" xsi:type="array"> + <item name="title" xsi:type="string">custom menu</item> + <item name="is_require" xsi:type="string">Yes</item> + <item name="type" xsi:type="string">Select/Radio Buttons</item> + <item name="options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">5 bucks</item> + <item name="price" xsi:type="string">5</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku_radio_buttons_row_1</item> + <item name="sort_order" xsi:type="string">0</item> + </item> + <item name="1" xsi:type="array"> + <item name="title" xsi:type="string">10 bucks</item> + <item name="price" xsi:type="string">10</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku_radio_buttons_row_2</item> + <item name="sort_order" xsi:type="string">1</item> + </item> + </item> + </field> + </dataset> + + <dataset name="two_fixed_radio_options"> + <field name="0" xsi:type="array"> + <item name="title" xsi:type="string">custom menu</item> + <item name="is_require" xsi:type="string">No</item> + <item name="type" xsi:type="string">Select/Radio Buttons</item> + <item name="options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">12 bucks</item> + <item name="price" xsi:type="string">12</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku_radio_buttons_row_1</item> + <item name="sort_order" xsi:type="string">0</item> + </item> + <item name="1" xsi:type="array"> + <item name="title" xsi:type="string">89 bucks</item> + <item name="price" xsi:type="string">89</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku_radio_buttons_row_2</item> + <item name="sort_order" xsi:type="string">0</item> + </item> + </item> + </field> + </dataset> + + <dataset name="drop_down_with_three_options"> + <field name="0" xsi:type="array"> + <item name="title" xsi:type="string">custom option drop down %isolation%</item> + <item name="is_require" xsi:type="string">Yes</item> + <item name="type" xsi:type="string">Select/Drop-down</item> + <item name="options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">12 bucks</item> + <item name="price" xsi:type="string">12</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku_radio_buttons_row_1</item> + <item name="sort_order" xsi:type="string">0</item> + </item> + <item name="1" xsi:type="array"> + <item name="title" xsi:type="string">12 bucks</item> + <item name="price" xsi:type="string">12</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku_radio_buttons_row_1</item> + <item name="sort_order" xsi:type="string">0</item> + </item> + <item name="2" xsi:type="array"> + <item name="title" xsi:type="string">12 bucks</item> + <item name="price" xsi:type="string">12</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku_radio_buttons_row_1</item> + <item name="sort_order" xsi:type="string">0</item> + </item> + </item> + </field> + </dataset> + <dataset name="not_required_text_option"> <field name="0" xsi:type="array"> <item name="title" xsi:type="string">Test1 option %isolation%</item> @@ -398,6 +490,5 @@ </item> </field> </dataset> - </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.php index d458ffeb22475b78d66eeb6441d1f911c12b0cc8..61b0a51df65368230266644a686f4dc10e707ab1 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.php @@ -23,7 +23,7 @@ use Magento\Mtf\TestCase\Injectable; * 6. Verify created category * * @group Category_Management - * @ZephyrId MAGETWO-23411 + * @ZephyrId MAGETWO-23411, MAGETWO-48379 */ class CreateCategoryEntityTest extends Injectable { diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index 238fddba3de426f683e981c0a1b7b1b60c12fbeb..90863eb208ce252496e2ac002df04da6b2ee022e 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -87,6 +87,7 @@ <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> <data name="category/data/schedule_update_to" xsi:type="string">12/31/2024</data> <data name="filterByPath" xsi:type="string">request_path</data> + <data name="redirectType" xsi:type="string">No</data> <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteCategoryInGrid" /> <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryForm" /> @@ -153,11 +154,10 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryForAssignedProducts" /> </variation> - <variation name="CreateCategoryEntityTestVariation9_ThreeNesting"> + <variation name="CreateCategoryEntityTestVariation9_FiveNesting" summary="Create category with five nesting" ticketId="MAGETWO-48379"> <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data> - <data name="description" xsi:type="string">Create category with three nesting</data> <data name="addCategory" xsi:type="string">addSubcategory</data> - <data name="category/data/parent_id/dataset" xsi:type="string">two_nested_categories</data> + <data name="category/data/parent_id/dataset" xsi:type="string">five_nested_categories</data> <data name="category/data/is_active" xsi:type="string">Yes</data> <data name="category/data/name" xsi:type="string">Category%isolation%</data> <data name="category/data/description" xsi:type="string">Category Required</data> @@ -166,5 +166,14 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryBreadcrumbs" /> </variation> + <variation name="CreateCategoryEntityTestVariation10_InCustomRootCategory" summary="Create category in the custom root category that is used for custom website" ticketId="MAGETWO-12938"> + <data name="addCategory" xsi:type="string">addSubcategory</data> + <data name="category/data/parent_id/dataset" xsi:type="string">root_category</data> + <data name="category/data/is_active" xsi:type="string">Yes</data> + <data name="category/data/include_in_menu" xsi:type="string">Yes</data> + <data name="category/data/name" xsi:type="string">Subcategory%isolation%</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryOnCustomWebsite" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cebcd14ab050f7ee4121cc33199c29f4d58327f8 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\TestCase\Category; + +use Magento\Catalog\Test\Fixture\Category; +use Magento\Catalog\Test\Page\Adminhtml\CatalogCategoryEdit; +use Magento\Catalog\Test\Page\Adminhtml\CatalogCategoryIndex; +use Magento\Mtf\TestCase\Injectable; + +/** + * Precondition: + * 1. Categories are created + * + * Test Flow: + * 1. Log in to Backend + * 2. Navigate to the Products>Inventory>Categories + * 3. Click on 'Add Category' button + * 4. Fill out all data according to data set + * 5. Save category + * 6. Verify created category + * + * @group Category_Management + * @ZephyrId MAGETWO-27319 + */ +class MoveCategoryEntityTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + /* end tags */ + + /** + * CatalogCategoryIndex page. + * + * @var CatalogCategoryIndex + */ + private $catalogCategoryIndex; + + /** + * CatalogCategoryEdit page. + * + * @var CatalogCategoryEdit + */ + private $catalogCategoryEdit; + + /** + * Inject page end prepare default category. + * + * @param CatalogCategoryIndex $catalogCategoryIndex + * @param CatalogCategoryEdit $catalogCategoryEdit + * @return void + */ + public function __inject( + CatalogCategoryIndex $catalogCategoryIndex, + CatalogCategoryEdit $catalogCategoryEdit + ) { + $this->catalogCategoryIndex = $catalogCategoryIndex; + $this->catalogCategoryEdit = $catalogCategoryEdit; + } + + /** + * Runs test. + * + * @param Category $childCategory + * @param Category $parentCategory + * @return array + */ + public function test(Category $childCategory, Category $parentCategory) + { + // Preconditions: + $parentCategory->persist(); + $childCategory->persist(); + + // Steps: + $this->catalogCategoryIndex->open(); + $this->catalogCategoryIndex->getTreeCategories()->expandAllCategories(); + $this->catalogCategoryIndex->getTreeCategories()->assignCategory( + $parentCategory->getName(), + $childCategory->getName() + ); + $this->catalogCategoryEdit->getModalBlock()->acceptWarning(); + + return [ + 'category' => $childCategory, + 'parentCategory' => $parentCategory, + 'childCategory' => $childCategory + ]; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..b3a17778970243129ed41996edeeb5d4a81cfef0 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Catalog\Test\TestCase\Category\MoveCategoryEntityTest" summary="Move category from one to another" ticketId="MAGETWO-27319"> + <variation name="MoveCategoryEntityTestVariation1"> + <data name="childCategory/dataset" xsi:type="string">three_nested_categories</data> + <data name="parentCategory/dataset" xsi:type="string">default</data> + <data name="nestingLevel" xsi:type="string">3</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryMovedMessage" /> + <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteCategoryInGrid" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml index e98cd1529409aa05e899d5bdd847783c78e16828..5b7ef1e4c341fbcde2c9dae99fb610ca2fc3c8fe 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/UpdateCategoryEntityTest.xml @@ -14,6 +14,7 @@ <data name="category/data/description" xsi:type="string">Category Description Updated</data> <data name="category/data/url_key" xsi:type="string">UrlKey%isolation%</data> <data name="category/data/meta_title" xsi:type="string">Category Title Updated</data> + <data name="redirectType" xsi:type="string">No</data> <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryForm" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteCategoryInGrid" /> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml index 7979faa5e1f761844fa5d1bb8bebd00430e87623..d906713ae726dd1d5bb1399ec674cd054552c711 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml @@ -542,5 +542,16 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductHasImageInGrid" /> <constraint name="Magento\Catalog\Test\Constraint\AssertCanSaveProduct" /> </variation> + <variation name="CreateSimpleProductNotVisibleIndividually" summary="Check that url rewrite is not generated for product if visibility is 'Not Visible Individually'" ticketId="MAGETWO-53788"> + <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> + <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> + <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> + <data name="product/data/price/value" xsi:type="string">15</data> + <data name="product/data/weight" xsi:type="string">5</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">1</data> + <data name="product/data/visibility" xsi:type="string">Not Visible Individually</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> + <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteProductNotInGrid" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ManageProductsStockTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ManageProductsStockTest.php new file mode 100644 index 0000000000000000000000000000000000000000..48553a934c8e9df9189a99205e32c094b9da4a1b --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ManageProductsStockTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\TestCase\Product; + +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Mtf\TestCase\Injectable; + +/** + * Preconditions: + * 1. Set Configuration + * 2. Create products according to dataset + * + * Steps: + * 1. Open product on frontend + * 2. Add product to cart if needed + * 3. Perform all assertions + * + * @group Inventory + * @ZephyrId MAGETWO-29543, MAGETWO-13645 + */ +class ManageProductsStockTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + /* end tags */ + + /** + * Fixture factory. + * + * @var FixtureFactory + */ + protected $fixtureFactory; + + /** + * Configuration data. + * + * @var string + */ + protected $configData; + + /** + * Setup configuration. + * + * @param FixtureFactory $fixtureFactory + * @return void + */ + public function __inject(FixtureFactory $fixtureFactory) + { + $this->fixtureFactory = $fixtureFactory; + } + + /** + * Manage products stock. + * + * @param CatalogProductSimple $product + * @param string $skipAddingToCart + * @param string $configData + * @return mixed + */ + public function test(CatalogProductSimple $product, $skipAddingToCart = null, $configData = null) + { + $this->configData = $configData; + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData] + )->run(); + + // Preconditions + $product->persist(); + + // Steps + if (!$skipAddingToCart) { + $this->objectManager->create( + \Magento\Checkout\Test\TestStep\AddProductsToTheCartStep::class, + ['products' => [$product]] + )->run(); + + $cart['data']['items'] = ['products' => [$product]]; + + return ['cart' => $this->fixtureFactory->createByCode('cart', $cart)]; + } + } + + /** + * Set default configuration. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ManageProductsStockTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ManageProductsStockTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..bf104b72fd6475cd4ea78165cb71f3c85665a4f3 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ManageProductsStockTest.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Catalog\Test\TestCase\Product\ManageProductsStockTest" summary="Managing products stock with several conditions" ticketId="MAGETWO-29543"> + <variation name="ManageProductsStockTestVariation1" summary="Adding Out of Stock product to cart"> + <data name="product/dataset" xsi:type="string">default</data> + <data name="product/data/qty_and_stock_status/is_in_stock" xsi:type="string">Out of Stock</data> + <data name="configData" xsi:type="string">display_out_of_stock,backorders_allow_qty_below</data> + <constraint name="Magento\Checkout\Test\Constraint\AssertAddedProductToCartSuccessMessage" /> + <constraint name="Magento\Checkout\Test\Constraint\AssertProductQtyInShoppingCart" /> + </variation> + <variation name="ManageProductsStockTestVariation2" summary="Checking that Out of Stock products are not visible in category" ticketId="MAGETWO-13645"> + <data name="product/dataset" xsi:type="string">product_with_category</data> + <data name="product/data/stock_data/manage_stock" xsi:type="string">Yes</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">5</data> + <data name="product/data/stock_data/use_config_min_sale_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/min_sale_qty" xsi:type="string">1</data> + <data name="product/data/stock_data/use_config_max_sale_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/max_sale_qty" xsi:type="string">10000</data> + <data name="product/data/stock_data/is_qty_decimal" xsi:type="string">Yes</data> + <data name="product/data/stock_data/enable_qty_increments" xsi:type="string">No</data> + <data name="product/data/stock_data/use_config_notify_stock_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/notify_stock_qty" xsi:type="string">1</data> + <data name="product/data/stock_data/use_config_backorders" xsi:type="string">No</data> + <data name="product/data/quantity_and_stock_status/is_in_stock" xsi:type="string">Out of Stock</data> + <data name="skipAddingToCart" xsi:type="string">Yes</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductNotVisibleInCategory" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductOutOfStock" /> + </variation> + <variation name="ManageProductsStockTestVariation3" summary="Add In Stock product to cart" ticketId="MAGETWO-13645"> + <data name="product/dataset" xsi:type="string">product_with_category</data> + <data name="product/data/stock_data/manage_stock" xsi:type="string">Yes</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">5</data> + <data name="product/data/quantity_and_stock_status/is_in_stock" xsi:type="string">In Stock</data> + <data name="product/data/stock_data/use_config_min_sale_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/min_sale_qty" xsi:type="string">1</data> + <data name="product/data/stock_data/use_config_max_sale_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/max_sale_qty" xsi:type="string">10000</data> + <data name="product/data/stock_data/is_qty_decimal" xsi:type="string">Yes</data> + <data name="product/data/stock_data/enable_qty_increments" xsi:type="string">No</data> + <data name="product/data/stock_data/use_config_notify_stock_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/notify_stock_qty" xsi:type="string">1</data> + <data name="product/data/stock_data/use_config_backorders" xsi:type="string">No</data> + <data name="skipAddingToCart" xsi:type="string">Yes</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductVisibleInCategory" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductPage" /> + </variation> + <variation name="ManageProductsStockTestVariation4" summary="Enable displaying of out of stock products in category" ticketId="MAGETWO-13645"> + <data name="product/dataset" xsi:type="string">product_with_category</data> + <data name="product/data/stock_data/manage_stock" xsi:type="string">Yes</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">5</data> + <data name="product/data/quantity_and_stock_status/is_in_stock" xsi:type="string">In Stock</data> + <data name="product/data/stock_data/use_config_min_sale_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/min_sale_qty" xsi:type="string">1</data> + <data name="product/data/stock_data/use_config_max_sale_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/max_sale_qty" xsi:type="string">10000</data> + <data name="product/data/stock_data/is_qty_decimal" xsi:type="string">Yes</data> + <data name="product/data/stock_data/enable_qty_increments" xsi:type="string">No</data> + <data name="product/data/stock_data/use_config_notify_stock_qty" xsi:type="string">Yes</data> + <data name="product/data/stock_data/notify_stock_qty" xsi:type="string">1</data> + <data name="product/data/stock_data/use_config_backorders" xsi:type="string">No</data> + <data name="skipAddingToCart" xsi:type="string">Yes</data> + <data name="configData" xsi:type="string">display_out_of_stock</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductVisibleInCategory" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.php index f68ec539c62e0c7b59f9c6f958bff7dc350a91d6..76d13013917ab6c322aa972fc4d190012068470b 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.php @@ -7,10 +7,12 @@ namespace Magento\Catalog\Test\TestCase\Product; use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Store\Test\Fixture\Store; use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit; use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; -use Magento\Mtf\ObjectManager; use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Catalog\Test\Fixture\Category; /** * Precondition: @@ -56,19 +58,29 @@ class UpdateSimpleProductEntityTest extends Injectable */ protected $configData; + /** + * Fixture Factory. + * + * @var FixtureFactory + */ + private $fixtureFactory; + /** * Injection data. * * @param CatalogProductIndex $productGrid * @param CatalogProductEdit $editProductPage + * @param FixtureFactory $fixtureFactory * @return void */ public function __inject( CatalogProductIndex $productGrid, - CatalogProductEdit $editProductPage + CatalogProductEdit $editProductPage, + FixtureFactory $fixtureFactory ) { $this->productGrid = $productGrid; $this->editProductPage = $editProductPage; + $this->fixtureFactory = $fixtureFactory; } /** @@ -76,20 +88,25 @@ class UpdateSimpleProductEntityTest extends Injectable * * @param CatalogProductSimple $initialProduct * @param CatalogProductSimple $product + * @param Store|null $store * @param string $configData * @return array */ - public function test(CatalogProductSimple $initialProduct, CatalogProductSimple $product, $configData = '') - { + public function test( + CatalogProductSimple $initialProduct, + CatalogProductSimple $product, + Store $store = null, + $configData = '' + ) { $this->configData = $configData; // Preconditions $initialProduct->persist(); - $initialCategory = $initialProduct->hasData('category_ids') - ? $initialProduct->getDataFieldConfig('category_ids')['source']->getCategories()[0] - : null; - $category = $product->hasData('category_ids') && $product->getCategoryIds()[0] - ? $product->getDataFieldConfig('category_ids')['source']->getCategories()[0] - : $initialCategory; + $category = $this->getCategory($initialProduct, $product); + + if ($store) { + $store->persist(); + $productName[$store->getStoreId()] = $product->getName(); + } $this->objectManager->create( \Magento\Config\Test\TestStep\SetupConfigurationStep::class, @@ -101,10 +118,34 @@ class UpdateSimpleProductEntityTest extends Injectable $this->productGrid->open(); $this->productGrid->getProductGrid()->searchAndOpen($filter); + if ($store) { + $this->editProductPage->getFormPageActions()->changeStoreViewScope($store); + } $this->editProductPage->getProductForm()->fill($product); $this->editProductPage->getFormPageActions()->save(); - return ['category' => $category]; + return [ + 'category' => $category, + 'stores' => isset($store) ? [$store] : [], + 'productNames' => isset($productName) ? $productName : [], + ]; + } + + /** + * Get Category instance. + * + * @param CatalogProductSimple $initialProduct + * @param CatalogProductSimple $product + * @return Category + */ + protected function getCategory(CatalogProductSimple $initialProduct, CatalogProductSimple $product) + { + $initialCategory = $initialProduct->hasData('category_ids') + ? $initialProduct->getDataFieldConfig('category_ids')['source']->getCategories()[0] + : null; + return $product->hasData('category_ids') && $product->getCategoryIds()[0] + ? $product->getDataFieldConfig('category_ids')['source']->getCategories()[0] + : $initialCategory; } /** diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml index 31aaad654f92a914c14276ab770fa7fe9790f5de..b211242bbd0b04ba376e0f4414c8274beba9209c 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml @@ -169,5 +169,13 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductForm" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductInCart" /> </variation> + <variation name="UpdateSimpleProductEntityTestVariation12" summary="Verify data overriding on Store View level" ticketId="MAGETWO-50640"> + <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> + <data name="store/dataset" xsi:type="string">custom</data> + <data name="product/data/use_default_name" xsi:type="string">No</data> + <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductNameOnDifferentStoreViews" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/CreateProductAttributeEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/CreateProductAttributeEntityTest.xml index 543bece9331f4d0968e38b7d8f887f563b9b8ec3..1726ffb7f9828ce9810fe4c3f3d77c8a585649d0 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/CreateProductAttributeEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/CreateProductAttributeEntityTest.xml @@ -19,8 +19,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertAttributeForm" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAddedProductAttributeOnProductForm" /> </variation> - <variation name="CreateProductAttributeEntityTestVariation2"> - <data name="tag" xsi:type="string">to_maintain:yes</data> + <variation name="CreateProductAttributeEntityTestVariation2" summary="Create custom text attribute product field" ticketId="MAGETWO-17475"> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttribute/data/frontend_label" xsi:type="string">Text_Field_Admin_%isolation%</data> <data name="productAttribute/data/frontend_input" xsi:type="string">Text Area</data> @@ -37,6 +36,7 @@ <data name="sectionName" xsi:type="string">product-details</data> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeInGrid" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAttributeForm" /> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductByAttribute" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAddedProductAttributeOnProductForm" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeIsRequired" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAttributeSearchableByLabel" /> @@ -114,8 +114,8 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAttributeSearchableByLabel" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAttributeOptionsOnProductForm" /> </variation> - <variation name="CreateProductAttributeEntityTestVariation6"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test, to_maintain:yes</data> + <variation name="CreateProductAttributeEntityTestVariation6" summary="Create custom dropdown attribute product field" ticketId="MAGETWO-17475"> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttribute/data/frontend_label" xsi:type="string">Dropdown_Admin_%isolation%</data> <data name="productAttribute/data/frontend_input" xsi:type="string">Dropdown</data> @@ -139,6 +139,7 @@ <data name="sectionName" xsi:type="string">product-details</data> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeInGrid" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAttributeForm" /> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductByAttribute" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAddedProductAttributeOnProductForm" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeIsRequired" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeIsGlobal" /> @@ -152,8 +153,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeIsFilterableInSearch" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAttributeOptionsOnProductForm" /> </variation> - <variation name="CreateProductAttributeEntityTestVariation7"> - <data name="tag" xsi:type="string">to_maintain:yes</data> + <variation name="CreateProductAttributeEntityTestVariation7" summary="Create custom price attribute product field" ticketId="MAGETWO-17475"> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttribute/data/frontend_label" xsi:type="string">Price_Admin_%isolation%</data> <data name="productAttribute/data/frontend_input" xsi:type="string">Price</data> @@ -169,6 +169,7 @@ <data name="attributeValue" xsi:type="number">15</data> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeInGrid" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAttributeForm" /> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductByAttribute" /> <constraint name="Magento\Catalog\Test\Constraint\AssertAddedProductAttributeOnProductForm" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeDisplayingOnSearchForm" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeIsFilterable" /> diff --git a/dev/tests/functional/tests/app/Magento/CatalogInventory/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/CatalogInventory/Test/Repository/ConfigData.xml index 8e5888431b3b256a80c8996526f9d605fcb97ec2..4798bdebbee4e99998dac2d0efdea7846fae03b0 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogInventory/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogInventory/Test/Repository/ConfigData.xml @@ -25,6 +25,15 @@ </field> </dataset> + <dataset name="decrease_stock_after_order_no"> + <field name="cataloginventory/options/can_subtract" xsi:type="array"> + <item name="scope" xsi:type="string">cataloginventory</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">No</item> + <item name="value" xsi:type="number">0</item> + </field> + </dataset> + <dataset name="display_out_of_stock_rollback"> <field name="cataloginventory/options/show_out_of_stock" xsi:type="array"> <item name="scope" xsi:type="string">cataloginventory</item> @@ -42,5 +51,14 @@ <item name="value" xsi:type="number">0</item> </field> </dataset> + + <dataset name="decrease_stock_after_order_no_rollback"> + <field name="cataloginventory/options/can_subtract" xsi:type="array"> + <item name="scope" xsi:type="string">cataloginventory</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">Yes</item> + <item name="value" xsi:type="number">1</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedCatalogPage.php b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedCatalogPage.php index 1a83f4258a4ceb025e92bd1c7b92839c34c31b58..73b09e78cbdf2cbd5b9235f8332af3de6453ea70 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedCatalogPage.php +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedCatalogPage.php @@ -53,9 +53,11 @@ class AssertCatalogPriceRuleAppliedCatalogPage extends AbstractConstraint $priceBlock->isVisible(), 'Price block is not displayed for product ' . $product->getName() ); - $actualPrice['regular'] = (float)$priceBlock->getOldPrice(); $actualPrice['special'] = (float)$priceBlock->getSpecialPrice(); - $actualPrice['discount_amount'] = $actualPrice['regular'] - $actualPrice['special']; + if ($productPrice[$key]['regular'] !== 'No') { + $actualPrice['regular'] = (float)$priceBlock->getOldPrice(); + $actualPrice['discount_amount'] = $actualPrice['regular'] - $actualPrice['special']; + } $diff = $this->verifyData($actualPrice, $productPrice[$key]); \PHPUnit_Framework_Assert::assertTrue( empty($diff), diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedProductPage.php b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedProductPage.php index 653792417be5adf969dcb89931a1f1722a359aff..be71e19d0e52e1626fab5987102686f528d8ca1b 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedProductPage.php +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedProductPage.php @@ -53,9 +53,11 @@ class AssertCatalogPriceRuleAppliedProductPage extends AbstractConstraint $catalogProductViewPage->getViewBlock()->waitLoader(); $productPriceBlock = $catalogProductViewPage->getViewBlock()->getPriceBlock(); - $actualPrice['regular'] = $productPriceBlock->getOldPrice(); $actualPrice['special'] = $productPriceBlock->getSpecialPrice(); - $actualPrice['discount_amount'] = $actualPrice['regular'] - $actualPrice['special']; + if ($productPrice[$key]['regular'] !== 'No') { + $actualPrice['regular'] = $productPriceBlock->getOldPrice(); + $actualPrice['discount_amount'] = $actualPrice['regular'] - $actualPrice['special']; + } $diff = $this->verifyData($actualPrice, $productPrice[$key]); \PHPUnit_Framework_Assert::assertTrue( empty($diff), diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedOnepageCheckout.php b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleOnOnepageCheckout.php similarity index 97% rename from dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedOnepageCheckout.php rename to dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleOnOnepageCheckout.php index 04b1f3a7093e0778fcaed39188b6f55c51858c36..57b403adcc37befe723d4e1ed9ac929c4052a2fe 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleAppliedOnepageCheckout.php +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleOnOnepageCheckout.php @@ -13,7 +13,7 @@ use Magento\Mtf\Constraint\AbstractConstraint; /** * Assert that Catalog Price Rule is applied on OnePage Checkout page. */ -class AssertCatalogPriceRuleAppliedOnepageCheckout extends AbstractConstraint +class AssertCatalogPriceRuleOnOnepageCheckout extends AbstractConstraint { /* tags */ const SEVERITY = 'high'; diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.php b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.php index 68972e5bb766d458425ad6bf0796760325b931e8..79783c06cfb293ac21e45a9f25717703b809a118 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.php +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.php @@ -35,6 +35,13 @@ class ApplyCatalogPriceRulesTest extends AbstractCatalogRuleEntityTest const MVP = 'yes'; /* end tags */ + /** + * Index number of promo product. + * + * @var int + */ + protected $promo; + /** * Apply catalog price rules. * @@ -45,6 +52,7 @@ class ApplyCatalogPriceRulesTest extends AbstractCatalogRuleEntityTest * @param bool $isCronEnabled * @param Customer $customer * @param array $products + * @param int $promo * @return FixtureInterface[] */ public function test( @@ -54,8 +62,10 @@ class ApplyCatalogPriceRulesTest extends AbstractCatalogRuleEntityTest Cron $cron, $isCronEnabled = false, Customer $customer = null, - array $products = null + array $products = [], + $promo = 0 ) { + $this->promo = $promo; if ($customer !== null) { $customer->persist(); } @@ -87,29 +97,47 @@ class ApplyCatalogPriceRulesTest extends AbstractCatalogRuleEntityTest * @param array $catalogPriceRule * @return array */ - private function prepareCondition(FixtureInterface $product, array $catalogPriceRule) + protected function prepareCondition(FixtureInterface $product, array $catalogPriceRule) { - $result = []; $conditionEntity = explode('|', trim($catalogPriceRule['data']['rule'], '[]'))[0]; - - switch ($conditionEntity) { - case 'Category': - $result['%category_id%'] = $product->getDataFieldConfig('category_ids')['source']->getIds()[0]; - break; - case 'Attribute': - /** @var \Magento\Catalog\Test\Fixture\CatalogProductAttribute[] $attrs */ - $attributes = $product->getDataFieldConfig('attribute_set_id')['source'] - ->getAttributeSet()->getDataFieldConfig('assigned_attributes')['source']->getAttributes(); - - $result['%attribute_id%'] = $attributes[0]->getAttributeCode(); - $result['%attribute_value%'] = $attributes[0]->getOptions()[0]['id']; - break; - } - foreach ($result as $key => $value) { - $catalogPriceRule['data']['rule'] = str_replace($key, $value, $catalogPriceRule['data']['rule']); + $actionName = 'get' . $conditionEntity; + if (method_exists($this, $actionName)) { + $result = $this->$actionName($product); + foreach ($result as $key => $value) { + $catalogPriceRule['data']['rule'] = str_replace($key, $value, $catalogPriceRule['data']['rule']); + } + return $catalogPriceRule; + } else { + $message = sprintf('Method "%s" does not exist in %s', $actionName, get_class($this)); + throw new \BadMethodCallException($message); } + } - return $catalogPriceRule; + /** + * Add category_id to catalog price rule. + * + * @param FixtureInterface $product + * @return array + */ + protected function getCategory(FixtureInterface $product) + { + $result['%category_id%'] = $product->getDataFieldConfig('category_ids')['source']->getIds()[0]; + return $result; + } + + /** + * Add attribute_id to catalog price rule. + * + * @param FixtureInterface $product + * @return array + */ + protected function getAttribute(FixtureInterface $product) + { + $attributes = $product->getDataFieldConfig('attribute_set_id')['source'] + ->getAttributeSet()->getDataFieldConfig('assigned_attributes')['source']->getAttributes(); + $result['%attribute_id%'] = $attributes[0]->getAttributeCode(); + $result['%attribute_value%'] = $attributes[0]->getOptions()[$this->promo]['id']; + return $result; } /** @@ -119,7 +147,7 @@ class ApplyCatalogPriceRulesTest extends AbstractCatalogRuleEntityTest * @param Customer $customer * @return array */ - private function applyCustomerGroup(array $catalogPriceRule, Customer $customer) + protected function applyCustomerGroup(array $catalogPriceRule, Customer $customer) { /** @var \Magento\Customer\Test\Fixture\CustomerGroup $customerGroup */ $customerGroup = $customer->getDataFieldConfig('group_id')['source']->getCustomerGroup(); @@ -136,7 +164,7 @@ class ApplyCatalogPriceRulesTest extends AbstractCatalogRuleEntityTest * @param Customer $customer * @return CatalogRule */ - private function createCatalogPriceRule( + protected function createCatalogPriceRule( array $catalogPriceRule, FixtureInterface $product, Customer $customer = null diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml index 00b02c61bd61cb07587fd2df00ef035d85c09b95..a24c571a9b829591352adf8b397c94d818d239c7 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml @@ -115,8 +115,8 @@ </variation> <variation name="ApplyCatalogRuleForSimpleProductWithCustomOptions" summary="Apply Catalog Rule for Simple Product With Custom Options" ticketId="MAGETWO-23038"> <data name="products/0" xsi:type="string">catalogProductSimple::with_default_custom_option</data> - <data name="products/1" xsi:type="string">catalogProductSimple::with_fixed_custom_option</data> - <data name="products/2" xsi:type="string">catalogProductSimple::with_percent_custom_option</data> + <data name="products/1" xsi:type="string">catalogProductSimple::with_percent_and_fixed_custom_option</data> + <data name="products/2" xsi:type="string">catalogProductSimple::with_custom_options_and_price_56_78</data> <data name="customer/dataset" xsi:type="string">customer_US</data> <data name="catalogRules/0/data/name" xsi:type="string">CatalogPriceRule %isolation%</data> <data name="catalogRules/0/data/is_active" xsi:type="string">Active</data> @@ -144,7 +144,56 @@ <data name="shippingAddress/dataset" xsi:type="string">UK_address</data> <data name="payment/method" xsi:type="string">free</data> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedShoppingCart" /> - <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedOnepageCheckout" /> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleOnOnepageCheckout" /> + </variation> + <variation name="ApplyCatalogRuleForSimpleProductAndConfigurableProduct" summary="Apply Catalog Rule For Simple Product And Configurable Product" ticketId="MAGETWO-20616"> + <data name="products/0" xsi:type="string">configurableProduct::product_with_price_10</data> + <data name="products/1" xsi:type="string">catalogProductSimple::simple_10_dollar</data> + <data name="catalogRules/0/data/customer_group_ids/option_0" xsi:type="string">NOT LOGGED IN</data> + <data name="catalogRules/0/data/name" xsi:type="string">CatalogPriceRule %isolation%</data> + <data name="catalogRules/0/data/stop_rules_processing" xsi:type="string">Yes</data> + <data name="catalogRules/0/data/is_active" xsi:type="string">Active</data> + <data name="catalogRules/0/data/website_ids/option_0" xsi:type="string">Main Website</data> + <data name="catalogRules/0/data/simple_action" xsi:type="string">Apply as percentage of original</data> + <data name="catalogRules/0/data/discount_amount" xsi:type="string">10</data> + <data name="cartPrice/sub_total" xsi:type="string">18</data> + <data name="cartPrice/grand_total" xsi:type="string">28</data> + <data name="productPrice/0/special" xsi:type="string">9</data> + <data name="productPrice/0/sub_total" xsi:type="string">9</data> + <data name="productPrice/0/regular" xsi:type="string">No</data> + <data name="productPrice/1/special" xsi:type="string">9</data> + <data name="productPrice/1/sub_total" xsi:type="string">9</data> + <data name="productPrice/1/regular" xsi:type="string">10</data> + <data name="productPrice/1/discount_amount" xsi:type="string">1</data> + <data name="customer/dataset" xsi:type="string">customer_US</data> + <data name="customer/data/group_id/dataset" xsi:type="string">default</data> + <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="shipping/shipping_method" xsi:type="string">Fixed</data> + <data name="shippingAddress/dataset" xsi:type="string">UK_address</data> + <data name="payment/method" xsi:type="string">checkmo</data> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedCatalogPage" /> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedProductPage" /> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedShoppingCart" /> + </variation> + <variation name="ApplyCatalogRuleForSimpleProductAndFixedMethod" summary="Apply Catalog Rule For Simple Product And Fixed Shipping Method" ticketId="MAGETWO-23039"> + <data name="products/0" xsi:type="string">catalogProductSimple::with_fixed_custom_option_price_100</data> + <data name="customer/dataset" xsi:type="string">customer_US</data> + <data name="catalogRules/0/data/name" xsi:type="string">CatalogPriceRule %isolation%</data> + <data name="catalogRules/0/data/is_active" xsi:type="string">Active</data> + <data name="catalogRules/0/data/website_ids/option_0" xsi:type="string">Main Website</data> + <data name="customer/data/group_id/dataset" xsi:type="string">default</data> + <data name="catalogRules/0/data/simple_action" xsi:type="string">Apply as percentage of original</data> + <data name="catalogRules/0/data/discount_amount" xsi:type="string">10</data> + <data name="cartPrice/sub_total" xsi:type="string">125</data> + <data name="cartPrice/grand_total" xsi:type="string">130</data> + <data name="productPrice/0/special" xsi:type="string">90</data> + <data name="productPrice/0/regular" xsi:type="string">100</data> + <data name="productPrice/0/discount_amount" xsi:type="string">10</data> + <data name="productPrice/0/sub_total" xsi:type="string">125</data> + <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="shipping/shipping_method" xsi:type="string">Fixed</data> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedProductPage" /> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedShoppingCart" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/DeleteCatalogPriceRuleEntityTest.php b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/DeleteCatalogPriceRuleEntityTest.php index a2d0ff9500d4c4c8ac62bf2e4b9f54e6fcc5c4f4..fb4d1946dd2d8183203ef0ad03242e8217d3a82e 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/DeleteCatalogPriceRuleEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/DeleteCatalogPriceRuleEntityTest.php @@ -10,6 +10,7 @@ use Magento\CatalogRule\Test\Fixture\CatalogRule; use Magento\CatalogRule\Test\Page\Adminhtml\CatalogRuleIndex; use Magento\CatalogRule\Test\Page\Adminhtml\CatalogRuleNew; use Magento\Mtf\TestCase\Injectable; +use Magento\Customer\Test\Fixture\Customer; /** * Test Creation for Delete CatalogPriceRuleEntity. @@ -17,6 +18,7 @@ use Magento\Mtf\TestCase\Injectable; * Test Flow: * Preconditions: * 1. Catalog Price Rule is created. + * 2. Customer is created if needed. * Steps: * 1. Log in as default admin user. * 2. Go to Marketing > Catalog Price Rules. @@ -25,7 +27,7 @@ use Magento\Mtf\TestCase\Injectable; * 5. Perform all assertions. * * @group Catalog_Price_Rules - * @ZephyrId MAGETWO-25211 + * @ZephyrId MAGETWO-25211, MAGETWO-20431 */ class DeleteCatalogPriceRuleEntityTest extends Injectable { @@ -46,7 +48,7 @@ class DeleteCatalogPriceRuleEntityTest extends Injectable * @var CatalogRuleNew */ protected $catalogRuleNew; - + /** * Injection data. * @@ -67,13 +69,18 @@ class DeleteCatalogPriceRuleEntityTest extends Injectable * * @param CatalogRule $catalogPriceRule * @param string $product + * @param Customer|null $customer * @return array */ - public function test(CatalogRule $catalogPriceRule, $product) + public function test(CatalogRule $catalogPriceRule, $product, Customer $customer = null) { // Precondition $catalogPriceRule->persist(); + if ($customer) { + $customer->persist(); + } + $filter = [ 'name' => $catalogPriceRule->getName(), 'rule_id' => $catalogPriceRule->getId(), diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/DeleteCatalogPriceRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/DeleteCatalogPriceRuleEntityTest.xml index ed469088ca3c412f064083b81d97da571ccfce8c..6608ccd9a95b55e344a091055d10a9f09d18795b 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/DeleteCatalogPriceRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/DeleteCatalogPriceRuleEntityTest.xml @@ -7,15 +7,22 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogRule\Test\TestCase\DeleteCatalogPriceRuleEntityTest" summary="Delete Catalog Price Rule" ticketId="MAGETWO-25211"> - <variation name="CatalogRule_Delete_AdminOnly_ProductSimple" summary="Assert that Catalog Price Rule is not applied for simple product"> + <variation name="CatalogRule_Delete_AdminOnly_ProductSimple" summary="Assert that Catalog Price Rule is not applied for simple product" ticketId="MAGETWO-20431"> <data name="catalogPriceRule/dataset" xsi:type="string">active_catalog_price_rule_with_conditions</data> <data name="product" xsi:type="string">catalogProductSimple::simple_for_salesrule_1</data> - <data name="productPrice/0/regular" xsi:type="string">100</data> + <data name="customer/dataset" xsi:type="string">customer_US</data> + <data name="productPrice/0/regular" xsi:type="number">100</data> + <data name="cartPrice/sub_total" xsi:type="number">100</data> + <data name="cartPrice/grand_total" xsi:type="number">105</data> + <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="shipping/shipping_method" xsi:type="string">Fixed</data> + <data name="payment/method" xsi:type="string">checkmo</data> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleSuccessDeleteMessage" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleNotInGrid" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleNotAppliedCatalogPage" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleNotAppliedProductPage" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleNotAppliedShoppingCart" /> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleOnOnepageCheckout" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/UpdateCatalogPriceRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/UpdateCatalogPriceRuleEntityTest.xml index a9c1ecc9d9e0bc70a289a1414d5f4feb3ce62491..b92ae2a2e14ff26e7d0acf6fa9aa5d34c61370c1 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/UpdateCatalogPriceRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/UpdateCatalogPriceRuleEntityTest.xml @@ -44,7 +44,7 @@ <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedCatalogPage" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedProductPage" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedShoppingCart" /> - <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedOnepageCheckout" /> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleOnOnepageCheckout" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRuleConfigurable/Test/TestCase/ApplyConfigurableProductCatalogPriceRulesTest.php b/dev/tests/functional/tests/app/Magento/CatalogRuleConfigurable/Test/TestCase/ApplyConfigurableProductCatalogPriceRulesTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b4a3b0a98d478460b8253031d390cfeaa8dfb29f --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/CatalogRuleConfigurable/Test/TestCase/ApplyConfigurableProductCatalogPriceRulesTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogRuleConfigurable\Test\TestCase; + +use Magento\CatalogRule\Test\TestCase\ApplyCatalogPriceRulesTest; +use Magento\CatalogRule\Test\Fixture\CatalogRule; +use Magento\Customer\Test\Fixture\Customer; +use Magento\Mtf\Util\Command\Cli\Cron; +use Magento\CatalogRule\Test\Page\Adminhtml\CatalogRuleEdit; +use Magento\Mtf\TestStep\TestStepFactory; +use Magento\Mtf\Fixture\FixtureInterface; + +/** + * Preconditions: + * 1. Delete all active catalog price rules. + * 2. Create catalog price rule from dataset using Curl. + * + * Steps: + * 1. Apply all created rules. + * 2. Create configurable product. + * 3. Perform all assertions. + * + * @group Catalog_Rule_Configurable + * @ZephyrId MAGETWO-24780 + */ +class ApplyConfigurableProductCatalogPriceRulesTest extends ApplyCatalogPriceRulesTest +{ + /** + * Add attribute_id to catalog price rule. + * + * @param FixtureInterface $product + * @return array + */ + protected function getAttribute(FixtureInterface $product) + { + if ($product->hasData('configurable_attributes_data')) { + $attributes = $product->getDataFieldConfig('configurable_attributes_data')['source'] + ->getAttributesData()['attribute_key_0']; + $result['%attribute_id%'] = $attributes['attribute_code']; + $result['%attribute_value%'] = $attributes['options']['option_key_' . $this->promo]['id']; + return $result; + } else { + return parent::getAttribute($product); + } + } +} diff --git a/dev/tests/functional/tests/app/Magento/CatalogRuleConfigurable/Test/TestCase/ApplyConfigurableProductCatalogPriceRulesTest.xml b/dev/tests/functional/tests/app/Magento/CatalogRuleConfigurable/Test/TestCase/ApplyConfigurableProductCatalogPriceRulesTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..d156327d2f71d447af996713ae0fd6ab645c780c --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/CatalogRuleConfigurable/Test/TestCase/ApplyConfigurableProductCatalogPriceRulesTest.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\CatalogRuleConfigurable\Test\TestCase\ApplyConfigurableProductCatalogPriceRulesTest" summary="Apply Catalog Price Rules"> + <variation name="ApplyCatalogRuleForComfigurableProductWithOptions1" summary="Apply Catalog Rule For Configurable Product With Promo Simple Product" ticketId="MAGETWO-47186"> + <data name="products/0" xsi:type="string">configurableProduct::Stellar_Solar_Jacket_SIZE_S</data> + <data name="products/1" xsi:type="string">configurableProduct::Stellar_Solar_Jacket_SIZE_M</data> + <data name="products/2" xsi:type="string">configurableProduct::Stellar_Solar_Jacket_SIZE_L</data> + <data name="promo" xsi:type="string">2</data> + <data name="catalogRules/0/data/name" xsi:type="string">Catalog Price Rule %isolation%</data> + <data name="catalogRules/0/data/is_active" xsi:type="string">Active</data> + <data name="catalogRules/0/data/website_ids/option_0" xsi:type="string">Main Website</data> + <data name="catalogRules/0/data/customer_group_ids/option_0" xsi:type="string">NOT LOGGED IN</data> + <data name="catalogRules/0/data/rule" xsi:type="string">[Attribute|%attribute_id%|is|%attribute_value%]</data> + <data name="catalogRules/0/data/simple_action" xsi:type="string">Apply as fixed amount</data> + <data name="catalogRules/0/data/discount_amount" xsi:type="string">0.55</data> + <data name="cartPrice/sub_total" xsi:type="string">224.45</data> + <data name="cartPrice/grand_total" xsi:type="string">239.45</data> + <data name="productPrice/0/special" xsi:type="string">74.45</data> + <data name="productPrice/0/sub_total" xsi:type="string">75</data> + <data name="productPrice/0/regular" xsi:type="string">No</data> + <data name="productPrice/1/special" xsi:type="string">74.45</data> + <data name="productPrice/1/sub_total" xsi:type="string">75</data> + <data name="productPrice/1/regular" xsi:type="string">No</data> + <data name="productPrice/2/special" xsi:type="string">74.45</data> + <data name="productPrice/2/sub_total" xsi:type="string">74.45</data> + <data name="productPrice/2/regular" xsi:type="string">No</data> + <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="shipping/shipping_method" xsi:type="string">Fixed</data> + <data name="shippingAddress/dataset" xsi:type="string">UK_address</data> + <data name="payment/method" xsi:type="string">checkmo</data> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedProductPage" /> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedShoppingCart" /> + </variation> + <variation name="ApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts" summary="Apply Catalog Rule For Configurable Product With Custom Options" ticketId="MAGETWO-23042"> + <data name="products/0" xsi:type="string">configurableProduct::first_product_with_custom_options_and_option_key_1</data> + <data name="products/1" xsi:type="string">configurableProduct::first_product_with_custom_options_and_option_key_2</data> + <data name="products/2" xsi:type="string">configurableProduct::second_product_with_custom_options_and_option_key_1</data> + <data name="products/3" xsi:type="string">configurableProduct::second_product_with_custom_options_and_option_key_2</data> + <data name="customer/dataset" xsi:type="string">customer_US</data> + <data name="customer/data/group_id/dataset" xsi:type="string">default</data> + <data name="catalogRules/0/data/customer_group_ids/option_0" xsi:type="string">NOT LOGGED IN</data> + <data name="catalogRules/0/data/name" xsi:type="string">CatalogPriceRule %isolation%</data> + <data name="catalogRules/0/data/is_active" xsi:type="string">Active</data> + <data name="catalogRules/0/data/website_ids/option_0" xsi:type="string">Main Website</data> + <data name="catalogRules/0/data/simple_action" xsi:type="string">Apply as fixed amount</data> + <data name="catalogRules/0/data/stop_rules_processing" xsi:type="string">Yes</data> + <data name="catalogRules/0/data/discount_amount" xsi:type="string">5</data> + <data name="cartPrice/sub_total" xsi:type="string">382</data> + <data name="cartPrice/grand_total" xsi:type="string">402</data> + <data name="productPrice/0/special" xsi:type="string">15.01</data> + <data name="productPrice/0/sub_total" xsi:type="string">86.99</data> + <data name="productPrice/0/regular" xsi:type="string">No</data> + <data name="productPrice/1/special" xsi:type="string">15.01</data> + <data name="productPrice/1/sub_total" xsi:type="string">163.99</data> + <data name="productPrice/1/regular" xsi:type="string">No</data> + <data name="productPrice/2/special" xsi:type="string">15.01</data> + <data name="productPrice/2/sub_total" xsi:type="string">27.01</data> + <data name="productPrice/2/regular" xsi:type="string">No</data> + <data name="productPrice/3/special" xsi:type="string">15.01</data> + <data name="productPrice/3/sub_total" xsi:type="string">104.01</data> + <data name="productPrice/3/regular" xsi:type="string">No</data> + <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="shipping/shipping_method" xsi:type="string">Fixed</data> + <data name="shippingAddress/dataset" xsi:type="string">UK_address</data> + <data name="payment/method" xsi:type="string">checkmo</data> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedProductPage" /> + <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleAppliedShoppingCart" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/Form.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/Form.php index e65838bb7fc55dc5c564a4136f664e831cd65a78..eb7f352edfd93aff427b3a41745823febb977b0b 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/Form.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/Form.php @@ -78,6 +78,10 @@ class Form extends ParentForm $data = array_merge($data, $data['price']); unset($data['price']); } + if (isset($data['additional_attributes'])) { + $data = array_merge($data, $data['additional_attributes']); + unset($data['additional_attributes']); + } // Mapping $mapping = $this->dataMapping($data); @@ -86,12 +90,19 @@ class Form extends ParentForm /** @var CatalogProductAttribute $attribute */ $attribute = $fixture->getDataFieldConfig('custom_attribute')['source']->getAttribute(); $attributeType = $attribute->getFrontendInput(); + if ($attributeType == 'Text Area') { + $attributeType = 'Text Field'; + } $attributeCode = $attribute->getAttributeCode(); } if ($this->hasRender($attributeType)) { $element = $this->_rootElement->find(sprintf($this->customAttributeSelector, $attributeCode)); $arguments = ['fixture' => $fixture, 'element' => $element, 'mapping' => $mapping]; $this->callRender($attributeType, 'fill', $arguments); + } elseif ($attributeType == 'Price') { + $value = $data['custom_attribute']['value']; + $this->_rootElement->find('#' . $attributeCode)->setValue($value); + $this->_rootElement->find('#' . $attributeCode . '_to')->setValue($value); } else { $this->_fill($mapping, $element); } diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/Form.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/Form.xml index 5cbcfdf974099c89fee848d01a73d82183e2f204..ba4035f0dbf8345a13e46c4d3d256dc1c2ae16e0 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/Form.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/Form.xml @@ -25,5 +25,11 @@ <price_to> <selector>#price_to</selector> </price_to> + <weight_from> + <selector>#weight</selector> + </weight_from> + <weight_to> + <selector>#weight_to</selector> + </weight_to> </fields> </mapping> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/SearchResultsTitle.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/SearchResultsTitle.php new file mode 100644 index 0000000000000000000000000000000000000000..d8ff977e14769804556b6269f7ff02402d98d1f4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Block/Advanced/SearchResultsTitle.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Test\Block\Advanced; + +use Magento\Mtf\Block\Block; +use Magento\Mtf\Client\Locator; + +/** + * Block for search results page title. + */ +class SearchResultsTitle extends Block +{ + /** + * CSS selector for block 'Search results for'. + * + * @var string + */ + protected $searchResultsFor = '[data-ui-id="page-title-wrapper"]'; + + /** + * Getting actual search query value. + * + * @return string|null + */ + public function getSearchQuery() + { + $searchQueryResult = $this->_rootElement->find(sprintf($this->searchResultsFor), Locator::SELECTOR_CSS) + ->getText(); + preg_match("~Search results for: \'(.*)\'~", $searchQueryResult, $matches); + $query = isset($matches[1]) ? $matches[1] : null; + return $query; + } +} diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchProductByAttribute.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchProductByAttribute.php index be2d2d508a3f9f4d611400eb6bcfb281c35c94bb..6af1b07f132e40a70b7d75d92606513d811ec731 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchProductByAttribute.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchProductByAttribute.php @@ -34,6 +34,7 @@ class AssertAdvancedSearchProductByAttribute extends AbstractConstraint * @param AdvancedSearch $searchPage * @param CatalogsearchResult $catalogSearchResult * @param FixtureFactory $fixtureFactory + * @param int|null $attributeValue * @return void */ public function processAssert( @@ -41,13 +42,14 @@ class AssertAdvancedSearchProductByAttribute extends AbstractConstraint InjectableFixture $product, AdvancedSearch $searchPage, CatalogsearchResult $catalogSearchResult, - FixtureFactory $fixtureFactory + FixtureFactory $fixtureFactory, + $attributeValue = null ) { $this->fixtureFactory = $fixtureFactory; $cmsIndex->open(); $cmsIndex->getFooterBlock()->openAdvancedSearch(); $searchForm = $searchPage->getForm(); - $productSearch = $this->prepareFixture($product); + $productSearch = $this->prepareFixture($product, $attributeValue); $searchForm->fill($productSearch); $searchForm->submit(); @@ -63,11 +65,15 @@ class AssertAdvancedSearchProductByAttribute extends AbstractConstraint * Preparation of fixture data before comparing. * * @param InjectableFixture $productSearch + * @param int|null $attributeValue * @return CatalogProductSimple */ - protected function prepareFixture(InjectableFixture $productSearch) + protected function prepareFixture(InjectableFixture $productSearch, $attributeValue) { $customAttribute = $productSearch->getDataFieldConfig('custom_attribute')['source']->getAttribute(); + if ($attributeValue !== null) { + $customAttribute = ['value' => $attributeValue, 'attribute' => $customAttribute]; + } return $this->fixtureFactory->createByCode( 'catalogProductSimple', ['data' => ['custom_attribute' => $customAttribute]] diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchProductResult.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchProductResult.php new file mode 100644 index 0000000000000000000000000000000000000000..1f09f71284c15f598f10c0247570ecfd6edbc049 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertAdvancedSearchProductResult.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Test\Constraint; + +use Magento\CatalogSearch\Test\Page\AdvancedResult; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that Advanced Search result page contains only product(s) according to requested from fixture. + */ +class AssertAdvancedSearchProductResult extends AbstractConstraint +{ + /** + * Text for founded product. + */ + const FOUNDED_PRODUCT_MESSAGE = 'Product %s is founded'; + + /** + * Assert that Advanced Search result page contains only product(s) according to requested from fixture. + * + * @param array $isVisibleInAdvancedSearch + * @param array $allProducts + * @param AdvancedResult $resultPage + * @return void + */ + public function processAssert( + array $isVisibleInAdvancedSearch, + array $allProducts, + AdvancedResult $resultPage + ) { + $expectedResult = $this->prepareExpectedResult($isVisibleInAdvancedSearch, $allProducts); + $foundedProducts = $this->advancedSearchProducts($resultPage, $allProducts); + \PHPUnit_Framework_Assert::assertEquals( + $expectedResult, + $foundedProducts, + 'Expected and founded products not the same.' + . "\nExpected: " . print_r($expectedResult) + . "\nActual: " . print_r($foundedProducts) + ); + } + + /** + * Returns array with expected products. + * + * @param array $isVisibleInAdvancedSearch + * @param array $products + * @return array + */ + private function prepareExpectedResult(array $isVisibleInAdvancedSearch, array $products) + { + $expectedResult = []; + foreach ($isVisibleInAdvancedSearch as $key => $value) { + if ($value == "Yes") { + $expectedResult[] = sprintf(self::FOUNDED_PRODUCT_MESSAGE, $products[$key]->getName()); + } + } + sort($expectedResult); + return $expectedResult; + } + + /** + * Returns array with found products. + * + * @param AdvancedResult $resultPage + * @param array $allProducts + * @return array + */ + private function advancedSearchProducts(AdvancedResult $resultPage, array $allProducts) + { + $products = $allProducts; + $foundedProducts = []; + do { + $dirtKeys = []; + foreach ($allProducts as $key => $product) { + $isProductVisible = $resultPage->getListProductBlock()->getProductItem($product)->isVisible(); + if ($isProductVisible) { + $foundedProducts[] = sprintf(self::FOUNDED_PRODUCT_MESSAGE, $products[$key]->getName()); + $dirtKeys[] = $key; + } + } + foreach ($dirtKeys as $key) { + unset($products[$key]); + } + } while ($resultPage->getBottomToolbar()->nextPage() && (count($products) > 0)); + sort($foundedProducts); + return $foundedProducts; + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'All products are involved in the search were found successfully.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertCatalogSearchQueryLength.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertCatalogSearchQueryLength.php new file mode 100644 index 0000000000000000000000000000000000000000..831becb6f73c91fafe8e6cd57bf396cd6051647c --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertCatalogSearchQueryLength.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\CatalogSearch\Test\Page\CatalogsearchResult; + +/** + * Assert that search query length truncated to 128 symbols. + */ +class AssertCatalogSearchQueryLength extends AbstractConstraint +{ + /** + * Assert that search query length truncated to 128 symbols. + * + * @param CatalogsearchResult $catalogSearchResult + * @return void + */ + public function processAssert(CatalogsearchResult $catalogSearchResult) + { + \PHPUnit_Framework_Assert::assertEquals( + mb_strlen($catalogSearchResult->getSearchResultsTitleBlock()->getSearchQuery()), + 128, + 'Search query length is not truncated to 128 symbols.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Search query truncated to 128 symbols.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchAttributeTest.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchAttributeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c2a719752e8d93f27470029963ce12b329ce89ef --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Constraint/AssertSearchAttributeTest.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Test\Constraint; + +use Magento\CatalogSearch\Test\Page\AdvancedSearch; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert advanced attribute is present(or absent) in Advanced Search Page. + */ +class AssertSearchAttributeTest extends AbstractConstraint +{ + /** + * Assert advanced attribute is present(or absent) in Advanced Search Page. + * + * @param AdvancedSearch $advancedSearch + * @param array $attributeForSearch + * @return void + */ + public function processAssert( + AdvancedSearch $advancedSearch, + array $attributeForSearch + ) { + $advancedSearch->open(); + $availableAttributes = $advancedSearch->getForm()->getFormLabels(); + if (isset($attributeForSearch['isVisible'])) { + \PHPUnit_Framework_Assert::assertTrue( + (false !== array_search($attributeForSearch['name'], $availableAttributes)), + 'Attribute ' . $attributeForSearch['name'] . 'was not found in Advanced Search Page.' + ); + } else { + \PHPUnit_Framework_Assert::assertTrue( + (false == array_search($attributeForSearch['name'], $availableAttributes)), + 'Attribute ' . $attributeForSearch['name'] . ' was found in Advanced Search Page.' + ); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Attribute was found in Advanced Search Page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Page/CatalogsearchResult.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Page/CatalogsearchResult.xml index 49ee4cd35e9c8878489f1621b281be74af1351c5..bc8931b7b325dc057f20b9706ad6e0c70882b273 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Page/CatalogsearchResult.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Page/CatalogsearchResult.xml @@ -10,5 +10,6 @@ <block name="listProductBlock" class="Magento\Catalog\Test\Block\Product\ListProduct" locator=".search.results" strategy="css selector" /> <block name="bottomToolbar" class="Magento\Catalog\Test\Block\Product\ProductList\BottomToolbar" locator=".//*[contains(@class,'toolbar-products')][2]" strategy="xpath" /> <block name="searchResultBlock" class="Magento\CatalogSearch\Test\Block\Advanced\Result" locator=".column.main" strategy="css selector" /> + <block name="searchResultsTitleBlock" class="Magento\CatalogSearch\Test\Block\Advanced\SearchResultsTitle" locator=".page-title-wrapper" strategy="css selector" /> </page> </config> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.php index c845e7f0bfe85958c20136ebcf0da30f53d31874..5848a6cfd7ea6ab178cfdfa8263e0a7c0bbf92a6 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.php @@ -20,7 +20,7 @@ use Magento\Mtf\TestCase\Injectable; * 3. Perform all assertions. * * @group Search_Frontend - * @ZephyrId MAGETWO-25095 + * @ZephyrId MAGETWO-25095, MAGETWO-36542 */ class SearchEntityResultsTest extends Injectable { @@ -51,11 +51,12 @@ class SearchEntityResultsTest extends Injectable * Run searching result test. * * @param CatalogSearchQuery $catalogSearch + * @param string|null $queryLength * @return void */ - public function test(CatalogSearchQuery $catalogSearch) + public function test(CatalogSearchQuery $catalogSearch, $queryLength = null) { $this->cmsIndex->open(); - $this->cmsIndex->getSearchBlock()->search($catalogSearch->getQueryText()); + $this->cmsIndex->getSearchBlock()->search($catalogSearch->getQueryText(), $queryLength); } } diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml index 3f8e5191a80f745ae5f5204c5d2cb1361fe3bd09..7851e6b697d3efcd193bc8340e3d4517574d2676 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml @@ -60,5 +60,27 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchNoResultMessage" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchNoResult" /> </variation> + <variation name="SearchEntityResultsTestVariation12" summary="Search for simple product name using 2 symbols query length" ticketId="MAGETWO-36542"> + <data name="catalogSearch/data/query_text/value" xsi:type="string">catalogProductSimple::default::name</data> + <data name="queryLength" xsi:type="string">2</data> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchNoResultMessage" /> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchNoResult" /> + </variation> + <variation name="SearchEntityResultsTestVariation13" summary="Search for simple product name using 3 symbols query length" ticketId="MAGETWO-36542"> + <data name="catalogSearch/data/query_text/value" xsi:type="string">catalogProductSimple::default::name</data> + <data name="queryLength" xsi:type="string">3</data> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertProductCanBeOpenedFromSearchResult" /> + </variation> + <variation name="SearchEntityResultsTestVariation14" summary="Search for simple product name using 128 symbols query length" ticketId="MAGETWO-36542"> + <data name="catalogSearch/data/query_text/value" xsi:type="string">catalogProductSimple::product_with_long_name::name</data> + <data name="queryLength" xsi:type="string">128</data> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertProductCanBeOpenedFromSearchResult" /> + </variation> + <variation name="SearchEntityResultsTestVariation15" summary="Search for simple product name using 129 symbols query length" ticketId="MAGETWO-36542"> + <data name="catalogSearch/data/query_text/value" xsi:type="string">catalogProductSimple::product_with_long_name::name</data> + <data name="queryLength" xsi:type="string">129</data> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchQueryLength" /> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertProductCanBeOpenedFromSearchResult" /> + </variation> </testCase> </config> 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 a10d28f5e70e35a6e64abf63bf5f69df99823eb8..036f984441457cc2150597a5c3cc79f6e91b52c6 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 @@ -51,6 +51,13 @@ class Shipping extends Form */ protected $estimationFields = ['country_id', 'region_id', 'region', 'postcode']; + /** + * Selector for top destinations in country field. + * + * @var string + */ + private $topOptions = './option[@value="delimiter"]/preceding-sibling::option[string(@value)]'; + /** * Block wait element. * @@ -59,7 +66,7 @@ class Shipping extends Form protected $blockWaitElement = '._block-content-loading'; /** - * Get shipping price selector for exclude and include price + * Get shipping price selector for exclude and include price. * * @var string */ @@ -77,6 +84,29 @@ class Shipping extends Form } } + /** + * Get countries displayed at the top of country element. + * + * @return array + */ + public function getTopCountries() + { + $this->openEstimateShippingAndTax(); + $mapping = $this->dataMapping(array_flip(['country_id'])); + $countryField = $this->getElement($this->_rootElement, $mapping['country_id']); + $this->_rootElement->waitUntil( + function () use ($countryField) { + return $countryField->isVisible() ? true : null; + } + ); + return array_map( + function ($option) { + return $option->getAttribute('value'); + }, + $countryField->getElements($this->topOptions, Locator::SELECTOR_XPATH) + ); + } + /** * Select shipping method. * @@ -168,7 +198,7 @@ class Shipping extends Form } /** - * Wait for common shipping price block to appear + * Wait for common shipping price block to appear. * * @return void */ diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php index fe844bc08b0017b2aa1cabfe4a7882b3733c118a..6b7016f1b99bfd4123fe7ca80d8727b9bc6bff2d 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php @@ -16,7 +16,7 @@ use Magento\Mtf\Client\Locator; class Shipping extends Form { /** - * CSS Selector for "New Address" button + * CSS Selector for "New Address" button. * * @var string */ @@ -69,6 +69,65 @@ class Shipping extends Form */ private $selectedShippingAddressBlock = '.selected-item'; + /** + * Email instructions selector. + * + * @var string + */ + private $emailInstructions = '#customer-email-fieldset .note span'; + + /** + * Email tooltip button selector. + * + * @var string + */ + private $emailTooltipButton = '#customer-email-fieldset .field-tooltip-action'; + + /** + * Email tooltip content selector. + * + * @var string + */ + private $emailTooltipContent = '#customer-email-fieldset .field-tooltip-content'; + + /** + * Email error selector. + * + * @var string + */ + private $emailError = '#customer-email-error'; + + /** + * Get email error. + * + * @return string + */ + public function getEmailError() + { + return $this->_rootElement->find($this->emailError)->getText(); + } + + /** + * Get email tooltip. + * + * @return string + */ + public function getEmailTooltip() + { + $this->_rootElement->find($this->emailTooltipButton)->click(); + return $this->_rootElement->find($this->emailTooltipContent)->getText(); + } + + /** + * Get email instructions. + * + * @return string + */ + public function getEmailInstructions() + { + return $this->_rootElement->find($this->emailInstructions)->getText(); + } + /** * Click on "New Address" button. * diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php index 24bddf79c1a07c9205e0b22426946e11b92d0489..cbddb26781479302f962141723886df497d43237 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/Method.php @@ -21,6 +21,13 @@ class Method extends Block */ protected $shippingMethod = './/tbody//tr[td[contains(., "%s")] and td[contains(., "%s")]]//input'; + /** + * Shipping method amount selector. + * + * @var string + */ + private $shippingMethodAmount = './/tr[td[contains(., "%s")] and td[contains(., "%s")]]//span[@class="price"]'; + /** * Continue checkout button. * @@ -108,4 +115,16 @@ class Method extends Block } ); } + + /** + * Get shipping method amount. + * + * @param array $method + * @return string + */ + public function getShippingMethodAmount(array $method) + { + $selector = sprintf($this->shippingMethodAmount, $method['shipping_method'], $method['shipping_service']); + return $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->getText(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonAbsentOnCategoryPage.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonAbsentOnCategoryPage.php new file mode 100644 index 0000000000000000000000000000000000000000..183b28d9c46d24bbd6a6cd1de7cb04635d7e0c22 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonAbsentOnCategoryPage.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\Constraint; + +use Magento\Mtf\Fixture\InjectableFixture; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Catalog\Test\Fixture\Category; +use Magento\Catalog\Test\Page\Category\CatalogCategoryView; +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Cms\Test\Page\CmsIndex; + +/** + * Checks that "Add to Cart" button is absent on category page. + */ +class AssertAddToCartButtonAbsentOnCategoryPage extends AbstractConstraint +{ + /** + * Assert that "Add to Cart" button is absent on category page. + * + * @param InjectableFixture $product + * @param CmsIndex $cmsIndex + * @param CatalogCategoryView $catalogCategoryView + * @param Category|null $category [optional] + * @return void + */ + public function processAssert( + InjectableFixture $product, + CmsIndex $cmsIndex, + CatalogCategoryView $catalogCategoryView, + Category $category = null + ) { + $cmsIndex->open(); + $categoryName = $category === null ? $product->getCategoryIds()[0] : $category->getName(); + $cmsIndex->getTopmenu()->selectCategoryByName($categoryName); + + $isProductVisible = $catalogCategoryView->getListProductBlock()->getProductItem($product)->isVisible(); + while (!$isProductVisible && $catalogCategoryView->getBottomToolbar()->nextPage()) { + $isProductVisible = $catalogCategoryView->getListProductBlock()->getProductItem($product)->isVisible(); + } + \PHPUnit_Framework_Assert::assertTrue($isProductVisible, 'Product is absent on category page.'); + + \PHPUnit_Framework_Assert::assertFalse( + $catalogCategoryView->getListProductBlock()->getProductItem($product)->isVisibleAddToCardButton(), + 'Button "Add to Cart" is present on category page.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Button "Add to Cart" is absent on product page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonAbsentOnProductPage.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonAbsentOnProductPage.php new file mode 100644 index 0000000000000000000000000000000000000000..fb7db2c361595d9e7caea6dd71689725d64b5545 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonAbsentOnProductPage.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\InjectableFixture; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Catalog\Test\Page\Product\CatalogProductView; + +/** + * Checks that "Add to Cart" button is absent on product page. + */ +class AssertAddToCartButtonAbsentOnProductPage extends AbstractConstraint +{ + /** + * Assert that "Add to Cart" button is absent on product page. + * + * @param BrowserInterface $browser + * @param InjectableFixture $product + * @param CatalogProductView $catalogProductView + * @return void + */ + public function processAssert( + BrowserInterface $browser, + InjectableFixture $product, + CatalogProductView $catalogProductView + ) { + $browser->open($_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'); + \PHPUnit_Framework_Assert::assertFalse( + $catalogProductView->getViewBlock()->isVisibleAddToCardButton(), + 'Button "Add to Cart" is present on product page.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Button "Add to Cart" is absent on product page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonPresentOnCategoryPage.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonPresentOnCategoryPage.php new file mode 100644 index 0000000000000000000000000000000000000000..dfa8420d70fa788a3f229ef30acf31d2f6d36cd7 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonPresentOnCategoryPage.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\InjectableFixture; +use Magento\Catalog\Test\Fixture\Category; +use Magento\Catalog\Test\Page\Category\CatalogCategoryView; +use Magento\Cms\Test\Page\CmsIndex; + +/** + * Checks that "Add to Cart" button is visible on category page. + */ +class AssertAddToCartButtonPresentOnCategoryPage extends AbstractConstraint +{ + /** + * Assert that "Add to Cart" button is present on category page. + * + * @param InjectableFixture $product + * @param CmsIndex $cmsIndex + * @param CatalogCategoryView $catalogCategoryView + * @param Category|null $category [optional] + * @return void + */ + public function processAssert( + InjectableFixture $product, + CmsIndex $cmsIndex, + CatalogCategoryView $catalogCategoryView, + Category $category = null + ) { + $cmsIndex->open(); + $categoryName = $category === null ? $product->getCategoryIds()[0] : $category->getName(); + $cmsIndex->getTopmenu()->selectCategoryByName($categoryName); + + $isProductVisible = $catalogCategoryView->getListProductBlock()->getProductItem($product)->isVisible(); + while (!$isProductVisible && $catalogCategoryView->getBottomToolbar()->nextPage()) { + $isProductVisible = $catalogCategoryView->getListProductBlock()->getProductItem($product)->isVisible(); + } + \PHPUnit_Framework_Assert::assertTrue($isProductVisible, 'Product is absent on category page.'); + + \PHPUnit_Framework_Assert::assertTrue( + $catalogCategoryView->getListProductBlock()->getProductItem($product)->isVisibleAddToCardButton(), + 'Button "Add to Cart" is absent on category page.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Button "Add to Cart" is present on category page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonPresentOnProductPage.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonPresentOnProductPage.php new file mode 100644 index 0000000000000000000000000000000000000000..1590c98488b2ada80f5a141d8551c366863109e3 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertAddToCartButtonPresentOnProductPage.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\InjectableFixture; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Catalog\Test\Page\Product\CatalogProductView; + +/** + * Checks that "Add to Cart" button is visible on product page. + */ +class AssertAddToCartButtonPresentOnProductPage extends AbstractConstraint +{ + /** + * Assert that "Add to Cart" button is present on product page. + * + * @param BrowserInterface $browser + * @param InjectableFixture $product + * @param CatalogProductView $catalogProductView + * @return void + */ + public function processAssert( + BrowserInterface $browser, + InjectableFixture $product, + CatalogProductView $catalogProductView + ) { + $browser->open($_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'); + \PHPUnit_Framework_Assert::assertTrue( + $catalogProductView->getViewBlock()->isVisibleAddToCardButton(), + 'Button "Add to Cart" is absent on product page.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Button "Add to Cart" is present on product page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertEmailErrorValidationMessage.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertEmailErrorValidationMessage.php new file mode 100644 index 0000000000000000000000000000000000000000..01941682c328cdc5b5176e75245be65148f5bf84 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertEmailErrorValidationMessage.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\Constraint; + +use Magento\Checkout\Test\Page\CheckoutOnepage; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that email validation message is correct. + */ +class AssertEmailErrorValidationMessage extends AbstractConstraint +{ + /** + * Email validation message. + */ + const EMAIL_VALIDATION_MESSAGE = 'Please enter a valid email address (Ex: johndoe@domain.com).'; + + /** + * Assert that email validation message is correct. + * + * @param CheckoutOnepage $checkoutOnepage + * @return void + */ + public function processAssert( + CheckoutOnepage $checkoutOnepage + ) { + \PHPUnit_Framework_Assert::assertEquals( + self::EMAIL_VALIDATION_MESSAGE, + $checkoutOnepage->getShippingBlock()->getEmailError(), + 'Email validation message is not correct.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Email validation message is correct.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertEmailToolTips.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertEmailToolTips.php new file mode 100644 index 0000000000000000000000000000000000000000..974d1ba290da82ad9c0002d51ad0e7d52ea89ed4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertEmailToolTips.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\Constraint; + +use Magento\Checkout\Test\Page\CheckoutOnepage; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that email field tooltips are present. + */ +class AssertEmailToolTips extends AbstractConstraint +{ + /** + * Email tooltip message. + */ + const EMAIL_TOOLTIP = 'We\'ll send your order confirmation here.'; + + /** + * Email instructions message. + */ + const EMAIL_INSTRUCTIONS = 'You can create an account after checkout.'; + + /** + * Assert that email field tooltips are present. + * + * @param CheckoutOnepage $checkoutOnepage + * @return void + */ + public function processAssert( + CheckoutOnepage $checkoutOnepage + ) { + \PHPUnit_Framework_Assert::assertEquals( + self::EMAIL_TOOLTIP, + $checkoutOnepage->getShippingBlock()->getEmailTooltip(), + 'Email tooltip is not correct.' + ); + + \PHPUnit_Framework_Assert::assertEquals( + self::EMAIL_INSTRUCTIONS, + $checkoutOnepage->getShippingBlock()->getEmailInstructions(), + 'Email instructions are not correct.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Email field tooltips are present.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertTopDestinationsInSelect.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertTopDestinationsInSelect.php new file mode 100644 index 0000000000000000000000000000000000000000..340cdef2c0ea73044d066fcc1d6d66dd5c68520a --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertTopDestinationsInSelect.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\Constraint; + +use Magento\Checkout\Test\Page\CheckoutCart; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that countries selected as Top Destinations are at the top in Estimate Shipping and Tax block. + */ +class AssertTopDestinationsInSelect extends AbstractConstraint +{ + /** + * Assert top destinations in select in Estimate Shipping and Tax block. + * + * @param CheckoutCart $checkoutCart + * @param array $topDestinations + * @return void + */ + public function processAssert(CheckoutCart $checkoutCart, array $topDestinations) + { + $checkoutCart->open(); + \PHPUnit_Framework_Assert::assertEquals( + $topDestinations, + $checkoutCart->getShippingBlock()->getTopCountries(), + 'Top countries are different from the ones selected as Top Destinations.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Countries selected as Top Destinations are at the top in select.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.php index e41af50cca9f624c489dd8af497d601f0d41a426..0a11e63a8c2fb9825d25b2b9915404745a0b95b1 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.php @@ -15,13 +15,13 @@ use Magento\Mtf\TestCase\Injectable; /** * Preconditions: - * 1. All type products is created + * 1. All type products is created. * * Steps: - * 1. Navigate to frontend - * 2. Open test product page - * 3. Add to cart test product - * 4. Perform all asserts + * 1. Navigate to frontend. + * 2. Open test product page. + * 3. Add to cart test product. + * 4. Perform all asserts. * * @group Shopping_Cart * @ZephyrId MAGETWO-25382 @@ -34,35 +34,42 @@ class AddProductsToShoppingCartEntityTest extends Injectable /* end tags */ /** - * Browser interface + * Browser interface. * * @var BrowserInterface */ - protected $browser; + private $browser; /** - * Fixture factory + * Fixture factory. * * @var FixtureFactory */ - protected $fixtureFactory; + private $fixtureFactory; /** - * Catalog product view page + * Catalog product view page. * * @var CatalogProductView */ - protected $catalogProductView; + private $catalogProductView; /** - * Checkout cart page + * Checkout cart page. * * @var CheckoutCart */ protected $cartPage; /** - * Prepare test data + * Config settings. + * + * @var string + */ + private $configData; + + /** + * Prepare test data. * * @param BrowserInterface $browser * @param FixtureFactory $fixtureFactory @@ -83,15 +90,21 @@ class AddProductsToShoppingCartEntityTest extends Injectable } /** - * Run test add products to shopping cart + * Run test add products to shopping cart. * * @param array $productsData * @param array $cart + * @param string|null $configData [optional] * @return array */ - public function test(array $productsData, array $cart) + public function test(array $productsData, array $cart, $configData = null) { // Preconditions + $this->configData = $configData; + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData] + )->run(); $products = $this->prepareProducts($productsData); // Steps @@ -102,7 +115,7 @@ class AddProductsToShoppingCartEntityTest extends Injectable } /** - * Create products + * Create products. * * @param array $productList * @return array @@ -119,7 +132,7 @@ class AddProductsToShoppingCartEntityTest extends Injectable } /** - * Add products to cart + * Add products to cart. * * @param array $products * @return void @@ -132,4 +145,17 @@ class AddProductsToShoppingCartEntityTest extends Injectable ); $addToCartStep->run(); } + + /** + * Reset config settings to default. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.xml index cac5e0a9c2182a05e8925a375396b7fe6f610a99..616f10aef0878e4d1b4d2425876901c7c2386665 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.xml @@ -116,5 +116,18 @@ <constraint name="Magento\Checkout\Test\Constraint\AssertGrandTotalInShoppingCart" /> <constraint name="Magento\Checkout\Test\Constraint\AssertSubtotalInMiniShoppingCart" /> </variation> + <variation name="AddProductsToShoppingCartEntityTestVariation9" summary="Verify Top Destinations for Country Options configuration applied in shopping cart" ticketId="MAGETWO-38700"> + <data name="productsData/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> + <data name="configData" xsi:type="string">top_destinations_DE_ES_GB</data> + <data name="cart/data/grand_total" xsi:type="string">15.00</data> + <data name="cart/data/subtotal" xsi:type="string">10.00</data> + <data name="topDestinations" xsi:type="array"> + <item name="0" xsi:type="string">DE</item> + <item name="1" xsi:type="string">ES</item> + <item name="2" xsi:type="string">GB</item> + </data> + <data name="issue" xsi:type="string">MAGETWO-61592 - [Shopping Cart] Top destinations are not displayed in the shopping cart summary</data> + <constraint name="Magento\Checkout\Test\Constraint\AssertTopDestinationsInSelect" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml index 1b6f78148a129a1bd575fe12a91b99e7cbde87aa..0e40b7d5b2c15c2f342f8390e05b61d9ef771cf8 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml @@ -151,7 +151,7 @@ <data name="products/0" xsi:type="string">catalogProductVirtual::product_50_dollar</data> <data name="salesRule" xsi:type="string">active_sales_rule_with_fixed_price_discount_coupon</data> <data name="customer/dataset" xsi:type="string">default</data> - <data name="billingAddress/dataset" xsi:type="string">UK_address</data> + <data name="billingAddress/dataset" xsi:type="string">UK_address_without_email</data> <data name="checkoutMethod" xsi:type="string">guest</data> <data name="prices" xsi:type="array"> <item name="grandTotal" xsi:type="string">0.00</item> @@ -170,7 +170,7 @@ <data name="tag" xsi:type="string">severity:S1</data> <data name="products/0" xsi:type="string">catalogProductSimple::product_with_qty_25</data> <data name="checkoutMethod" xsi:type="string">guest</data> - <data name="shippingAddress/dataset" xsi:type="string">UK_address</data> + <data name="shippingAddress/dataset" xsi:type="string">UK_address_without_email</data> <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <data name="prices" xsi:type="array"> @@ -223,24 +223,25 @@ </variation> <variation name="OnePageCheckoutTestVariation10" summary="One Page Checkout with all product types"> <data name="tag" xsi:type="string">stable:no, severity:S0</data> - <data name="products/0" xsi:type="string">catalogProductVirtual::default</data> - <data name="products/1" xsi:type="string">downloadableProduct::with_two_separately_links</data> - <data name="products/2" xsi:type="string">configurableProduct::with_one_option</data> - <data name="products/3" xsi:type="string">bundleProduct::bundle_fixed_100_dollar_product</data> - <data name="products/4" xsi:type="string">catalogProductSimple::simple_10_dollar</data> - <data name="products/5" xsi:type="string">groupedProduct::three_simple_products</data> + <data name="products/0" xsi:type="string">catalogProductVirtual::buy_all</data> + <data name="products/1" xsi:type="string">downloadableProduct::with_two_separately_links_buy_all</data> + <data name="products/2" xsi:type="string">configurableProduct::with_one_option_buy_all</data> + <data name="products/3" xsi:type="string">bundleProduct::bundle_fixed_100_dollar_product_buy_all</data> + <data name="products/4" xsi:type="string">catalogProductSimple::simple_10_dollar_buy_all</data> + <data name="products/5" xsi:type="string">groupedProduct::three_simple_products_buy_all</data> <data name="customer/dataset" xsi:type="string">default</data> <data name="checkoutMethod" xsi:type="string">login</data> <data name="shippingAddress/dataset" xsi:type="string">UK_address_without_email</data> <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">2118.43</item> + <item name="grandTotal" xsi:type="string">3558.43</item> </data> <data name="payment/method" xsi:type="string">checkmo</data> - <data name="configData" xsi:type="string">checkmo</data> + <data name="configData" xsi:type="string">checkmo, display_out_of_stock</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerDefaultAddressFrontendAddressBook" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductsOutOfStock" /> </variation> <variation name="OnePageCheckoutUsingSingInLink" summary="Login during checkout using 'Sign In' link" ticketId="MAGETWO-42547"> <data name="tag" xsi:type="string">severity:S1</data> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0f9efecc46ea4296444b03ca198da11cff5c0bce --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\TestCase; + +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Checkout\Test\Page\CheckoutCart; +use Magento\Checkout\Test\Page\CheckoutOnepage; +use Magento\Customer\Test\Fixture\Customer; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\TestCase\Injectable; + +/** + * Precondition: + * 1. Simple product is created + * 2. Clear shopping cart + * + * Steps: + * 1. Go to Storefront as Guest + * 2. Add simple product to shopping cart + * 3. Go to Checkout + * 4. Enter the email according to the data set + * 5. Perform assertions + * + * @ZephyrId MAGETWO-42543 + */ +class ValidateEmailOnCheckoutTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + /* end tags */ + + /** + * Validate email on checkout. + * + * @param CatalogProductSimple $product + * @param CheckoutCart $cartPage + * @param CatalogProductView $catalogProductView + * @param BrowserInterface $browser + * @param CheckoutOnepage $checkoutOnepage + * @param Customer $customer + * @return void + */ + public function test( + CatalogProductSimple $product, + CheckoutCart $cartPage, + CatalogProductView $catalogProductView, + BrowserInterface $browser, + CheckoutOnepage $checkoutOnepage, + Customer $customer + ) { + //Preconditions + $product->persist(); + + $cartPage->open(); + $cartPage->getCartBlock()->clearShoppingCart(); + + //Steps + $browser->open($_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'); + $productView = $catalogProductView->getViewBlock(); + $productView->fillOptions($product); + $productView->setQty($product->getCheckoutData()['qty']); + $productView->clickAddToCart(); + $catalogProductView->getMessagesBlock()->waitSuccessMessage(); + + $checkoutOnepage->open(); + $checkoutOnepage->getShippingBlock()->fill($customer); + $checkoutOnepage->getShippingMethodBlock()->clickContinue(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..2e32affe4fbcaf645698208d29a86f5d1b85c422 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Checkout\Test\TestCase\ValidateEmailOnCheckoutTest" summary="Email validation for Guest on checkout flow" ticketId="MAGETWO-42543"> + <variation name="ValidateEmailOnCheckoutTestVariation1"> + <data name="customer/data/email" xsi:type="string">johndoe</data> + <data name="customer/data/firstname" xsi:type="string">John</data> + <constraint name="Magento\Checkout\Test\Constraint\AssertEmailErrorValidationMessage" /> + <constraint name="Magento\Checkout\Test\Constraint\AssertEmailToolTips" /> + </variation> + <variation name="ValidateEmailOnCheckoutTestVariation2"> + <data name="customer/data/email" xsi:type="string">johndoe#example.com</data> + <data name="customer/data/firstname" xsi:type="string">John</data> + <constraint name="Magento\Checkout\Test\Constraint\AssertEmailErrorValidationMessage" /> + </variation> + <variation name="ValidateEmailOnCheckoutTestVariation3"> + <data name="customer/data/email" xsi:type="string">johndoe@example.c</data> + <data name="customer/data/firstname" xsi:type="string">John</data> + <constraint name="Magento\Checkout\Test\Constraint\AssertEmailErrorValidationMessage" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php index 720965212ad38d6d249d7ef4f1634e2c43f5deb2..c695b96cf687b9b8f1e2f7d88ab16f05a6185576 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php @@ -112,7 +112,7 @@ class FillBillingInformationStep implements TestStepInterface */ public function run() { - $billingAddress = null; + $billingAddress = $this->billingAddress; if ($this->billingCheckboxState) { $this->assertBillingAddressCheckbox->processAssert($this->checkoutOnepage, $this->billingCheckboxState); } diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php index 9d7386380a09013b0a540df5bd4faf4d41a384b7..a46203cce370fdb89fc4fc103734e03928676e99 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/PageForm.php @@ -28,6 +28,13 @@ class PageForm extends FormTabs */ protected $contentForm = "#page_content"; + /** + * Cms page loader. + * + * @var string + */ + protected $loader = "data-role='loader'"; + /** * Page Content Show/Hide Editor toggle button. * @@ -76,4 +83,19 @@ class PageForm extends FormTabs }; return $this; } + + /** + * Check if block with system variables is visible. + * + * @return bool + */ + public function isVariablesBlockVisible() + { + $this->openTab('content'); + /** @var \Magento\Cms\Test\Block\Adminhtml\Page\Edit\Tab\Content $contentTab */ + $contentTab = $this->getTab('content'); + $contentTab->clickInsertVariable(); + $this->waitForElementNotVisible($this->loader); + return $contentTab->isVariablesBlockVisible(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/Tab/Content.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/Tab/Content.php index a4f1bd4d1f85e2147472f21867d29f38bab437d0..0568d53b4580bdc34327c96e0c72ffe1a3ed3983 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/Tab/Content.php +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Adminhtml/Page/Edit/Tab/Content.php @@ -171,4 +171,14 @@ class Content extends Tab 'content_heading' => '' ]; } + + /** + * Check if system variables block is visible. + * + * @return bool + */ + public function isVariablesBlockVisible() + { + return $this->_rootElement->find($this->systemVariableBlock, Locator::SELECTOR_XPATH)->isVisible(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Messages.php b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Messages.php new file mode 100644 index 0000000000000000000000000000000000000000..1a3bbfefab7bae3eb38d7734347d13100a645fa5 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Block/Messages.php @@ -0,0 +1,15 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Cms\Test\Block; + +/** + * Store front messages block. + */ +class Messages extends \Magento\Ui\Test\Block\Messages +{ + // +} diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.xml index c4a98c9ba3223ee307df5cdce1aeb37e57d1304f..b747464c579dea402a631212419a0e01ff03598b 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/Page/CmsIndex.xml @@ -11,11 +11,12 @@ <block name="topmenu" class="Magento\Theme\Test\Block\Html\Topmenu" locator="[data-action='navigation']" strategy="css selector" /> <block name="titleBlock" class="Magento\Theme\Test\Block\Html\Title" locator=".page-title-wrapper" strategy="css selector" /> <block name="footerBlock" class="Magento\Theme\Test\Block\Html\Footer" locator="footer.page-footer" strategy="css selector" /> + <block name="logoBlock" class="Magento\Theme\Test\Block\Html\Logo" locator=".header .logo" strategy="css selector" /> <block name="linksBlock" class="Magento\Theme\Test\Block\Links" locator=".header .links" strategy="css selector" /> <block name="storeSwitcherBlock" class="Magento\Store\Test\Block\Switcher" locator="[data-ui-id='language-switcher']" strategy="css selector" /> <block name="currencyBlock" class="Magento\Directory\Test\Block\Currency\Switcher" locator=".switcher.currency" strategy="css selector" /> <block name="cmsPageBlock" class="Magento\Cms\Test\Block\Page" locator=".page-main" strategy="css selector" /> <block name="widgetView" class="Magento\Widget\Test\Block\WidgetView" locator=".widget" strategy="css selector" /> - <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator=".messages" strategy="css selector"/> + <block name="messagesBlock" class="Magento\Cms\Test\Block\Messages" locator=".messages" strategy="css selector"/> </page> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Edit/Section/Variations/Config/Attribute.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Edit/Section/Variations/Config/Attribute.php index 805f20d89640aa7b7df6a3f86a7df4dc2df61295..7b765d76e3e0773db79826274b4094acfba1ddc6 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Edit/Section/Variations/Config/Attribute.php +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Edit/Section/Variations/Config/Attribute.php @@ -177,6 +177,7 @@ class Attribute extends Form //select attributes $this->getAttributesGrid()->resetFilter(); + $this->getTemplateBlock()->waitLoader(); $attributesList = $this->browser->find($this->selectedAttributes)->getText(); if ($attributesList != '--') { $this->getAttributesGrid()->deselectAttributes(); diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductsQtyAfterReorder.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductsQtyAfterReorder.php new file mode 100644 index 0000000000000000000000000000000000000000..8ce9392b2d5878c29695e75e5fdd596f6d22dc78 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertConfigurableProductsQtyAfterReorder.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Test\Constraint; + +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Sales\Test\Fixture\OrderInjectable; +use Magento\Catalog\Test\Constraint\AssertProductForm; +use Magento\Sales\Test\Constraint\AssertProductsQtyAfterOrderCancel; + +/** + * Assert that products quantity is correct after reorder. + */ +class AssertConfigurableProductsQtyAfterReorder extends AbstractConstraint +{ + /** + * Assert products quantity after placing new order with the same products. + * + * @param OrderInjectable $order + * @param CatalogProductIndex $productGrid + * @param CatalogProductEdit $productPage + * @param FixtureFactory $fixtureFactory + * @param AssertProductForm $assertProductForm + * @param AssertConfigurableProductForm $assertConfigurableProductForm + * @param AssertProductsQtyAfterOrderCancel $assertProductsQty + * @return void + */ + public function processAssert( + OrderInjectable $order, + CatalogProductIndex $productGrid, + CatalogProductEdit $productPage, + FixtureFactory $fixtureFactory, + AssertProductForm $assertProductForm, + AssertConfigurableProductForm $assertConfigurableProductForm, + AssertProductsQtyAfterOrderCancel $assertProductsQty + ) { + $newOrder = $fixtureFactory->createByCode('orderInjectable', [ + 'dataset' => 'default', + 'data' => [ + 'entity_id' => [ + 'products' => $order->getEntityId()['products'], + ] + ] + ]); + $newOrder->persist(); + $assertProductsQty->processAssert( + $newOrder, + $productGrid, + $productPage, + $fixtureFactory, + $assertProductForm, + $assertConfigurableProductForm + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Products quantity is correct after reorder.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Fixture/ConfigurableProduct/ConfigurableAttributesData.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Fixture/ConfigurableProduct/ConfigurableAttributesData.php index 65715d116ab45780dcacae89ef987a16e9127ceb..6989e2785e96144c1f33c82fc495026dd56e5ccc 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Fixture/ConfigurableProduct/ConfigurableAttributesData.php +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Fixture/ConfigurableProduct/ConfigurableAttributesData.php @@ -129,7 +129,6 @@ class ConfigurableAttributesData extends DataSource foreach ($this->attributes as $attributeKey => $attribute) { $attributeData = $attribute->getData(); $options = []; - foreach ($attributeData['options'] as $key => $option) { $options['option_key_' . $key] = $option; } diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml index 32f1957d4173f87b10069b60c8c428b921e260fb..b2a44d5454a9244c9b80102fc85aee6dcdaab986 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml @@ -39,6 +39,9 @@ <field name="checkout_data" xsi:type="array"> <item name="dataset" xsi:type="string">configurable_default</item> </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> </dataset> <dataset name="out_of_stock"> @@ -334,6 +337,40 @@ </field> </dataset> + <dataset name="with_one_option_buy_all"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">30</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">with_one_option_buy_all</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">configurable_one_option</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> + </dataset> + <dataset name="with_out_of_stock_item"> <field name="name" xsi:type="string">Test configurable product %isolation%</field> <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> @@ -497,5 +534,365 @@ <item name="dataset" xsi:type="string">price_40</item> </field> </dataset> + + <dataset name="product_with_price_10"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">10</item> + </field> + <field name="special_price" xsi:type="string">10</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">5</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">two_options_with_assigned_product_special_price</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">configurable_two_new_options_with_special_price</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + + <dataset name="first_product_with_custom_options_and_option_key_1"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="special_price" xsi:type="string">49.99</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">5</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">two_options_with_assigned_product_special_price_2</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="custom_options" xsi:type="array"> + <item name="dataset" xsi:type="string">two_fixed_radio_options</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">product_1_and_option_1</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + + <dataset name="first_product_with_custom_options_and_option_key_2"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="special_price" xsi:type="string">10</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">5</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">two_options_with_assigned_product_special_price_2</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="custom_options" xsi:type="array"> + <item name="dataset" xsi:type="string">two_fixed_radio_options</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">product_1_and_option_2</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + + <dataset name="second_product_with_custom_options_and_option_key_1"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="special_price" xsi:type="string">10</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">5</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">two_options_with_assigned_product_special_price_2</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="custom_options" xsi:type="array"> + <item name="dataset" xsi:type="string">two_fixed_radio_options</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">product_2_and_option_1</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + + <dataset name="second_product_with_custom_options_and_option_key_2"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="special_price" xsi:type="string">10</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">5</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">two_options_with_assigned_product_special_price_2</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="custom_options" xsi:type="array"> + <item name="dataset" xsi:type="string">two_fixed_radio_options</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">product_2_and_option_2</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + + <dataset name="Stellar_Solar_Jacket_SIZE_S"> + <field name="name" xsi:type="string">Stellar Solar Jacket %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">5</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">75</item> + <item name="dataset" xsi:type="string">price_75</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">three_options_with_sizes_S_M_L</item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">custom_attribute_set_with_sizes</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">configurable_the_first_option</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + + <dataset name="Stellar_Solar_Jacket_SIZE_M"> + <field name="name" xsi:type="string">Stellar Solar Jacket %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">5</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">75</item> + <item name="dataset" xsi:type="string">price_75</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">three_options_with_sizes_S_M_L</item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">custom_attribute_set_with_sizes</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">configurable_the_second_option</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + + <dataset name="Stellar_Solar_Jacket_SIZE_L"> + <field name="name" xsi:type="string">Stellar Solar Jacket %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">5</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">75</item> + <item name="dataset" xsi:type="string">price_75</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">three_options_with_sizes_S_M_L</item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">custom_attribute_set_with_sizes</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">configurable_the_third_option</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> + + <dataset name="configurable_low_stock"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">30</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">configurable_low_stock</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">configurable_low_stock</item> + </field> + </dataset> + + <dataset name="one_simple_product"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">560</item> + </field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">2</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">one_option_with_simple_product</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/CheckoutData.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/CheckoutData.xml index 949c6dc065c494a18e2ae617ef859f6663dd05d4..9641bc980a71b6cbb1e24266f98b89b97fb80ff1 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/CheckoutData.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/CheckoutData.xml @@ -201,6 +201,42 @@ </field> </dataset> + <dataset name="configurable_the_first_option"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">1</field> + </dataset> + + <dataset name="configurable_the_second_option"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">1</field> + </dataset> + + <dataset name="configurable_the_third_option"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_2</item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">1</field> + </dataset> + <dataset name="configurable_two_options_with_fixed_price"> <field name="options" xsi:type="array"> <item name="configurable_options" xsi:type="array"> @@ -227,5 +263,186 @@ <item name="price" xsi:type="string">11</item> </field> </dataset> + + <dataset name="configurable_two_options_with_fixed_price_1"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + </field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">9</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">9</item> + </field> + </dataset> + + <dataset name="product_1_and_option_1"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + </field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">9</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">9</item> + </field> + </dataset> + + <dataset name="product_1_and_option_2"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + </field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">9</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">9</item> + </field> + </dataset> + + <dataset name="product_2_and_option_1"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + </field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">9</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">9</item> + </field> + </dataset> + + <dataset name="product_2_and_option_2"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + </field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">9</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">9</item> + </field> + </dataset> + + <dataset name="configurable_option_1_and_custom_option_2"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + </field> + <field name="cartItem" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + </field> + </dataset> + + <dataset name="configurable_option_2_and_custom_option_1"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + </field> + <field name="cartItem" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + </field> + </dataset> + + <dataset name="configurable_option_2_and_custom_option_2"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + <item name="custom_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + </field> + <field name="cartItem" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + </field> + </dataset> + + <dataset name="configurable_low_stock"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">1</field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">15</item> + <item name="qty" xsi:type="string">1</item> + <item name="subtotal" xsi:type="string">15</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml index 44e2e14545db02103bfe2fa2613f196937cfc558..dc3ffc389c56b1ac57b49b178536bafc25856829 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml @@ -440,6 +440,42 @@ </field> </dataset> + <dataset name="two_options_with_assigned_product_special_price_2"> + <field name="attributes_data" xsi:type="array"> + <item name="attribute_key_0" xsi:type="array"> + <item name="options" xsi:type="array"> + <item name="option_key_0" xsi:type="array"> + <item name="label" xsi:type="string">option_key_1_%isolation%</item> + <item name="pricing_value" xsi:type="string">79.99</item> + <item name="include" xsi:type="string">Yes</item> + </item> + <item name="option_key_1" xsi:type="array"> + <item name="label" xsi:type="string">option_key_2_%isolation%</item> + <item name="pricing_value" xsi:type="string">20.01</item> + <item name="include" xsi:type="string">Yes</item> + </item> + </item> + </item> + </field> + <field name="attributes" xsi:type="array"> + <item name="attribute_key_0" xsi:type="string">catalogProductAttribute::attribute_type_dropdown_two_options</item> + </field> + <field name="products" xsi:type="array"> + <item name="attribute_key_0:option_key_0" xsi:type="string">catalogProductSimple::product_with_price_79_99</item> + <item name="attribute_key_0:option_key_1" xsi:type="string">catalogProductSimple::product_with_price_20_01</item> + </field> + <field name="matrix" xsi:type="array"> + <item name="attribute_key_0:option_key_0" xsi:type="array"> + <item name="qty" xsi:type="string">10</item> + <item name="weight" xsi:type="string">1</item> + </item> + <item name="attribute_key_0:option_key_1" xsi:type="array"> + <item name="qty" xsi:type="string">20</item> + <item name="weight" xsi:type="string">1</item> + </item> + </field> + </dataset> + <dataset name="two_options_with_assigned_product_tier_price"> <field name="attributes_data" xsi:type="array"> <item name="attribute_key_0" xsi:type="array"> @@ -595,6 +631,44 @@ </field> </dataset> + <dataset name="with_one_option_buy_all"> + <field name="attributes_data" xsi:type="array"> + <item name="attribute_key_0" xsi:type="array"> + <item name="options" xsi:type="array"> + <item name="option_key_0" xsi:type="array"> + <item name="pricing_value" xsi:type="string">1</item> + <item name="include" xsi:type="string">Yes</item> + </item> + <item name="option_key_1" xsi:type="array"> + <item name="pricing_value" xsi:type="string">2</item> + <item name="include" xsi:type="string">Yes</item> + </item> + <item name="option_key_2" xsi:type="array"> + <item name="pricing_value" xsi:type="string">3</item> + <item name="include" xsi:type="string">Yes</item> + </item> + </item> + </item> + </field> + <field name="attributes" xsi:type="array"> + <item name="attribute_key_0" xsi:type="string">catalogProductAttribute::attribute_type_dropdown</item> + </field> + <field name="matrix" xsi:type="array"> + <item name="attribute_key_0:option_key_0" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + <item name="weight" xsi:type="string">1</item> + </item> + <item name="attribute_key_0:option_key_1" xsi:type="array"> + <item name="qty" xsi:type="string">0</item> + <item name="weight" xsi:type="string">1</item> + </item> + <item name="attribute_key_0:option_key_2" xsi:type="array"> + <item name="qty" xsi:type="string">0</item> + <item name="weight" xsi:type="string">1</item> + </item> + </field> + </dataset> + <dataset name="with_out_of_stock_item"> <field name="attributes_data" xsi:type="array"> <item name="attribute_key_0" xsi:type="array"> @@ -612,6 +686,12 @@ <field name="attributes" xsi:type="array"> <item name="attribute_key_0" xsi:type="string">catalogProductAttribute::attribute_type_dropdown_one_option</item> </field> + <field name="matrix" xsi:type="array"> + <item name="attribute_key_0:option_key_0" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + <item name="weight" xsi:type="string">1</item> + </item> + </field> </dataset> <dataset name="two_options_with_fixed_price"> @@ -713,5 +793,94 @@ </item> </field> </dataset> + + <dataset name="three_options_with_sizes_S_M_L"> + <field name="attributes_data" xsi:type="array"> + <item name="attribute_key_0" xsi:type="array"> + <item name="options" xsi:type="array"> + <item name="option_key_0" xsi:type="array"> + <item name="label" xsi:type="string">SIZE_S</item> + <item name="pricing_value" xsi:type="string">75</item> + <item name="include" xsi:type="string">Yes</item> + </item> + <item name="option_key_1" xsi:type="array"> + <item name="label" xsi:type="string">SIZE_M</item> + <item name="pricing_value" xsi:type="string">75</item> + <item name="include" xsi:type="string">Yes</item> + </item> + <item name="option_key_2" xsi:type="array"> + <item name="label" xsi:type="string">SIZE_L</item> + <item name="pricing_value" xsi:type="string">75</item> + <item name="include" xsi:type="string">Yes</item> + </item> + </item> + </item> + </field> + <field name="attributes" xsi:type="array"> + <item name="attribute_key_0" xsi:type="string">catalogProductAttribute::sizes_for_promo_rules</item> + </field> + <field name="matrix" xsi:type="array"> + <item name="attribute_key_0:option_key_0" xsi:type="array"> + <item name="qty" xsi:type="string">10</item> + <item name="weight" xsi:type="string">1</item> + </item> + <item name="attribute_key_0:option_key_1" xsi:type="array"> + <item name="qty" xsi:type="string">20</item> + <item name="weight" xsi:type="string">1</item> + </item> + <item name="attribute_key_0:option_key_2" xsi:type="array"> + <item name="qty" xsi:type="string">20</item> + <item name="weight" xsi:type="string">1</item> + </item> + </field> + </dataset> + + <dataset name="configurable_low_stock"> + <field name="attributes_data" xsi:type="array"> + <item name="attribute_key_0" xsi:type="array"> + <item name="options" xsi:type="array"> + <item name="option_key_0" xsi:type="array"> + <item name="pricing_value" xsi:type="string">12.00</item> + <item name="include" xsi:type="string">Yes</item> + </item> + </item> + </item> + </field> + <field name="attributes" xsi:type="array"> + <item name="attribute_key_0" xsi:type="string">catalogProductAttribute::attribute_type_dropdown_one_option</item> + </field> + <field name="matrix" xsi:type="array"> + <item name="attribute_key_0:option_key_0" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + <item name="weight" xsi:type="string">1</item> + </item> + </field> + </dataset> + + <dataset name="one_option_with_simple_product"> + <field name="attributes_data" xsi:type="array"> + <item name="attribute_key_0" xsi:type="array"> + <item name="options" xsi:type="array"> + <item name="option_key_0" xsi:type="array"> + <item name="label" xsi:type="string">option_key_1_%isolation%</item> + <item name="pricing_value" xsi:type="string">560</item> + <item name="include" xsi:type="string">Yes</item> + </item> + </item> + </item> + </field> + <field name="attributes" xsi:type="array"> + <item name="attribute_key_0" xsi:type="string">catalogProductAttribute::attribute_type_dropdown_one_option</item> + </field> + <field name="products" xsi:type="array"> + <item name="attribute_key_0:option_key_0" xsi:type="string">catalogProductSimple::default_with_weight_2</item> + </field> + <field name="matrix" xsi:type="array"> + <item name="attribute_key_0:option_key_0" xsi:type="array"> + <item name="qty" xsi:type="string">10</item> + <item name="weight" xsi:type="string">1</item> + </item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/Price.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/Price.xml index 1b6282b9432c39fd444ca2089f1b8c8aa6bfb876..2704871a561d1dfc8102972b0fe558eb6f9e2da2 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/Price.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/Price.xml @@ -14,9 +14,17 @@ <field name="category_price" xsi:type="string">40</field> <field name="compare_price" xsi:type="string">40</field> </dataset> + <dataset name="price_10"> + <field name="category_price" xsi:type="string">10</field> + <field name="compare_price" xsi:type="string">10</field> + </dataset> <dataset name="MAGETWO-12620"> <field name="category_price" xsi:type="string">11</field> </dataset> + <dataset name="price_75"> + <field name="category_price" xsi:type="string">75</field> + <field name="compare_price" xsi:type="string">75</field> + </dataset> <dataset name="from-9"> <field name="price_from" xsi:type="string">9</field> </dataset> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCurrencyRateTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCurrencyRateTest.xml index 6ba7729120ae909591f0703c36b83b51f2ea8aa0..a03f2aabd62399420d7a79d937e213c7944f7b65 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCurrencyRateTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCurrencyRateTest.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Directory\Test\TestCase\CreateCurrencyRateTest" summary="Create Currency Rate" ticketId="MAGETWO-36824"> - <variation name="CreateCurrencyRateTestVariation3"> + <variation name="CreateCurrencyRateTestVariation4"> <data name="currencyRate/data/currency_from" xsi:type="string">USD</data> <data name="currencyRate/data/currency_to" xsi:type="string">UAH</data> <data name="currencyRate/data/rate" xsi:type="number">2.000</data> diff --git a/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/ConfigData.xml index 77b311bd7a51f80b0347ba092dc700e467c2cf84..39853e7f8d8473b77b24cffd7028ca561124022b 100644 --- a/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/ConfigData.xml @@ -211,5 +211,16 @@ </item> </field> </dataset> + + <dataset name="config_allowed_currency_usd_and_uah"> + <field name="currency/options/allow" xsi:type="array"> + <item name="scope" xsi:type="string">currency</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="value" xsi:type="array"> + <item name="US Dollar" xsi:type="string">USD</item> + <item name="Ukrainian Hryvnia" xsi:type="string">UAH</item> + </item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/CurrencySymbolEntity.xml b/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/CurrencySymbolEntity.xml index 69db8d9ab8224d8c12737d0ea812d5a5758c2735..211ade81582531cd4f481037224e0207e0b53860 100644 --- a/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/CurrencySymbolEntity.xml +++ b/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/CurrencySymbolEntity.xml @@ -15,6 +15,7 @@ <field name="custom_currency_symbol" xsi:type="array"> <item name="UAH" xsi:type="string">custom</item> </field> + <field name="code" xsi:type="string">UAH</field> </dataset> <dataset name="currency_symbols_eur"> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Account/AuthenticationPopup.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Account/AuthenticationPopup.php index cc23998f60ac2ced653c294313cfa54eb98ef5a9..c796ed435b3bff9e9bf4676680bebaa9f9e5b1a1 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Account/AuthenticationPopup.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Account/AuthenticationPopup.php @@ -32,7 +32,24 @@ class AuthenticationPopup extends Form * * @var string */ - protected $createAccountButton = '.action.action-register.primary'; + private $createAccountButton = '.action.action-register.primary'; + + /** + * Selector for password field with autocomplete off. + * + * @var string + */ + private $passwordFieldWithAutocompleteOff = 'input[name="password"][autocomplete="off"]'; + + /** + * Checks if password field autocomplete is off. + * + * @return bool + */ + public function isPasswordAutocompleteOff() + { + return $this->_rootElement->find($this->passwordFieldWithAutocompleteOff)->isVisible(); + } /** * Click 'Create an Account' button. diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php index e1e8f2ddebb709e17d8a977668f420d803247558..5980d0df9e3bc2f8b98ce79c92fb347c17bce44e 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php @@ -38,6 +38,21 @@ class CustomerForm extends Form */ protected $validationText = '.mage-error[for="%s"]'; + /** + * Fixture mapping and fields revision. + * + * @param array|null $fields + * @param string|null $parent + * @return array + */ + protected function dataMapping(array $fields = null, $parent = null) + { + if (isset($fields['website_id'])) { + unset($fields['website_id']); + } + return parent::dataMapping($fields, $parent); + } + /** * Click on save button. * diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Login.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Login.php index b28904aee9e070f38dc788598b21d139bee8d395..621d4be605120ccc51673c66cda35f80729914db 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Login.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Login.php @@ -11,27 +11,43 @@ use Magento\Mtf\Client\Locator; use Magento\Mtf\Fixture\FixtureInterface; /** - * Class Login - * Form for frontend login + * Form for frontend login. */ class Login extends Form { /** - * Login button for registered customers + * Login button for registered customers. * * @var string */ - protected $loginButton = '.action.login'; + private $loginButton = '.action.login'; /** - * 'Register' customer button + * 'Register' customer button. * * @var string */ - protected $registerButton = '.action.create'; + private $registerButton = '.action.create'; /** - * Login customer in the Frontend + * Selector for password field with autocomplete off. + * + * @var string + */ + private $passwordFieldWithAutocompleteOff = 'input[name="login[password]"][autocomplete="off"]'; + + /** + * Checks if password field autocomplete is off. + * + * @return bool + */ + public function isPasswordAutocompleteOff() + { + return $this->_rootElement->find($this->passwordFieldWithAutocompleteOff)->isVisible(); + } + + /** + * Login customer in the Frontend. * * @param FixtureInterface $customer * @@ -45,7 +61,7 @@ class Login extends Form } /** - * Submit login form + * Submit login form. */ public function submit() { @@ -53,7 +69,7 @@ class Login extends Form } /** - * Press 'Register' button + * Press 'Register' button. */ public function registerCustomer() { @@ -61,7 +77,7 @@ class Login extends Form } /** - * Check whether block is visible + * Check whether block is visible. * * @return bool */ diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Register.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Register.php index 69be9bcd8cec8f5d3022075c74ab5f9a004afdf1..72ea1c185fd600a219739c2e7ae6aaf9ec648d26 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Register.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/Register.php @@ -44,6 +44,21 @@ class Register extends Form */ protected $passwordConfirmationError = "#password-confirmation-error"; + /** + * Fixture mapping. + * + * @param array|null $fields + * @param string|null $parent + * @return array + */ + protected function dataMapping(array $fields = null, $parent = null) + { + if (isset($fields['website_id'])) { + unset($fields['website_id']); + } + return parent::dataMapping($fields, $parent); + } + /** * Create new customer account and fill billing address if it exists * diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerLoginErrorMessage.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerLoginErrorMessage.php new file mode 100644 index 0000000000000000000000000000000000000000..155a1529cdb5e3e015828396672419330dc9b5dc --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerLoginErrorMessage.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Customer\Test\Page\CustomerAccountLogin; + +/** + * Assert that customer login error message is displayed. + */ +class AssertCustomerLoginErrorMessage extends AbstractConstraint +{ + /** + * Customer login error message. + */ + const ERROR_MESSAGE = 'Invalid login or password.'; + + /** + * Assert that customer login error message is displayed. + * + * @param CustomerAccountLogin $customerLogin + * @return void + */ + public function processAssert( + CustomerAccountLogin $customerLogin + ) { + \PHPUnit_Framework_Assert::assertEquals( + self::ERROR_MESSAGE, + $customerLogin->getMessages()->getErrorMessage(), + 'Wrong error message is displayed.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Customer login error message is displayed.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerPasswordAutocompleteOnAuthorizationPopup.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerPasswordAutocompleteOnAuthorizationPopup.php new file mode 100644 index 0000000000000000000000000000000000000000..95944eb3b47bd48cae99b615db1df9ba9c4d232c --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerPasswordAutocompleteOnAuthorizationPopup.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Constraint; + +use Magento\Checkout\Test\Page\CheckoutCart; +use Magento\Checkout\Test\Page\CheckoutOnepage; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that autocomplete on password field on authorization pop up is off. + */ +class AssertCustomerPasswordAutocompleteOnAuthorizationPopup extends AbstractConstraint +{ + /** + * Assert that autocomplete on password field on authorization pop up is off. + * + * @param CheckoutOnepage $checkoutPage + * @param CheckoutCart $cartPage + * @return void + */ + public function processAssert( + CheckoutOnepage $checkoutPage, + CheckoutCart $cartPage + ) { + $cartPage->open(); + $cartPage->getProceedToCheckoutBlock()->proceedToCheckout(); + + \PHPUnit_Framework_Assert::assertTrue( + $checkoutPage->getAuthenticationPopupBlock()->isPasswordAutocompleteOff(), + 'Password field autocomplete is not off.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Assert that autocomplete is off.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerPasswordAutocompleteOnSignIn.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerPasswordAutocompleteOnSignIn.php new file mode 100644 index 0000000000000000000000000000000000000000..8794383a80c728e781a0b9ec3e5253c2d3eec21b --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertCustomerPasswordAutocompleteOnSignIn.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Constraint; + +use Magento\Customer\Test\Page\CustomerAccountLogin; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that autocomplete on password field on sign in page is off. + */ +class AssertCustomerPasswordAutocompleteOnSignIn extends AbstractConstraint +{ + /** + * Assert that autocomplete on password field on sign in page is off. + * + * @param CustomerAccountLogin $loginPage + * @return void + */ + public function processAssert(CustomerAccountLogin $loginPage) + { + $loginPage->open(); + \PHPUnit_Framework_Assert::assertTrue( + $loginPage->getLoginBlock()->isPasswordAutocompleteOff(), + 'Password field autocomplete is not off.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Assert that autocomplete is off.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertMassActionSuccessUpdateMessage.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertMassActionSuccessUpdateMessage.php index ae044ca025d1347112fe09cb0fe1323062cf8b81..d2637b6c8ea29ee4177593a5ddcb7031a7773461 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertMassActionSuccessUpdateMessage.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Constraint/AssertMassActionSuccessUpdateMessage.php @@ -28,13 +28,12 @@ class AssertMassActionSuccessUpdateMessage extends AbstractConstraint /** * Assert update message is appears on customer grid (Customers > All Customers) * - * @param Customer|Customer[] $customer + * @param Customer[] $customers * @param CustomerIndex $pageCustomerIndex * @return void */ - public function processAssert($customer, CustomerIndex $pageCustomerIndex) + public function processAssert(array $customers, CustomerIndex $pageCustomerIndex) { - $customers = is_array($customer) ? $customer : [$customer]; $actualMessage = $pageCustomerIndex->getMessagesBlock()->getSuccessMessage(); \PHPUnit_Framework_Assert::assertEquals(sprintf(self::UPDATE_MESSAGE, count($customers)), $actualMessage); } diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Fixture/Customer.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/Fixture/Customer.xml index 3a09f8861b601f7d1ba6a58b72b454a66cc78fa6..8572b15420ff8a381a01ac7d61a87d89af6f7949 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Fixture/Customer.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Fixture/Customer.xml @@ -37,7 +37,7 @@ <field name="store_id" is_required="1" group="account_information" /> <field name="suffix" is_required="0" group="account_information" /> <field name="taxvat" is_required="0" group="account_information" /> - <field name="website_id" is_required="1" group="account_information" /> + <field name="website_id" is_required="1" group="account_information" source="Magento\Customer\Test\Fixture\Customer\WebsiteId" /> <field name="amount_delta" is_required="1" group="store_credit" /> <field name="is_subscribed" /> <field name="password" group="null" /> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Fixture/Customer/WebsiteId.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Fixture/Customer/WebsiteId.php new file mode 100644 index 0000000000000000000000000000000000000000..18daa3271d8faf980f4146af3d24fb8d853571dc --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Fixture/Customer/WebsiteId.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Fixture\Customer; + +use Magento\Mtf\Fixture\DataSource; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Store\Test\Fixture\Store; +use Magento\Store\Test\Fixture\Website; + +/** + * Prepare website. + */ +class WebsiteId extends DataSource +{ + /** + * Store Fixture. + * + * @var Store + */ + private $store; + + /** + * Website. + * + * @var Website + */ + private $website; + + /** + * Fixture Factory instance. + * + * @var FixtureFactory + */ + private $fixtureFactory; + + /** + * Rought fixture field data. + * + * @var array + */ + private $fixtureData = null; + + /** + * @constructor + * @param FixtureFactory $fixtureFactory + * @param array $params + * @param array $data + */ + public function __construct( + FixtureFactory $fixtureFactory, + array $params, + $data = [] + ) { + $this->fixtureFactory = $fixtureFactory; + $this->params = $params; + $this->fixtureData = $data; + } + + /** + * Return prepared data set. + * + * @param string $key [optional] + * @return mixed + * @throws \Exception + */ + public function getData($key = null) + { + if (empty($this->fixtureData)) { + throw new \Exception("Data must be set"); + } + + if (isset($this->fixtureData['website'])) { + $this->website = $this->fixtureData['website']; + $this->data = $this->fixtureData['website']->getName(); + } else { + if (isset($this->fixtureData['dataset'])) { + $store = $this->fixtureFactory->createByCode('store', $this->fixtureData); + + if (!$store->getStoreId()) { + $store->persist(); + } + + $website = $store->getDataFieldConfig('group_id')['source'] + ->getStoreGroup()->getDataFieldConfig('website_id')['source']->getWebsite(); + + $this->data = $website->getName(); + $this->website = $website; + $this->store = $store; + } + } + + return parent::getData($key); + } + + /** + * Return store. + * + * @return Store + */ + public function getStore() + { + return $this->store; + } + + /** + * Return website code. + * + * @return Website + */ + public function getWebsite() + { + return $this->website; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/Customer/Curl.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/Customer/Curl.php index f880983074576396646ed0dc10be2ebb12c6b708..546f58cb6cb04fc4e59c8582a55e2818d32756ad 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/Customer/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/Customer/Curl.php @@ -87,6 +87,7 @@ class Curl extends AbstractCurl implements CustomerInterface /** @var Customer $customer */ $data = $customer->getData(); $data['group_id'] = $this->getCustomerGroup($customer); + $data['website_id'] = $this->getCustomerWebsite($customer); $address = []; $url = $_ENV['app_frontend_url'] . 'customer/account/createpost/?nocookie=true'; @@ -229,4 +230,15 @@ class Curl extends AbstractCurl implements CustomerInterface return $curlData; } + + /** + * Prepare customer website data. + * + * @param Customer $customer + * @return int + */ + private function getCustomerWebsite(Customer $customer) + { + return $customer->getDataFieldConfig('website_id')['source']->getWebsite()->getWebsiteId(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/Customer/Webapi.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/Customer/Webapi.php index aa63bfa50f052096d0468b5df6e8c12e7c1075af..a223985872434fc2bde5c906cc4d6b0fe8bd00b8 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/Customer/Webapi.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Handler/Customer/Webapi.php @@ -79,6 +79,7 @@ class Webapi extends AbstractWebapi implements CustomerInterface $data['customer'] = $this->replaceMappingData($customer->getData()); $data['customer']['group_id'] = $this->getCustomerGroup($customer); $data['password'] = $data['customer']['password']; + $data['customer']['website_id'] = $this->getCustomerWebsite($customer); unset($data['customer']['password']); unset($data['customer']['password_confirmation']); $data = $this->prepareAddressData($data); @@ -182,4 +183,15 @@ class Webapi extends AbstractWebapi implements CustomerInterface return $addressData; } + + /** + * Prepare customer website data. + * + * @param Customer $customer + * @return int + */ + private function getCustomerWebsite(Customer $customer) + { + return $customer->getDataFieldConfig('website_id')['source']->getWebsite()->getWebsiteId(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/ConfigData.xml index 524805f44038143d426624c563afefe1054ffd6f..eee9dbeb74a6d4f76d7d5746fa56702df1ab789f 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/ConfigData.xml @@ -60,5 +60,14 @@ <item name="value" xsi:type="string">0</item> </field> </dataset> + + <dataset name="password_autocomplete_off"> + <field name="customer/password/autocomplete_on_storefront" xsi:type="array"> + <item name="scope" xsi:type="string">customer</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">No</item> + <item name="value" xsi:type="string">0</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Customer.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Customer.xml index 3be7447c91dad863564835d08b459b6addfd86f5..eba62cba79ce2ff7ef1488863a6c666f49c8aaad 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Customer.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Customer.xml @@ -16,6 +16,9 @@ <field name="email" xsi:type="string">JohnDoe_%isolation%@example.com</field> <field name="password" xsi:type="string">123123^q</field> <field name="password_confirmation" xsi:type="string">123123^q</field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="customer_with_new_customer_group"> @@ -27,6 +30,9 @@ <field name="email" xsi:type="string">JohnDoe_%isolation%@example.com</field> <field name="password" xsi:type="string">123123^q</field> <field name="password_confirmation" xsi:type="string">123123^q</field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="johndoe"> @@ -40,6 +46,9 @@ <field name="group_id" xsi:type="array"> <item name="dataset" xsi:type="string">General</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="johndoe_retailer"> @@ -53,13 +62,18 @@ <field name="password_confirmation" xsi:type="string">123123^q</field> <field name="dob" xsi:type="string">01/01/1990</field> <field name="gender" xsi:type="string">Male</field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="defaultBackend"> - <field name="website_id" xsi:type="string">Main Website</field> <field name="firstname" xsi:type="string">John</field> <field name="lastname" xsi:type="string">Doe</field> <field name="email" xsi:type="string">JohnDoe_%isolation%@example.com</field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="johndoe_with_addresses"> @@ -74,6 +88,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">US_address</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="johndoe_unique"> @@ -88,6 +105,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">US_address_NY</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="johndoe_unique_TX"> @@ -102,6 +122,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">US_address_TX</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="johndoe_with_multiple_addresses"> @@ -116,6 +139,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">US_address_NY, US_address</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="register_customer"> @@ -138,6 +164,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">US_address_1</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="backend_retailer_customer"> @@ -152,6 +181,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">US_address_1</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="customer_US"> @@ -163,6 +195,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">US_address_1</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="customer_UK_1_default_billing_address"> @@ -174,6 +209,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">UK_address_default_billing</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="customer_UK_address_with_VAT"> @@ -185,6 +223,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">UK_address_with_VAT</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="customer_UK_US_addresses"> @@ -196,6 +237,9 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">UK_address_default_billing, US_address_default_shipping</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> <dataset name="customer_US_DE_UK"> @@ -210,6 +254,39 @@ <field name="address" xsi:type="array"> <item name="dataset" xsi:type="string">US_address, DE_address, UK_address</item> </field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + </dataset> + + <dataset name="default_in_custom_website"> + <field name="firstname" xsi:type="string">John</field> + <field name="lastname" xsi:type="string">Doe</field> + <field name="group_id" xsi:type="array"> + <item name="dataset" xsi:type="string">General</item> + </field> + <field name="email" xsi:type="string">JohnDoe_%isolation%@example.com</field> + <field name="password" xsi:type="string">123123^q</field> + <field name="password_confirmation" xsi:type="string">123123^q</field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">custom_store</item> + </field> + </dataset> + + <dataset name="johndoe_wholesale"> + <field name="firstname" xsi:type="string">John</field> + <field name="lastname" xsi:type="string">Doe</field> + <field name="group_id" xsi:type="array"> + <item name="dataset" xsi:type="string">Wholesale</item> + </field> + <field name="email" xsi:type="string">JohnDoe_%isolation%@example.com</field> + <field name="password" xsi:type="string">123123^q</field> + <field name="password_confirmation" xsi:type="string">123123^q</field> + <field name="dob" xsi:type="string">01/01/1990</field> + <field name="gender" xsi:type="string">Male</field> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml index 34479021cf93590c8e10c1b371afadb4d9de16d3..324b47bfebb82e2aa736b6baaef37d45dbc4264e 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml @@ -13,6 +13,7 @@ <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data> <data name="customer/data/password" xsi:type="string">123123q#</data> <data name="customer/data/password_confirmation" xsi:type="string">123123q#</data> + <data name="customer/data/website_id/dataset" xsi:type="string">default</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerFailRegisterMessage" /> </variation> </testCase> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.php b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e5929458ce653cba1b58b1f4f336e303f056e2f7 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\TestCase; + +use Magento\Customer\Test\Page\CustomerAccountLogin; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Mtf\TestCase\Injectable; +use Magento\Customer\Test\Fixture\Customer; + +/** + * Precondition: + * 1. Customer is created. + * + * Steps: + * 1. Open login page. + * 2. Fill email. + * 3. Fill wrong password. + * 4. Click login. + * 5. Check error message. + * + * @group Customer + * @ZephyrId MAGETWO-16883 + */ +class LoginOnFrontendFailTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + /* end tags */ + + /** + * Login on frontend. + * + * @param Customer $customer + * @param CustomerAccountLogin $loginPage + * @param FixtureFactory $fixtureFactory + * @return void + */ + public function test(Customer $customer, CustomerAccountLogin $loginPage, FixtureFactory $fixtureFactory) + { + // Precondition + $customer->persist(); + $customerData = $customer->getData(); + $customerData['password'] = 'fail'; + $customerData['group_id'] = ['dataset' => 'default']; + $failCustomer = $fixtureFactory->createByCode('customer', ['data' => $customerData]); + + // Steps + $loginPage->open(); + $loginPage->getLoginBlock()->fill($failCustomer); + $loginPage->getLoginBlock()->submit(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..3d2a6cec11acce8beed4af3d3a98bec65b9cc09e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Customer\Test\TestCase\LoginOnFrontendFailTest" summary="Check error message with wrong credentials" ticketId="MAGETWO-16883"> + <variation name="LoginOnFrontendFailTestVariation1"> + <data name="customer/dataset" xsi:type="string">default</data> + <constraint name="Magento\Customer\Test\Constraint\AssertCustomerLoginErrorMessage" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.php b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.php index f7448024b93aa2d889a4ac35b31df900adb68aed..d3c2cefcbd511e96ede91e33620a9cce1bd4ebb2 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.php @@ -7,8 +7,8 @@ namespace Magento\Customer\Test\TestCase; use Magento\Customer\Test\Fixture\CustomerGroup; -use Magento\Customer\Test\Fixture\Customer; use Magento\Customer\Test\Page\Adminhtml\CustomerIndex; +use Magento\Mtf\Fixture\FixtureFactory; use Magento\Mtf\TestCase\Injectable; /** @@ -17,7 +17,7 @@ use Magento\Mtf\TestCase\Injectable; * Test Flow: * * Preconditions: - * 1. Create customer + * 1. Create customers * 2. Create customer group * * Steps: @@ -30,7 +30,7 @@ use Magento\Mtf\TestCase\Injectable; * 7. Perform all assertions * * @group Customer_Groups, Customers - * @ZephyrId MAGETWO-27892 + * @ZephyrId MAGETWO-27892, MAGETWO-19456 */ class MassAssignCustomerGroupTest extends Injectable { @@ -53,19 +53,6 @@ class MassAssignCustomerGroupTest extends Injectable */ protected $customersGridActions = 'Assign a Customer Group'; - /** - * Prepare data - * - * @param Customer $customer - * @return array - */ - public function __prepare(Customer $customer) - { - $customer->persist(); - - return ['customer' => $customer]; - } - /** * Injection data * @@ -80,19 +67,32 @@ class MassAssignCustomerGroupTest extends Injectable /** * Mass assign customer group * - * @param Customer $customer * @param CustomerGroup $customerGroup - * @return void + * @param FixtureFactory $fixtureFactory + * @param array $customers + * @return array */ - public function test(Customer $customer, CustomerGroup $customerGroup) + public function test(CustomerGroup $customerGroup, FixtureFactory $fixtureFactory, array $customers) { + // Preconditions + if (!$customerGroup->hasData('customer_group_id')) { + $customerGroup->persist(); + } + + $customerEmails = []; + foreach ($customers as &$customer) { + $customer = $fixtureFactory->createByCode('customer', ['dataset' => $customer]); + $customer->persist(); + $customerEmails[] = ['email' => $customer->getEmail()]; + } + // Steps - $customerGroup->persist(); $this->customerIndex->open(); $this->customerIndex->getCustomerGridBlock()->massaction( - [['email' => $customer->getEmail()]], + $customerEmails, [$this->customersGridActions => $customerGroup->getCustomerGroupCode()], true ); + return ['customers' => $customers]; } } diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.xml index af57affcd7f07bf42182f444b16fc85ffe274ade..464fed37d5df12ffbc6e7c49bb1087dbea87b27a 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.xml @@ -7,11 +7,21 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Customer\Test\TestCase\MassAssignCustomerGroupTest" summary="Mass Assign Customer's Group to Customers" ticketId="MAGETWO-27892"> - <variation name="MassAssignCustomerGroupTestVariation1"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test, to_maintain:yes</data> + <variation name="MassAssignCustomerGroupTestVariation1" summary="Customer is created and mass action for changing customer group to created group is applied"> + <data name="customers" xsi:type="array"> + <item name="0" xsi:type="string">default</item> + </data> <data name="customerGroup/dataset" xsi:type="string">default</data> <constraint name="Magento\Customer\Test\Constraint\AssertMassActionSuccessUpdateMessage" /> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerGroupInGrid" /> </variation> + <variation name="MassAssignCustomerGroupTestVariation2" summary="Two customers are created and mass actions for changing customer group to 'Retail' is applied" ticketId="MAGETWO-19456"> + <data name="customers" xsi:type="array"> + <item name="0" xsi:type="string">default</item> + <item name="1" xsi:type="string">customer_US</item> + </data> + <data name="customerGroup/dataset" xsi:type="string">Retailer</data> + <constraint name="Magento\Customer\Test\Constraint\AssertMassActionSuccessUpdateMessage" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.php b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.php new file mode 100644 index 0000000000000000000000000000000000000000..347fda1e91ff17e06755ba159c5e71d7ad85fb92 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\TestCase; + +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Checkout\Test\Page\CheckoutCart; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\TestCase\Injectable; + +/** + * Precondition: + * 1. Configs are applied. + * 2. Simple product is created. + * + * Steps: + * 1. Add product to shopping cart. + * 2. Perform asserts. + * + * @group Customer + * @ZephyrId MAGETWO-45324 + */ +class PasswordAutocompleteOffTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + /* end tags */ + + /** + * Configuration setting. + * + * @var string + */ + protected $configData; + + /** + * Test password field autocomplete is off. + * + * @param BrowserInterface $browser + * @param CatalogProductView $catalogProductView + * @param CheckoutCart $checkoutCart + * @param CatalogProductSimple $product + * @param string $configData + * @return void + */ + public function test( + BrowserInterface $browser, + CatalogProductView $catalogProductView, + CheckoutCart $checkoutCart, + CatalogProductSimple $product, + $configData + ) { + $this->configData = $configData; + + // Preconditions + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData] + )->run(); + + $product->persist(); + + // Steps + $checkoutCart->open(); + $checkoutCart->getCartBlock()->clearShoppingCart(); + + $browser->open($_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'); + $catalogProductView->getViewBlock()->addToCart($product); + $catalogProductView->getMessagesBlock()->waitSuccessMessage(); + } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..ee5a591fb449998fb1f9c0518dd79c0d9e32acf4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Customer\Test\TestCase\PasswordAutocompleteOffTest" summary="Test that autocomplete is off" ticketId="MAGETWO-45324"> + <variation name="RegisterCustomerFrontendEntityTestVariation1" summary="Test that autocomplete is off"> + <data name="product/dataset" xsi:type="string">default</data> + <data name="configData" xsi:type="string">disable_guest_checkout,password_autocomplete_off</data> + <constraint name="Magento\Customer\Test\Constraint\AssertCustomerPasswordAutocompleteOnAuthorizationPopup" /> + <constraint name="Magento\Customer\Test\Constraint\AssertCustomerPasswordAutocompleteOnSignIn" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/etc/testcase.xml index d34a96100f74e9b087ed3415e21a571dadb5ca44..4ecd82ac96da37315ed387968d7ed1eff01d83b0 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/etc/testcase.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/etc/testcase.xml @@ -10,7 +10,8 @@ <step name="createCustomer" module="Magento_Customer" next="openCustomerOnBackend"/> <step name="openCustomerOnBackend" module="Magento_Customer" next="createOrderFromCustomerAccount"/> <step name="createOrderFromCustomerAccount" module="Magento_Customer" next="createProducts"/> - <step name="createProducts" module="Magento_Catalog" next="addProducts"/> + <step name="createProducts" module="Magento_Catalog" next="selectStore"/> + <step name="selectStore" module="Magento_Sales" next="addProducts" /> <step name="addProducts" module="Magento_Sales" next="updateProductsData"/> <step name="updateProductsData" module="Magento_Sales" next="fillBillingAddress"/> <step name="fillBillingAddress" module="Magento_Sales" next="fillShippingAddress"/> diff --git a/dev/tests/functional/tests/app/Magento/Directory/Test/Constraint/AssertShippingPriceWithCustomCurrency.php b/dev/tests/functional/tests/app/Magento/Directory/Test/Constraint/AssertShippingPriceWithCustomCurrency.php new file mode 100644 index 0000000000000000000000000000000000000000..a2e3de1d933d9cd9200777ce17cd7ad2ffc7e128 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Directory/Test/Constraint/AssertShippingPriceWithCustomCurrency.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Directory\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Cms\Test\Page\CmsIndex; +use Magento\Checkout\Test\Page\CheckoutOnepage; +use Magento\Mtf\TestStep\TestStepFactory; +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\CurrencySymbol\Test\Fixture\CurrencySymbolEntity; + +/** + * Assert that shipping amount is correct in not base currency. + */ +class AssertShippingPriceWithCustomCurrency extends AbstractConstraint +{ + /** + * Assert that shipping amount is correct in not base currency in the checkout page. + * + * @param CmsIndex $cmsIndex + * @param CheckoutOnepage $checkoutOnepage + * @param TestStepFactory $testStepFactory + * @param CatalogProductSimple $product + * @param CurrencySymbolEntity $currencySymbol + * @param string $shippingAmount + * @param array $shipping + * @return void + */ + public function processAssert( + CmsIndex $cmsIndex, + CheckoutOnepage $checkoutOnepage, + TestStepFactory $testStepFactory, + CatalogProductSimple $product, + CurrencySymbolEntity $currencySymbol, + $shippingAmount, + array $shipping + ) { + $cmsIndex->open(); + $cmsIndex->getLinksBlock()->waitWelcomeMessage(); + $cmsIndex->getCurrencyBlock()->switchCurrency($currencySymbol); + $testStepFactory->create( + \Magento\Checkout\Test\TestStep\AddProductsToTheCartStep::class, + ['products' => [$product]] + )->run(); + $testStepFactory->create(\Magento\Checkout\Test\TestStep\ProceedToCheckoutStep::class)->run(); + \PHPUnit_Framework_Assert::assertEquals( + $shippingAmount, + $checkoutOnepage->getShippingMethodBlock()->getShippingMethodAmount($shipping), + 'Shipping amount is not correct in the checkout page.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return 'Shipping amount is correct in the checkout page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.xml b/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.xml index e984cad9ca327aa2a0896e44fc85e365e9f24503..a491a74aca727636d8015f4e7731a7ceac7765c1 100644 --- a/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.xml +++ b/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.xml @@ -33,5 +33,18 @@ <constraint name="Magento\Directory\Test\Constraint\AssertCurrencyRateSuccessSaveMessage" /> <constraint name="Magento\Directory\Test\Constraint\AssertCurrencyRateAppliedOnProductPage" /> </variation> + <variation name="CreateCurrencyRateTestVariation3" summary="Create currency rate if is not base currency" ticketId="MAGETWO-45310"> + <data name="currencyRate/data/currency_from" xsi:type="string">USD</data> + <data name="currencyRate/data/currency_to" xsi:type="string">UAH</data> + <data name="currencyRate/data/rate" xsi:type="number">0.5</data> + <data name="currencySymbol/dataset" xsi:type="string">currency_symbols_uah</data> + <data name="product" xsi:type="string">catalogProductSimple::simple_10_dollar</data> + <data name="shippingAmount" xsi:type="string">₴2.50</data> + <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="shipping/shipping_method" xsi:type="string">Fixed</data> + <data name="config/dataset" xsi:type="string">config_allowed_currency_usd_and_uah</data> + <constraint name="Magento\Directory\Test\Constraint\AssertCurrencyRateSuccessSaveMessage" /> + <constraint name="Magento\Directory\Test\Constraint\AssertShippingPriceWithCustomCurrency" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Downloadable/Test/Repository/DownloadableProduct.xml b/dev/tests/functional/tests/app/Magento/Downloadable/Test/Repository/DownloadableProduct.xml index c304fe8079b5618055f6976b80e0369be9c9ca82..90b0d9196c8dffb4daf727beb11a727834d18e4b 100644 --- a/dev/tests/functional/tests/app/Magento/Downloadable/Test/Repository/DownloadableProduct.xml +++ b/dev/tests/functional/tests/app/Magento/Downloadable/Test/Repository/DownloadableProduct.xml @@ -194,5 +194,34 @@ <item name="dataset" xsi:type="string">downloadable_one_dollar_product_with_no_separated_link</item> </field> </dataset> + + <dataset name="with_two_separately_links_buy_all"> + <field name="name" xsi:type="string">Downloadable product %isolation%</field> + <field name="sku" xsi:type="string">downloadable_product_%isolation%</field> + <field name="url_key" xsi:type="string">downloadable-product-%isolation%</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">20</item> + </field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">1</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="downloadable_links" xsi:type="array"> + <item name="dataset" xsi:type="string">with_two_separately_links</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">downloadable_with_two_separately_links</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Repository/GroupedProduct.xml b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Repository/GroupedProduct.xml index 7f1fd69d8e784d08363b07837c35e9ec30c54ea0..7522226ee804549079637e15f41b369899fec84e 100644 --- a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Repository/GroupedProduct.xml +++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Repository/GroupedProduct.xml @@ -38,6 +38,59 @@ </field> </dataset> + <dataset name="withSimpleProduct"> + <field name="name" xsi:type="string">Test grouped product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_grouped_product_%isolation%</field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="associated" xsi:type="array"> + <item name="dataset" xsi:type="string">one_simple_product</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">test-grouped-product-%isolation%</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + </dataset> + + <dataset name="withSimpleProduct_without_category"> + <field name="name" xsi:type="string">Test grouped product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_grouped_product_%isolation%</field> + <field name="associated" xsi:type="array"> + <item name="dataset" xsi:type="string">one_simple_product</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">test-grouped-product-%isolation%</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + </dataset> + <dataset name="grouped_product_out_of_stock"> <field name="name" xsi:type="string">Test grouped product %isolation%</field> <field name="sku" xsi:type="string">sku_test_grouped_product_%isolation%</field> @@ -129,6 +182,37 @@ </field> </dataset> + <dataset name="three_simple_products_buy_all"> + <field name="name" xsi:type="string">Grouped product %isolation%</field> + <field name="sku" xsi:type="string">grouped_product_%isolation%</field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="associated" xsi:type="array"> + <item name="dataset" xsi:type="string">three_simple_products_buy_all</item> + </field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">test-grouped-product-%isolation%</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">grouped_three_simple_products</item> + </field> + </dataset> + <dataset name="grouped_product_with_special_price"> <field name="name" xsi:type="string">Test grouped product with special price %isolation%</field> <field name="sku" xsi:type="string">sku_test_grouped_product_with_special_price_%isolation%</field> diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Repository/GroupedProduct/Associated.xml b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Repository/GroupedProduct/Associated.xml index 2360e0547c8c81bfdb0fe1cd4142ead581575ea9..519165a97ec757ac37b93a71c0c32a56da6a85bf 100644 --- a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Repository/GroupedProduct/Associated.xml +++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Repository/GroupedProduct/Associated.xml @@ -28,6 +28,20 @@ </field> </dataset> + <dataset name="one_simple_product"> + <field name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="id" xsi:type="string">%id%</item> + <item name="name" xsi:type="string">%item1_simple::getProductName%</item> + <item name="position" xsi:type="string">%position%</item> + <item name="qty" xsi:type="string">1</item> + </item> + </field> + <field name="products" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default</item> + </field> + </dataset> + <dataset name="defaultSimpleProduct_without_qty"> <field name="assigned_products" xsi:type="array"> <item name="0" xsi:type="array"> @@ -119,6 +133,34 @@ </field> </dataset> + <dataset name="three_simple_products_buy_all"> + <field name="assigned_products" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="id" xsi:type="string">%id%</item> + <item name="name" xsi:type="string">%item1_simple::getProductName%</item> + <item name="position" xsi:type="string">%position%</item> + <item name="qty" xsi:type="string">3</item> + </item> + <item name="1" xsi:type="array"> + <item name="id" xsi:type="string">%id%</item> + <item name="name" xsi:type="string">%item1_simple::getProductName%</item> + <item name="position" xsi:type="string">%position%</item> + <item name="qty" xsi:type="string">1</item> + </item> + <item name="2" xsi:type="array"> + <item name="id" xsi:type="string">%id%</item> + <item name="name" xsi:type="string">%item1_simple::getProductName%</item> + <item name="position" xsi:type="string">%position%</item> + <item name="qty" xsi:type="string">2</item> + </item> + </field> + <field name="products" xsi:type="array"> + <item name="0" xsi:type="string">catalogProductSimple::default_qty_3</item> + <item name="1" xsi:type="string">catalogProductSimple::default_qty_1</item> + <item name="2" xsi:type="string">catalogProductSimple::default_qty_2</item> + </field> + </dataset> + <dataset name="simple_downloadable_virtual"> <field name="assigned_products" xsi:type="array"> <item name="0" xsi:type="array"> diff --git a/dev/tests/functional/tests/app/Magento/Indexer/Test/Block/Adminhtml/IndexManagement/Grid.php b/dev/tests/functional/tests/app/Magento/Indexer/Test/Block/Adminhtml/IndexManagement/Grid.php new file mode 100644 index 0000000000000000000000000000000000000000..64642a5b37012b3fc8d609853d433d11b869d83a --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Indexer/Test/Block/Adminhtml/IndexManagement/Grid.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Indexer\Test\Block\Adminhtml\IndexManagement; + +use Magento\Mtf\Client\Locator; +use Magento\Backend\Test\Block\Widget\Grid as WidgetGrid; + +/** + * Grid in Index Management Page. + */ +class Grid extends WidgetGrid +{ + /** + * Select action toggle. + * + * @var string + */ + private $selectAction = './/*[@id=\'gridIndexer_massaction-select\']/option[.=\'Update by Schedule\']'; + + /** + * Select Submit Button. + * + * @var string + */ + private $updateButton = './/button[@title=\'Submit\']'; + + /** + * Indexer Status locator. + * + * @var string + */ + private $indxerStatus = './/*[@data-column=\'indexer_status\']/span/span'; + + /** + * Filters array mapping. + * + * @var array + */ + protected $filters = [ + 'Indexer' => [ + 'selector' => '[name="indexer_ids"]', + 'input' => 'checkbox', + 'value' => 'Yes', + ], + ]; + + /** + * Update indexers action in Index Management Page. + * + * @param array $indexers + * @throws \Exception + * @return void + */ + public function updateBySchedule(array $indexers) + { + foreach ($indexers as $indexer) { + $selectItem = $this->getRow(['Indexer' => trim($indexer)])->find($this->selectItem); + if ($selectItem->isVisible()) { + $selectItem->click(); + } else { + throw new \Exception("Searched item was not found by filter\n" . print_r($indexer, true)); + } + } + $this->_rootElement->find($this->selectAction, Locator::SELECTOR_XPATH, 'select')->click(); + $this->_rootElement->find($this->updateButton, Locator::SELECTOR_XPATH)->click(); + } + + /** + * Return indexers status in Index Management Page. + * + * @param string $indexer + * @return string|array + */ + public function getIndexerStatus($indexer) + { + return $this->getRow(['Indexer' => trim($indexer)]) + ->find($this->indxerStatus, Locator::SELECTOR_XPATH)->getText(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Indexer/Test/Constraint/AssertIndexerStatus.php b/dev/tests/functional/tests/app/Magento/Indexer/Test/Constraint/AssertIndexerStatus.php new file mode 100644 index 0000000000000000000000000000000000000000..38e45ed3cd68ebf8538f1e07a060a9a288ef8925 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Indexer/Test/Constraint/AssertIndexerStatus.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Indexer\Test\Constraint; + +use Magento\Indexer\Test\Page\Adminhtml\IndexManagement; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert Indexer status in Index Management Page. + */ +class AssertIndexerStatus extends AbstractConstraint +{ + /** + * Indexer status in Index Management Page. + * + * @var array + */ + private $indexerStatus = [ + 0 => 'REINDEX REQUIRED', + 1 => 'READY' + ]; + + /** + * Assert Correct Indexer Status. + * + * @param IndexManagement $indexManagement + * @param array $indexers + * @param bool $expectedStatus + * @return void + */ + public function processAssert(IndexManagement $indexManagement, array $indexers, bool $expectedStatus = true) + { + $expectedStatus = $expectedStatus === false ? $this->indexerStatus[0] : $this->indexerStatus[1]; + $indexManagement->open(); + foreach ($indexers as $indexer) { + $indexerStatus = $indexManagement->getMainBlock()->getIndexerStatus($indexer); + \PHPUnit_Framework_Assert::assertEquals( + $expectedStatus, + $indexerStatus, + 'Wrong ' . $indexer . ' status is displayed.' + . "\nExpected: " . $expectedStatus + . "\nActual: " . $indexerStatus + ); + } + } + + /** + * Returns indexers status. + * + * @return string + */ + public function toString() + { + return 'Indexer status is correct.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Indexer/Test/Constraint/AssertUpdateByScheduleSuccessSaveMessage.php b/dev/tests/functional/tests/app/Magento/Indexer/Test/Constraint/AssertUpdateByScheduleSuccessSaveMessage.php new file mode 100644 index 0000000000000000000000000000000000000000..13f40ac38dcc09629b1efe1b157cb7a2b809cf95 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Indexer/Test/Constraint/AssertUpdateByScheduleSuccessSaveMessage.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Indexer\Test\Constraint; + +use Magento\Indexer\Test\Page\Adminhtml\IndexManagement; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert indexers status after change action. + */ +class AssertUpdateByScheduleSuccessSaveMessage extends AbstractConstraint +{ + /** + * Text of save success message. + */ + const SUCCESS_SAVE_MESSAGE = '%s indexer(s) are in "Update by Schedule" mode.'; + + /** + * Assert attribute Update by Schedule. + * + * @param IndexManagement $indexManagement + * @param array $indexers + * @return void + */ + public function processAssert(IndexManagement $indexManagement, array $indexers) + { + $actualMessage = $indexManagement->getMessagesBlock()->getSuccessMessage(); + \PHPUnit_Framework_Assert::assertEquals( + sprintf(self::SUCCESS_SAVE_MESSAGE, count($indexers)), + $actualMessage, + 'Wrong success message is displayed.' + . "\nExpected: " . sprintf(self::SUCCESS_SAVE_MESSAGE, count($indexers)) + . "\nActual: " . $actualMessage + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Attribute Update by Schedule message is present.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Indexer/Test/Page/Adminhtml/IndexManagement.xml b/dev/tests/functional/tests/app/Magento/Indexer/Test/Page/Adminhtml/IndexManagement.xml new file mode 100644 index 0000000000000000000000000000000000000000..b955ed5548b457592b75728c5aeaa79bcbd8fae9 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Indexer/Test/Page/Adminhtml/IndexManagement.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" ?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> + <page name="IndexManagement" area="Adminhtml" mca="indexer/indexer/list" module="Magento_Indexer"> + <block name="mainBlock" class="Magento\Indexer\Test\Block\Adminhtml\IndexManagement\Grid" locator="#anchor-content" strategy="css selector" /> + <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator=".messages .message" strategy="css selector" /> + </page> +</config> diff --git a/dev/tests/functional/tests/app/Magento/PageCache/Test/Constraint/AssertCacheStatus.php b/dev/tests/functional/tests/app/Magento/PageCache/Test/Constraint/AssertCacheStatus.php index 22adf03401c77b8fb7663ad6a234f4141cd3cb79..f5809f8b9d855dc404ea239f7cad81467efc0d09 100644 --- a/dev/tests/functional/tests/app/Magento/PageCache/Test/Constraint/AssertCacheStatus.php +++ b/dev/tests/functional/tests/app/Magento/PageCache/Test/Constraint/AssertCacheStatus.php @@ -21,6 +21,7 @@ class AssertCacheStatus extends AbstractConstraint */ private $cacheTypes = [ 'block_html' => "Blocks HTML output", + 'full_page' => "Page Cache", ]; /** diff --git a/dev/tests/functional/tests/app/Magento/PageCache/Test/Constraint/AssertCategoryCaching.php b/dev/tests/functional/tests/app/Magento/PageCache/Test/Constraint/AssertCategoryCaching.php new file mode 100644 index 0000000000000000000000000000000000000000..7c6e5fcca61256500eba7b208038df7f5e7a01e5 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/PageCache/Test/Constraint/AssertCategoryCaching.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\PageCache\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Catalog\Test\Fixture\Category; +use Magento\Catalog\Test\Constraint\AssertProductNotVisibleInCategory; +use Magento\Catalog\Test\Constraint\AssertProductInCategory; +use Magento\Catalog\Test\Page\Category\CatalogCategoryView; +use Magento\Cms\Test\Page\CmsIndex; +use Magento\Mtf\Util\Command\Cli\Cron; + +/** + * Assert that category products are visible after indexing. + */ +class AssertCategoryCaching extends AbstractConstraint +{ + /** + * Assert category products are displayed only after indexing. + * + * @param Category $category + * @param CatalogCategoryView $categoryView + * @param CmsIndex $cmsIndex + * @param AssertProductInCategory $assertProduct + * @param AssertProductNotVisibleInCategory $assertProductNotVisible + * @param Cron $cron + * @return void + */ + public function processAssert( + Category $category, + CatalogCategoryView $categoryView, + CmsIndex $cmsIndex, + AssertProductInCategory $assertProduct, + AssertProductNotVisibleInCategory $assertProductNotVisible, + Cron $cron + ) { + $products = $category->getDataFieldConfig('category_products')['source']->getProducts(); + foreach ($products as $product) { + $assertProductNotVisible->processAssert($categoryView, $cmsIndex, $product, $category); + } + + $cron->run(); + $cron->run(); + + foreach ($products as $product) { + $assertProduct->processAssert($categoryView, $cmsIndex, $product, $category); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Category products are visible after indexing.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheStatusOnScheduledIndexingTest.php b/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheStatusOnScheduledIndexingTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e4aa022667021a9f893ea901c2c9916ffce027b2 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheStatusOnScheduledIndexingTest.php @@ -0,0 +1,136 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\PageCache\Test\TestCase; + +use Magento\Mtf\TestCase\Injectable; +use Magento\Indexer\Test\Page\Adminhtml\IndexManagement; +use Magento\Catalog\Test\Page\Adminhtml\CatalogCategoryEdit; +use Magento\Catalog\Test\Fixture\Category; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\Util\Command\Cli\Cache; +use Magento\Mtf\Fixture\FixtureFactory; + +/** + * Steps: + * 1. Set all indexers to Update on Schedule mode. + * 2. Clear all caches. + * 3. Create a category. + * 4. Add some products to the category. + * 5. Perform asserts. + * + * @ZephyrId MAGETWO-45833 + */ +class CacheStatusOnScheduledIndexingTest extends Injectable +{ + /* tags */ + const MVP = 'no'; + /* end tags */ + + /** + * Index Management page. + * + * @var IndexManagement + */ + private $indexManagement; + + /** + * Category Edit page. + * + * @var CatalogCategoryEdit + */ + private $categoryEdit; + + /** + * Browser instance. + * + * @var BrowserInterface + */ + private $browser; + + /** + * Fixture factory. + * + * @var FixtureFactory + */ + private $fixtureFactory; + + /** + * Cli command to do operations with cache. + * + * @var Cache + */ + private $cache; + + /** + * Inject pages. + * + * @param IndexManagement $indexManagement + * @param CatalogCategoryEdit $categoryEdit + * @param BrowserInterface $browser + * @param FixtureFactory $fixtureFactory + * @param Cache $cache + * @return void + */ + public function __inject( + IndexManagement $indexManagement, + CatalogCategoryEdit $categoryEdit, + BrowserInterface $browser, + FixtureFactory $fixtureFactory, + Cache $cache + ) { + $this->indexManagement = $indexManagement; + $this->categoryEdit = $categoryEdit; + $this->browser = $browser; + $this->fixtureFactory = $fixtureFactory; + $this->cache = $cache; + } + + /** + * Create category with products and verify cache invalidation. + * + * @param Category $initialCategory + * @param Category $category + * @return array + */ + public function test(Category $initialCategory, Category $category) + { + $this->indexManagement->open(); + $this->indexManagement->getMainBlock()->massaction([], 'Update by Schedule', false, 'Select All'); + $initialCategory->persist(); + $this->cache->flush(); + + $this->browser->open($_ENV['app_frontend_url'] . $initialCategory->getUrlKey() . '.html'); + $this->categoryEdit->open(['id' => $initialCategory->getId()]); + $this->categoryEdit->getEditForm()->fill($category); + $this->categoryEdit->getFormPageActions()->save(); + + $products = $category->getDataFieldConfig('category_products')['source']->getProducts(); + return [ + 'category' => $this->fixtureFactory->createByCode( + 'category', + [ + 'data' => array_merge( + $initialCategory->getData(), + $category->getData(), + ['category_products' => ['products' => $products]] + ) + ] + ), + ]; + } + + /** + * Restore indexers mode. + * + * @return void + */ + public function tearDown() + { + $this->indexManagement->open(); + $this->indexManagement->getMainBlock()->massaction([], 'Update on Save', false, 'Select All'); + } +} diff --git a/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheStatusOnScheduledIndexingTest.xml b/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheStatusOnScheduledIndexingTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..9f16518ae61764d2bee702e8d394b3a9f50e58ca --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/CacheStatusOnScheduledIndexingTest.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\PageCache\Test\TestCase\CacheStatusOnScheduledIndexingTest" summary="Page cache invalidation and flushing on scheduled indexing" ticketId="MAGETWO-45833"> + <variation name="CacheStatusOnScheduledIndexingTestVariation1" summary="Verify page cache invalidation and flushing on scheduled indexing" ticketId="MAGETWO-45833"> + <data name="initialCategory/dataset" xsi:type="string">default</data> + <data name="category/data/category_products/dataset" xsi:type="string">catalogProductSimple::default,catalogProductSimple::product_20_dollar</data> + <data name="caches/full_page" xsi:type="string">Enabled</data> + <constraint name="Magento\PageCache\Test\Constraint\AssertCategoryCaching" /> + <constraint name="Magento\PageCache\Test\Constraint\AssertCacheStatus" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/CreditMemos/Grid.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/CreditMemos/Grid.php index 53833125dce7ff55eded48828046c5a047a83cc9..1cfad8033bd5755df01975559682db88c4b26d61 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/CreditMemos/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/CreditMemos/Grid.php @@ -6,11 +6,13 @@ namespace Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\CreditMemos; +use Magento\Mtf\Client\Locator; +use Magento\Ui\Test\Block\Adminhtml\DataGrid; + /** - * Class Grid - * Credit memos grid on order view page + * Credit memos grid on order view page. */ -class Grid extends \Magento\Backend\Test\Block\Widget\Grid +class Grid extends DataGrid { /** * Base part of row locator template for getRow() method. @@ -20,14 +22,28 @@ class Grid extends \Magento\Backend\Test\Block\Widget\Grid protected $rowPattern = './/tr[%s]'; /** - * Locator value for link in action column + * Locator value for link in action column. * * @var string */ protected $editLink = 'tbody td[data-column="increment_id"]'; /** - * Filters array mapping + * Css selector for credit memo ids. + * + * @var string + */ + protected $creditMemoId = 'tbody td:nth-child(2)'; + + /** + * CreditMemos data grid loader Xpath locator. + * + * @var string + */ + protected $loader = '//div[contains(@data-component, "sales_order_view_creditmemo_grid")]'; + + /** + * Filters array mapping. * * @var array */ @@ -48,24 +64,26 @@ class Grid extends \Magento\Backend\Test\Block\Widget\Grid ]; /** - * Get credit memo id from grid + * Get credit memo id from grid. * * @return array|string */ public function getCreditMemoId() { - return $this->_rootElement->find($this->editLink)->getText(); + $this->waitForElementNotVisible($this->loader, Locator::SELECTOR_XPATH); + return $this->_rootElement->find($this->creditMemoId)->getText(); } /** - * Get credit memo ids + * Get credit memo ids. * * @return array */ public function getIds() { $result = []; - $creditMemoIds = $this->_rootElement->getElements($this->editLink); + $this->waitForElementNotVisible($this->loader, Locator::SELECTOR_XPATH); + $creditMemoIds = $this->_rootElement->getElements($this->creditMemoId); foreach ($creditMemoIds as $creditMemoId) { $result[] = trim($creditMemoId->getText()); } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Invoices/Grid.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Invoices/Grid.php index 41fbd8979b58df8fb58f000e3ea79e0f289344c4..c036067b3bbf270fba7353d48dfa75bf1f2a0e56 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Invoices/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Invoices/Grid.php @@ -6,6 +6,8 @@ namespace Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Invoices; +use Magento\Mtf\Client\Locator; + /** * Invoices grid on order view page. */ @@ -19,18 +21,18 @@ class Grid extends \Magento\Ui\Test\Block\Adminhtml\DataGrid protected $editLink = '[data-column="increment_id"]'; /** - * Locator for invoice ids + * Css selector for invoice ids. * * @var string */ protected $invoiceId = 'tbody td:nth-child(2)'; /** - * Invoices data grid loader locator. + * Invoices data grid loader Xpath locator. * * @var string */ - protected $loader = '[data-component="sales_order_view_invoice_grid"]'; + protected $loader = '//div[contains(@data-component, "sales_order_view_invoice_grid")]'; /** * Filters array mapping. @@ -64,7 +66,7 @@ class Grid extends \Magento\Ui\Test\Block\Adminhtml\DataGrid public function getIds() { $result = []; - $this->waitForElementNotVisible($this->loader); + $this->waitForElementNotVisible($this->loader, Locator::SELECTOR_XPATH); $invoiceIds = $this->_rootElement->getElements($this->invoiceId); foreach ($invoiceIds as $invoiceId) { $result[] = trim($invoiceId->getText()); @@ -80,7 +82,7 @@ class Grid extends \Magento\Ui\Test\Block\Adminhtml\DataGrid */ public function viewInvoice() { - $this->waitForElementNotVisible($this->loader); + $this->waitForElementNotVisible($this->loader, Locator::SELECTOR_XPATH); $this->_rootElement->find($this->invoiceId)->click(); } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Shipments/Grid.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Shipments/Grid.php index 7589e0d82f4e530cd1e58f561da0b01510af5235..f92594bec680776e72fac29435dfac72ccef6ec6 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Shipments/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Shipments/Grid.php @@ -6,34 +6,43 @@ namespace Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Shipments; +use Magento\Mtf\Client\Locator; +use Magento\Ui\Test\Block\Adminhtml\DataGrid; + /** - * Class Grid - * Shipments grid on order view page + * Shipments grid on order view page. */ -class Grid extends \Magento\Backend\Test\Block\Widget\Grid +class Grid extends DataGrid { /** - * Locator value for link in action column + * Locator value for link in action column. * * @var string */ protected $editLink = '[data-column="real_shipment_id"]'; /** - * Locator for shipment ids + * Css selector for shipment ids. + * + * @var string + */ + protected $shipmentId = 'tbody td:nth-child(2)'; + + /** + * Shipments data grid loader Xpath locator. * * @var string */ - protected $shipmentId = 'tbody td[data-column="real_shipment_id"]'; + protected $loader = '//div[contains(@data-component, "sales_order_view_shipment_grid")]'; /** - * Filters array mapping + * Filters array mapping. * * @var array */ protected $filters = [ 'id' => [ - 'selector' => 'input[name="real_shipment_id"]', + 'selector' => 'input[name="increment_id"]', ], 'qty_from' => [ 'selector' => '[name="total_qty[from]"]', @@ -44,13 +53,14 @@ class Grid extends \Magento\Backend\Test\Block\Widget\Grid ]; /** - * Get shipment ids + * Get shipment ids. * * @return array */ public function getIds() { $result = []; + $this->waitForElementNotVisible($this->loader, Locator::SELECTOR_XPATH); $shipmentIds = $this->_rootElement->getElements($this->shipmentId); foreach ($shipmentIds as $shipmentId) { $result[] = trim($shipmentId->getText()); diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceNotInInvoicesGrid.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceNotInInvoicesGrid.php new file mode 100644 index 0000000000000000000000000000000000000000..fd6347e481c9c24d1ee73ed7fc6ccac4578417c6 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceNotInInvoicesGrid.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Test\Constraint; + +use Magento\Sales\Test\Fixture\OrderInjectable; +use Magento\Sales\Test\Page\Adminhtml\InvoiceIndex; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that invoice with corresponding order ID is absent in the invoices grid. + */ +class AssertInvoiceNotInInvoicesGrid extends AbstractConstraint +{ + /* tags */ + const SEVERITY = 'low'; + /* end tags */ + + /** + * Assert that invoice with corresponding order ID is absent in the invoices grid. + * + * @param InvoiceIndex $invoiceIndex + * @param OrderInjectable $order + * @param array $ids + * @return void + */ + public function processAssert(InvoiceIndex $invoiceIndex, OrderInjectable $order, array $ids) + { + $invoiceIndex->open(); + $amount = $order->getPrice(); + $orderId = $order->getId(); + foreach ($ids['invoiceIds'] as $key => $invoiceId) { + $filter = [ + 'id' => $invoiceId, + 'order_id' => $orderId, + 'grand_total_from' => $amount[$key]['grand_invoice_total'], + 'grand_total_to' => $amount[$key]['grand_invoice_total'], + ]; + $invoiceIndex->getInvoicesGrid()->search($filter); + $filter['grand_total_from'] = number_format($amount[$key]['grand_invoice_total'], 2); + $filter['grand_total_to'] = number_format($amount[$key]['grand_invoice_total'], 2); + \PHPUnit_Framework_Assert::assertFalse( + $invoiceIndex->getInvoicesGrid()->isRowVisible($filter, false, false), + 'Invoice is present in invoices grid on invoice index page.' + ); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Invoice is absent in the invoices grid on invoice index page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductQtyDecreased.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductQtyDecreased.php new file mode 100644 index 0000000000000000000000000000000000000000..0d754dcfcd8f67ba93c602381ec0ed0a213937f0 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductQtyDecreased.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Test\Constraint; + +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Mtf\Fixture\FixtureInterface; +use Magento\Mtf\ObjectManager; +use Magento\Mtf\System\Event\EventManagerInterface; +use Magento\Sales\Test\Fixture\OrderInjectable; + +/** + * Assert that product quantity is decreased after placing an order. + */ +class AssertProductQtyDecreased extends AbstractConstraint +{ + /** + * @var FixtureFactory + */ + protected $fixtureFactory; + + /** + * Skip fields for create product fixture. + * + * @var array + */ + protected $skipFields = [ + 'attribute_set_id', + 'website_ids', + 'checkout_data', + 'type_id', + 'price', + ]; + + /** + * AssertProductQtyDecreased constructor. + * + * @param ObjectManager $objectManager + * @param EventManagerInterface $eventManager + * @param FixtureFactory $fixtureFactory + */ + public function __construct( + ObjectManager $objectManager, + EventManagerInterface $eventManager, + FixtureFactory $fixtureFactory + ) { + $this->fixtureFactory = $fixtureFactory; + parent::__construct($objectManager, $eventManager); + } + + /** + * Assert form data equals fixture data. + * + * @param OrderInjectable $order + * @param CatalogProductIndex $productGrid + * @param CatalogProductEdit $productPage + * @return void + */ + public function processAssert( + OrderInjectable $order, + CatalogProductIndex $productGrid, + CatalogProductEdit $productPage + ) { + $product = $this->getProduct($order); + $this->objectManager->get(\Magento\Catalog\Test\Constraint\AssertProductForm::class)->processAssert( + $product, + $productGrid, + $productPage + ); + } + + /** + * Get product's fixture. + * + * @param OrderInjectable $order + * @param int $index [optional] + * @return FixtureInterface + */ + protected function getProduct(OrderInjectable $order, $index = 0) + { + $product = $order->getEntityId()['products'][$index]; + $productData = $product->getData(); + $checkoutDataQty = isset($productData['checkout_data']['qty']) ? $productData['checkout_data']['qty'] : 1; + $productData['quantity_and_stock_status']['qty'] -= $checkoutDataQty; + + $productData = array_diff_key($productData, array_flip($this->skipFields)); + + return $this->fixtureFactory->create(get_class($product), ['data' => $productData]); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Product qty was decreased after placing an order.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductsQtyAfterOrderCancel.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductsQtyAfterOrderCancel.php new file mode 100644 index 0000000000000000000000000000000000000000..028cf9b9267e39aa65e0a7674bd2f969ca495c35 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertProductsQtyAfterOrderCancel.php @@ -0,0 +1,128 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Test\Constraint; + +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Sales\Test\Fixture\OrderInjectable; +use Magento\Bundle\Test\Fixture\BundleProduct; +use Magento\ConfigurableProduct\Test\Fixture\ConfigurableProduct; +use Magento\Catalog\Test\Constraint\AssertProductForm; +use Magento\ConfigurableProduct\Test\Constraint\AssertConfigurableProductForm; + +/** + * Assert that products quantity was increased after order cancel. + */ +class AssertProductsQtyAfterOrderCancel extends AbstractConstraint +{ + /** + * Skip fields for create product fixture. + * + * @var array + */ + protected $skipFields = [ + 'attribute_set_id', + 'website_ids', + 'checkout_data', + 'type_id', + 'price', + ]; + + /** + * Assert form data equals fixture data. + * + * @param OrderInjectable $order + * @param CatalogProductIndex $productGrid + * @param CatalogProductEdit $productPage + * @param FixtureFactory $fixtureFactory + * @param AssertProductForm $assertProductForm + * @param AssertConfigurableProductForm $assertConfigurableProductForm + * @return void + */ + public function processAssert( + OrderInjectable $order, + CatalogProductIndex $productGrid, + CatalogProductEdit $productPage, + FixtureFactory $fixtureFactory, + AssertProductForm $assertProductForm, + AssertConfigurableProductForm $assertConfigurableProductForm + ) { + for ($i = 0; $i < count($order->getEntityId()['products']); $i++) { + $product = $order->getEntityId()['products'][$i]; + $productData = $product->getData(); + if ($product instanceof BundleProduct) { + $this->assertBundleProduct($product, $productGrid, $productPage, $fixtureFactory, $assertProductForm); + } elseif ($product instanceof ConfigurableProduct) { + $assertConfigurableProductForm->processAssert( + $fixtureFactory->create( + get_class($product), + ['data' => array_diff_key($productData, array_flip($this->skipFields))] + ), + $productGrid, + $productPage + ); + } else { + $assertProductForm->processAssert( + $fixtureFactory->create( + get_class($product), + ['data' => array_diff_key($productData, array_flip($this->skipFields))] + ), + $productGrid, + $productPage + ); + } + } + } + + /** + * Assert quantity of products that are part of the bundle product. + * + * @param BundleProduct $product + * @param CatalogProductIndex $productGrid + * @param CatalogProductEdit $productPage + * @param FixtureFactory $fixtureFactory + * @param AssertProductForm $assertProductForm + * @return void + */ + public function assertBundleProduct( + BundleProduct $product, + CatalogProductIndex $productGrid, + CatalogProductEdit $productPage, + FixtureFactory $fixtureFactory, + AssertProductForm $assertProductForm + ) { + $productData = $product->getData(); + $bundleSelections = $product->getDataFieldConfig('bundle_selections')['source']->getProducts(); + foreach ($bundleSelections as $key => $selection) { + $valueName = $productData['checkout_data']['options']['bundle_options'][$key]['value']['name']; + foreach ($selection as $item) { + if (strpos($item->getName(), $valueName) !== false) { + $assertProductForm->processAssert( + $fixtureFactory->create( + get_class($product), + ['data' => array_diff_key($item->getData(), array_flip($this->skipFields))] + ), + $productGrid, + $productPage + ); + break; + } + } + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Products quantity was reverted after order cancel.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertRefundNotInRefundsGrid.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertRefundNotInRefundsGrid.php new file mode 100644 index 0000000000000000000000000000000000000000..c754d6529c0ef04a1a6f4e307cd151de917eb871 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertRefundNotInRefundsGrid.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Test\Constraint; + +use Magento\Sales\Test\Fixture\OrderInjectable; +use Magento\Sales\Test\Page\Adminhtml\CreditMemoIndex; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that refund is absent in the refunds grid. + */ +class AssertRefundNotInRefundsGrid extends AbstractConstraint +{ + /** + * Assert that refund is absent in the refunds grid. + * + * @param CreditMemoIndex $creditMemoIndex + * @param OrderInjectable $order + * @param array $ids + * @return void + */ + public function processAssert(CreditMemoIndex $creditMemoIndex, OrderInjectable $order, array $ids) + { + $creditMemoIndex->open(); + $amount = $order->getPrice(); + $orderId = $order->getId(); + foreach ($ids['creditMemoIds'] as $key => $creditMemoId) { + $filter = [ + 'id' => $creditMemoId, + 'order_id' => $orderId, + 'grand_total_from' => $amount[$key]['grand_creditmemo_total'], + 'grand_total_to' => $amount[$key]['grand_creditmemo_total'], + ]; + $creditMemoIndex->getCreditMemoGrid()->search($filter); + $filter['grand_total_from'] = number_format($amount[$key]['grand_creditmemo_total'], 2); + $filter['grand_total_to'] = number_format($amount[$key]['grand_creditmemo_total'], 2); + \PHPUnit_Framework_Assert::assertFalse( + $creditMemoIndex->getCreditMemoGrid()->isRowVisible($filter, false, false), + "Credit memo '#$creditMemoId' is present in credit memos grid on credit memo index page." + ); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Credit memo is absent in credit memos grid on credit memo index page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Fixture/OrderInjectable/StoreId.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Fixture/OrderInjectable/StoreId.php index 1e393033ffb057e53786a84f776cefbf9d50dbeb..2b6771b59d324de90b77ceef50991ab11112afab 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Fixture/OrderInjectable/StoreId.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Fixture/OrderInjectable/StoreId.php @@ -7,6 +7,7 @@ namespace Magento\Sales\Test\Fixture\OrderInjectable; use Magento\Store\Test\Fixture\Store; +use Magento\Store\Test\Fixture\Website; use Magento\Mtf\Fixture\FixtureFactory; use Magento\Mtf\Fixture\DataSource; @@ -22,6 +23,13 @@ class StoreId extends DataSource */ public $store; + /** + * Website fixture. + * + * @var Website + */ + private $website; + /** * @constructor * @param FixtureFactory $fixtureFactory @@ -31,20 +39,33 @@ class StoreId extends DataSource public function __construct(FixtureFactory $fixtureFactory, array $data, array $params = []) { $this->params = $params; - $storeData = isset($data['dataset']) ? ['dataset' => $data['dataset']] : []; if (isset($data['data'])) { $storeData = array_replace_recursive($storeData, $data); } - if ($storeData) { - $store = $fixtureFactory->createByCode('store', $storeData); - /** @var Store $store */ - if (!$store->getStoreId()) { - $store->persist(); + if (isset($data['store'])) { + $this->store = $data['store']; + $website = $data['store']->getDataFieldConfig('group_id')['source'] + ->getStoreGroup()->getDataFieldConfig('website_id')['source']->getWebsite(); + $this->website = $website; + $this->data = $data['store']->getName(); + } else { + if ($storeData) { + $store = $fixtureFactory->createByCode('store', $storeData); + /** @var Store $store */ + if (!$store->getStoreId()) { + $store->persist(); + } + if (isset($store->getData()['group_id'])) { + $website = $store->getDataFieldConfig('group_id')['source'] + ->getStoreGroup()->getDataFieldConfig('website_id')['source']->getWebsite(); + $this->website = $website; + } + + $this->store = $store; + $this->data = $store->getName(); } - $this->store = $store; - $this->data = $store->getName(); } } @@ -57,4 +78,17 @@ class StoreId extends DataSource { return $this->store; } + + /** + * Return Website fixture. + * + * @return Website|null + */ + public function getWebsite() + { + if (isset($this->website)) { + return $this->website; + } + return null; + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Handler/OrderInjectable/Webapi.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Handler/OrderInjectable/Webapi.php index 1286064662ebe097ae1239da04419ac1bad8b8a9..f0d63f71e7aa4b349147852bf0746b9676337a3b 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Handler/OrderInjectable/Webapi.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Handler/OrderInjectable/Webapi.php @@ -68,9 +68,8 @@ class Webapi extends AbstractWebapi implements OrderInjectableInterface /** @var OrderInjectable $fixture */ $this->createQuote($fixture); - $url = $this->isCustomerGuest ? 'guest-carts/' . $this->quote : 'carts/' . (int)$this->quote; - $this->url = $_ENV['app_frontend_url'] . 'rest/V1/' . $url; + $this->url = $_ENV['app_frontend_url'] . $this->prepareWebsiteUrl($fixture) . '/V1/' . $url; $this->setProducts($fixture); $this->setCoupon($fixture); @@ -78,8 +77,9 @@ class Webapi extends AbstractWebapi implements OrderInjectableInterface $this->setShippingInformation($fixture); $this->setPaymentMethod($fixture); $orderId = $this->placeOrder(); + $orderIncrementId = $this->getOrderIncrementId($orderId); - return ['id' => sprintf("%09d", $orderId)]; + return ['id' => $orderIncrementId]; } /** @@ -92,7 +92,7 @@ class Webapi extends AbstractWebapi implements OrderInjectableInterface protected function createQuote(OrderInjectable $order) { if ($this->isCustomerGuest) { - $url = $_ENV['app_frontend_url'] . 'rest/V1/guest-carts'; + $url = $_ENV['app_frontend_url'] . $this->prepareWebsiteUrl($order) . '/V1/guest-carts'; $this->webapiTransport->write($url); $response = json_decode($this->webapiTransport->read(), true); $this->webapiTransport->close(); @@ -102,7 +102,8 @@ class Webapi extends AbstractWebapi implements OrderInjectableInterface } $this->quote = $response; } else { - $url = $_ENV['app_frontend_url'] . 'rest/V1/customers/' . $order->getCustomerId()->getId() . '/carts'; + $url = $_ENV['app_frontend_url'] . $this->prepareWebsiteUrl($order) + . '/V1/customers/' . $order->getCustomerId()->getId() . '/carts'; $data = '{"customerId": "' . $order->getCustomerId()->getId() . '"}'; $this->webapiTransport->write($url, $data); $response = json_decode($this->webapiTransport->read(), true); @@ -357,4 +358,41 @@ class Webapi extends AbstractWebapi implements OrderInjectableInterface return ['extension_attributes' => ['downloadable_option' => ['downloadable_links' => $links]]]; } + + /** + * Prepare url for placing order in custom website. + * + * @param OrderInjectable $order + * @return string + */ + private function prepareWebsiteUrl(OrderInjectable $order) + { + $url = 'rest'; + if ($website = $order->getDataFieldConfig('store_id')['source']->getWebsite()) { + $store = $order->getDataFieldConfig('store_id')['source']->getStore(); + $url = 'websites/' . $website->getCode() . '/rest/' . $store->getCode(); + } + return $url; + } + + /** + * Retrieve order increment id. + * + * @param int $orderId + * @return string + * @throws \Exception + */ + private function getOrderIncrementId($orderId) + { + $url = $_ENV['app_frontend_url'] . 'rest/V1/orders/' . $orderId; + $this->webapiTransport->write($url, [], WebapiDecorator::GET); + $response = json_decode($this->webapiTransport->read(), true); + $this->webapiTransport->close(); + + if (!$response['increment_id']) { + $this->eventManager->dispatchEvent(['webapi_failed'], [$response]); + throw new \Exception('Could not get order details using web API.'); + } + return $response['increment_id']; + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Page/Adminhtml/OrderCreateIndex.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/Page/Adminhtml/OrderCreateIndex.xml index f80715543de67036849b982ec0defa92ba66af4a..6ef15dc192dab91f5279b80539d29e4e34c28500 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Page/Adminhtml/OrderCreateIndex.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Page/Adminhtml/OrderCreateIndex.xml @@ -14,5 +14,6 @@ <block name="couponsBlock" class="Magento\Sales\Test\Block\Adminhtml\Order\Create\Coupons" locator="#order-coupons" strategy="css selector"/> <block name="customerActivitiesBlock" class="Magento\Sales\Test\Block\Adminhtml\Order\Create\CustomerActivities" locator=".customer-current-activity" strategy="css selector"/> <block name="configureProductBlock" class="Magento\Catalog\Test\Block\Adminhtml\Product\Composite\Configure" locator="//*[@data-role='modal' and .//*[@id='product_composite_configure'] and contains(@class,'_show')]" strategy="xpath"/> + <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator="#messages .messages" strategy="css selector"/> </page> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable.xml index 65335ced8ed4c24f2bee701f1be039274585fd55..462f55af64fb5852a1c5f4501cb225e88a879030 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable.xml @@ -251,5 +251,32 @@ <field name="base_currency_code" xsi:type="string">0</field> <field name="order_currency_code" xsi:type="string">USD</field> </dataset> + + <dataset name="custom_store_order"> + <field name="entity_id" xsi:type="array"> + <item name="products" xsi:type="string">catalogProductSimple::default</item> + </field> + <field name="customer_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="billing_address_id" xsi:type="array"> + <item name="dataset" xsi:type="string">US_address</item> + </field> + <field name="store_id" xsi:type="array"> + <item name="dataset" xsi:type="string">custom_store</item> + </field> + <field name="shipping_method" xsi:type="string">flatrate_flatrate</field> + <field name="payment_auth_expiration" xsi:type="array"> + <item name="method" xsi:type="string">checkmo</item> + </field> + <field name="payment_authorization_amount" xsi:type="array"> + <item name="method" xsi:type="string">free</item> + </field> + <field name="base_currency_code" xsi:type="string">0</field> + <field name="order_currency_code" xsi:type="string">USD</field> + <field name="price" xsi:type="array"> + <item name="dataset" xsi:type="string">full_flow</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable/Price.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable/Price.xml index 1b9a8b1465457d9d46d04f34774f42425450a7d9..64e2f8808172492ca82dc8cb545f33a0554a3fc5 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable/Price.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable/Price.xml @@ -57,5 +57,13 @@ <item name="grand_invoice_total" xsi:type="string">0</item> </field> </dataset> + + <dataset name="full_flow"> + <field name="0" xsi:type="array"> + <item name="grand_creditmemo_total" xsi:type="string">565</item> + <item name="grand_invoice_total" xsi:type="string">565</item> + <item name="grand_order_total" xsi:type="string">565</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.php index 71575ffb16b858cd43353c02d82f2d496eac4a51..4c56bc61d291105ffd58db9efd0a0624c89e7e54 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.php @@ -48,6 +48,13 @@ class CancelCreatedOrderTest extends Injectable */ protected $salesOrderView; + /** + * Configuration setting. + * + * @var string + */ + private $configData; + /** * Inject pages. * @@ -72,6 +79,7 @@ class CancelCreatedOrderTest extends Injectable public function test(OrderInjectable $order, TestStepFactory $stepFactory, $configData) { // Preconditions + $this->configData = $configData; $stepFactory->create( \Magento\Config\Test\TestStep\SetupConfigurationStep::class, ['configData' => $configData] @@ -87,4 +95,17 @@ class CancelCreatedOrderTest extends Injectable 'customer' => $order->getDataFieldConfig('customer_id')['source']->getCustomer(), ]; } + + /** + * Reset config settings to default. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml index e839681907c6d291b0855f138fa7d52c9b1dd4fa..46dc7ecf646aaafa4f8daff73b6567aeb5b1a464 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml @@ -7,14 +7,14 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\CancelCreatedOrderTest" summary="Cancel Created Order for Offline Payment Methods" ticketId="MAGETWO-28191"> - <variation name="CancelCreatedOrderTestVariationWithCheckMoneyOrderPaymentMethod" summary="Cancel order with check/money order payment method and check status on storefront"> - <data name="tag" xsi:type="string">to_maintain:yes</data> + <variation name="CancelCreatedOrderTestVariationWithCheckMoneyOrderPaymentMethod" summary="Cancel order with check/money order payment method and check status on storefront" ticketId="MAGETWO-13647,MAGETWO-43318"> <data name="order/dataset" xsi:type="string">default</data> - <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default,catalogProductSimple::default</data> + <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::low_stock_product,catalogProductVirtual::virtual_low_stock,bundleProduct::bundle_low_stock,configurableProduct::configurable_low_stock</data> <data name="status" xsi:type="string">Canceled</data> <data name="configData" xsi:type="string">checkmo</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGrid" /> + <constraint name="Magento\Sales\Test\Constraint\AssertProductsQtyAfterOrderCancel" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> </variation> <variation name="CancelCreatedOrderTestVariationWithZeroSubtotalCheckout" summary="Cancel order with zero subtotal checkout payment method and check status on storefront"> @@ -53,5 +53,12 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> </variation> + <variation name="CancelCreatedOrderTestVariationProductQtyWithoutStockDecrease" summary="Check product qty after placing an order with Decrease Stock When Order is Placed = No" ticketId="MAGETWO-43321"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::low_stock_product</data> + <data name="configData" xsi:type="string">decrease_stock_after_order_no</data> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> + <constraint name="Magento\ConfigurableProduct\Test\Constraint\AssertConfigurableProductsQtyAfterReorder" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.php index 92b341bef2675fe635f804da2d9a17c5eea54c49..5a0fcf0cc92528318b96e07309c9d602758745ce 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.php @@ -62,7 +62,7 @@ class CreateCreditMemoEntityTest extends Injectable * @param FixtureFactory $fixtureFactory * @param OrderInjectable $order * @param array $data - * @param string $configData + * @param string|null $configData [optional] * @return array */ public function test( @@ -70,7 +70,7 @@ class CreateCreditMemoEntityTest extends Injectable FixtureFactory $fixtureFactory, OrderInjectable $order, array $data, - $configData + $configData = null ) { // Preconditions $this->fixtureFactory = $fixtureFactory; diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml index 2fb8f91d6d99794a426a91663f7a8ccef531a916..80208482bafd45f07eed259f0160d1e168fdecc2 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml @@ -27,6 +27,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGrid" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> + <constraint name="Magento\Sales\Test\Constraint\AssertProductQtyDecreased" /> </variation> <variation name="CreateOrderBackendTestVariation2"> <data name="description" xsi:type="string">Create order with virtual product for registered UK customer using Check/Money Order payment method</data> @@ -148,5 +149,21 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerForm" /> </variation> + <variation name="CreateOrderBackendTestVariation18" summary="Create order with condition available product qty = ordered product qty" ticketId="MAGETWO-12798"> + <data name="products/0" xsi:type="string">catalogProductSimple::product_with_qty_25</data> + <data name="customer/dataset" xsi:type="string">default</data> + <data name="billingAddress/dataset" xsi:type="string">US_address_1_without_email</data> + <data name="saveAddress" xsi:type="string">No</data> + <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="shipping/shipping_method" xsi:type="string">Fixed</data> + <data name="prices" xsi:type="array"> + <item name="grandTotal" xsi:type="string">375.00</item> + </data> + <data name="payment/method" xsi:type="string">cashondelivery</data> + <data name="configData" xsi:type="string">cashondelivery</data> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderSuccessCreateMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductsOutOfStock" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateShipmentStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateShipmentStep.php index a6378dc44d6503334e40f9567a1db4e97fb81d8a..32425c8aea4cc29d9fd2c9630d6d40289598cf29 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateShipmentStep.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateShipmentStep.php @@ -18,6 +18,11 @@ use Magento\Mtf\TestStep\TestStepInterface; */ class CreateShipmentStep implements TestStepInterface { + /** + * Shipment column title. + */ + const COLUMN_NAME = 'Shipment'; + /** * Orders Page. * @@ -111,6 +116,15 @@ class CreateShipmentStep implements TestStepInterface public function getShipmentIds() { $this->salesOrderView->getOrderForm()->openTab('shipments'); - return $this->salesOrderView->getOrderForm()->getTab('shipments')->getGridBlock()->getIds(); + $this->salesOrderView->getOrderForm()->getTab('shipments')->getGridBlock()->resetFilter(); + $shipmentIds = $this->salesOrderView->getOrderForm()->getTab('shipments')->getGridBlock()->getAllIds(); + $incrementIds = []; + foreach ($shipmentIds as $shipmentId) { + $incrementIds[] = $this->salesOrderView->getOrderForm() + ->getTab('shipments') + ->getGridBlock() + ->getColumnValue($shipmentId, self::COLUMN_NAME); + } + return $incrementIds; } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderStep.php index 0a82770fab38bbe574b77b0bb4e08eadbdc01a3f..3967b6571baa6fb2255b4629db9b89ada1e5d04c 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderStep.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/SubmitOrderStep.php @@ -66,16 +66,16 @@ class SubmitOrderStep implements TestStepInterface * @param SalesOrderView $salesOrderView * @param FixtureFactory $fixtureFactory * @param Customer $customer - * @param Address $billingAddress * @param \Magento\Mtf\Fixture\FixtureInterface[] $products + * @param Address|null $billingAddress */ public function __construct( OrderCreateIndex $orderCreateIndex, SalesOrderView $salesOrderView, FixtureFactory $fixtureFactory, Customer $customer, - Address $billingAddress, - array $products + array $products, + Address $billingAddress = null ) { $this->orderCreateIndex = $orderCreateIndex; $this->salesOrderView = $salesOrderView; diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/DeleteSalesRuleEntityTest.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/DeleteSalesRuleEntityTest.php index 7e0204a90d4e177e4eaad066cdeb9b3c6df702e4..a435334e7a76c724027f467d76d68eca34780f2d 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/DeleteSalesRuleEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/DeleteSalesRuleEntityTest.php @@ -9,11 +9,13 @@ namespace Magento\SalesRule\Test\TestCase; use Magento\SalesRule\Test\Fixture\SalesRule; use Magento\SalesRule\Test\Page\Adminhtml\PromoQuoteEdit; use Magento\SalesRule\Test\Page\Adminhtml\PromoQuoteIndex; +use Magento\Catalog\Test\Fixture\CatalogProductSimple; use Magento\Mtf\TestCase\Injectable; /** * Precondition: * 1. Several Cart Price Rules are created. + * 2. Create sales rule from dataset using Handler. * * Steps: * 1. Login to backend. @@ -63,13 +65,18 @@ class DeleteSalesRuleEntityTest extends Injectable * Delete Sales Rule Entity. * * @param SalesRule $salesRule + * @param CatalogProductSimple $productForSalesRule1 * @return void */ - public function testDeleteSalesRule(SalesRule $salesRule) + public function test(SalesRule $salesRule, CatalogProductSimple $productForSalesRule1 = null) { // Preconditions $salesRule->persist(); + if ($productForSalesRule1) { + $productForSalesRule1->persist(); + } + // Steps $this->promoQuoteIndex->open(); $this->promoQuoteIndex->getPromoQuoteGrid()->searchAndOpen(['name' => $salesRule->getName()]); diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/DeleteSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/DeleteSalesRuleEntityTest.xml index 80dafe5b30181ce2b1635d8ddaecf609b88bb891..14c325c309e263d18d5edc3998c2fc7dfd6d2a67 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/DeleteSalesRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/DeleteSalesRuleEntityTest.xml @@ -17,10 +17,13 @@ <constraint name="Magento\SalesRule\Test\Constraint\AssertCartPriceRuleSuccessDeleteMessage" /> <constraint name="Magento\SalesRule\Test\Constraint\AssertCartPriceRuleIsNotPresentedInGrid" /> </variation> - <variation name="DeleteSalesRuleEntityTestVariation3"> + <variation name="DeleteSalesRuleEntityTestVariation3" summary="Assert That Cart Price Rule Condition Is Not Applied" ticketId="MAGETWO-16987"> <data name="salesRule/dataset" xsi:type="string">inactive_sales_rule</data> + <data name="productForSalesRule1/dataset" xsi:type="string">simple_for_salesrule_1</data> + <data name="productQuantity/productForSalesRule1" xsi:type="string">1</data> <constraint name="Magento\SalesRule\Test\Constraint\AssertCartPriceRuleSuccessDeleteMessage" /> <constraint name="Magento\SalesRule\Test\Constraint\AssertCartPriceRuleIsNotPresentedInGrid" /> + <constraint name="Magento\SalesRule\Test\Constraint\AssertCartPriceRuleConditionIsNotApplied" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ShoppingCartWithFreeShippingTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ShoppingCartWithFreeShippingTest.xml index 7391d2281f8094b059cfaf9d26115c371d9498b0..3cfbf45c0ea8b231233608da21e64f9ce4fc2b24 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ShoppingCartWithFreeShippingTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ShoppingCartWithFreeShippingTest.xml @@ -16,7 +16,7 @@ </variation> <variation name="ShoppingCartWithFreeShippingTestVariation2"> <data name="salesRule/dataset" xsi:type="string">rule_with_freeshipping_by_weight</data> - <data name="product/dataset" xsi:type="string">simple_with_weight_10_for_salesrule</data> + <data name="product/dataset" xsi:type="string">simple_with_weight_10</data> <data name="cart/data/subtotal" xsi:type="string">560.00</data> <data name="cart/data/shipping_amount" xsi:type="string">5.00</data> <data name="cart/data/grand_total" xsi:type="string">565.00</data> diff --git a/dev/tests/functional/tests/app/Magento/Search/Test/Constraint/AssertSynonymRestrictedAccess.php b/dev/tests/functional/tests/app/Magento/Search/Test/Constraint/AssertSynonymRestrictedAccess.php new file mode 100644 index 0000000000000000000000000000000000000000..2153f80fec9a50aa2007045be609e2c8e2aa11b0 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Search/Test/Constraint/AssertSynonymRestrictedAccess.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Search\Test\Constraint; + +use Magento\Search\Test\Page\Adminhtml\SynonymGroupIndex; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Backend\Test\Page\Adminhtml\Dashboard; + +/** + * Assert that access to synonym group index page by direct url is restricted. + */ +class AssertSynonymRestrictedAccess extends AbstractConstraint +{ + /** + * Access denied text. + */ + const ACCESS_DENIED_TEXT = 'Access denied'; + + /** + * Assert that access to synonym group index page is restricted. + * + * @param Dashboard $dashboard + * @param SynonymGroupIndex $synonymGroupIndex + * @return void + */ + public function processAssert(Dashboard $dashboard, SynonymGroupIndex $synonymGroupIndex) + { + $synonymGroupIndex->open(); + + \PHPUnit_Framework_Assert::assertContains( + self::ACCESS_DENIED_TEXT, + $dashboard->getErrorBlock()->getContent(), + 'Synonym group index page is available.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return 'Access to synonym group index page by direct url is restricted.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/AdvancedSearchWithAttributeTest.php b/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/AdvancedSearchWithAttributeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..35d1bd033a0b91e9cd12ae5aed655f0de4983a5b --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/AdvancedSearchWithAttributeTest.php @@ -0,0 +1,391 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Search\Test\TestCase; + +use Magento\Indexer\Test\Constraint\AssertUpdateByScheduleSuccessSaveMessage as AssertSuccessSaveMessage; +use Magento\CatalogSearch\Test\Page\AdvancedSearch; +use Magento\Indexer\Test\Page\Adminhtml\IndexManagement; +use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\Util\Command\Cli\Indexer; +use Magento\CatalogSearch\Test\Constraint\AssertSearchAttributeTest; +use Magento\Indexer\Test\Constraint\AssertIndexerStatus; +use Magento\Catalog\Test\Constraint\AssertProductAttributeSaveMessage; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductAttributeIndex; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductNew; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductEdit; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductAttributeNew; +use Magento\CatalogSearch\Test\Page\AdvancedResult; +use Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductResult; +use Magento\Catalog\Test\Constraint\AssertProductSaveMessage; +use Magento\Catalog\Test\Fixture\CatalogProductAttribute; +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Catalog\Test\Fixture\Category; + +/** + * Preconditions: + * 1. Backend -> System -> New Index Management + * 2. Product EAV = Update by Schedule + * Cron is turned off. + * 3. Perform full reindex: "bin/magento indexer:reindex". + * Steps: + * 1. Call assert to check index status (Product EAV indexer: Status = Ready) + * 2. Open Backend -> Stores -> Attributes -> Product + * 3. Open Weight attribute + * 4. Update and save attribute to: + * Use in Advanced Search = Yes + * 5. Call assert to check index status (Product EAV indexer: Status = Required Reindex) + * 6. Assert that weight attribute is available on the Advanced Search + * 7. Run Full reindex from console + * 8. Change Weight attribute and save + * Scope = Website (Advanced Attribute Properties) + * 10. Call assert to check index status (Product EAV indexer: Status = Required Reindex) + * 11. Assert that weight attribute is available on the Advanced Search + * 12. Run Full reindex from console + * 13. Create simple product with default attribute set with weight = 1 + * 14. Create grouped product so that it will include simple product as option + * 15. Create bundle product so that it will include simple product as option + * 16. Create configurable product with one option product for which weight = 2 + * 17. Call assert to check index status (Product EAV indexer: Status = Ready + * 18. Open Advanced Search on frontend + * 19. Enter value to Weight = 1 and click Search button + * 20. Assert that page with 3 products is open: + * Simple + * Bundle + * Grouped + * 21. Update Weight Attribute in Backend + * Use in Advanced Search = No + * 22. Call assert to check index status (Product EAV indexer: Status = Required Reindex) + * 23. Assert that weight attribute is absent the Advanced Search + * 24. Run Full reindex from console + * + * @group Search + * @ZephyrId MAGETWO-25931 + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class AdvancedSearchWithAttributeTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + /* end tags */ + + /** + * Assert that Attribute is present in Advanced Search Page. + * + * @var AssertSearchAttributeTest + */ + private $assertSearchAttributeTest; + + /** + * Products for search + * + * @var array + */ + private $products; + + /** + * Attribute for check in Advanced Search Page. + * + * @var string + */ + private $attributeForSearch; + + /** + * Default weight attribute value. + * + * @var CatalogProductAttribute + */ + private $attributeDisable; + + /** + * Indexers in Index Management Page. + * + * @var array + */ + private $indexers; + + /** + * Advanced Search Page. + * + * @var AdvancedSearch + */ + private $advancedSearch; + + /** + * Index Management Page. + * + * @var IndexManagement + */ + private $indexManagement; + + /** + * Perform bin/magento commands from command line for functional tests executions. + * + * @var Indexer + */ + private $cli; + + /** + * Advanced Result Page. + * + * @var ResultPage + */ + private $resultPage; + + /** + * Catalog Product Index Page. + * + * @var ProductGrid + */ + private $productGrid; + + /** + * Catalog Product New Page. + * + * @var NewProductPage + */ + private $newProductPage; + + /** + * Catalog Product Edit Page. + * + * @var ProductEdit + */ + private $productEdit; + + /** + * Catalog Product Attribute New Page. + * + * @var AttributeNewPage + */ + private $attributeNewPage; + + /** + * Assert Indexer Status. + * + * @var AssertIndexerStatus + */ + private $assertIndexerStatus; + + /** + * Assert Creation Product. + * + * @var AssertProductSaveMessage + */ + private $assertCreateProducts; + + /** + * Assert Creation Product. + * + * @var CatalogProductAttributeIndex + */ + private $productAttributePage; + + /** + * Assert Success Message Indexer Update by Schedule. + * + * @var AssertSuccessSaveMessage + */ + private $assertSuccessSaveMessage; + + /** + * Assert Success Message is Present After Save Attribute. + * + * @var AssertAdvancedSearchResult + */ + private $assertAdvancedSearchResult; + + /** + * Assert Products in Advanced Search Result Page. + * + * @var AssertAttributeStatus + */ + private $assertAttributeStatus; + + /** + * Inject pages. + * + * @param IndexManagement $indexManagement + * @param AdvancedSearch $advancedSearch + * @param AdvancedResult $resultPage + * @param CatalogProductIndex $productGrid + * @param CatalogProductNew $newProductPage + * @param CatalogProductEdit $productEdit + * @param AssertIndexerStatus $assertIndexerStatus + * @param AssertProductSaveMessage $assertCreateProducts + * @param CatalogProductAttributeIndex $productAttributePage + * @param CatalogProductAttributeNew $attributeNewPage + * @param AssertSuccessSaveMessage $assertSuccessSaveMessage + * @param AssertSearchAttributeTest $assertSearchAttributeTest + * @param AssertAdvancedSearchProductResult $assertAdvancedSearchResult + * @param AssertProductAttributeSaveMessage $assertAttributeStatus + * @return void + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __inject( + IndexManagement $indexManagement, + AdvancedSearch $advancedSearch, + AdvancedResult $resultPage, + CatalogProductIndex $productGrid, + CatalogProductNew $newProductPage, + CatalogProductEdit $productEdit, + AssertIndexerStatus $assertIndexerStatus, + AssertProductSaveMessage $assertCreateProducts, + CatalogProductAttributeIndex $productAttributePage, + CatalogProductAttributeNew $attributeNewPage, + AssertSuccessSaveMessage $assertSuccessSaveMessage, + AssertSearchAttributeTest $assertSearchAttributeTest, + AssertAdvancedSearchProductResult $assertAdvancedSearchResult, + AssertProductAttributeSaveMessage $assertAttributeStatus + ) { + $this->indexManagement = $indexManagement; + $this->advancedSearch = $advancedSearch; + $this->resultPage = $resultPage; + $this->productGrid = $productGrid; + $this->newProductPage = $newProductPage; + $this->productEdit = $productEdit; + $this->assertIndexerStatus = $assertIndexerStatus; + $this->assertCreateProducts = $assertCreateProducts; + $this->productAttributePage = $productAttributePage; + $this->attributeNewPage = $attributeNewPage; + $this->assertSuccessSaveMessage = $assertSuccessSaveMessage; + $this->assertSearchAttributeTest = $assertSearchAttributeTest; + $this->assertAdvancedSearchResult = $assertAdvancedSearchResult; + $this->assertAttributeStatus = $assertAttributeStatus; + } + + /** + * Use Advanced Search by Decimal indexable attribute if Edit/Add Attribute. + * + * @param Indexer $cli + * @param Category $category + * @param FixtureFactory $fixtureFactory + * @param CatalogProductSimple $productSearch + * @param CatalogProductAttribute $attributeEnable + * @param CatalogProductAttribute $attributeDisable + * @param CatalogProductAttribute $attributeGlobalStatus + * @param string $attributeForSearch + * @param array $isVisibleInAdvancedSearch + * @param array $productDropDownList + * @param array $products + * @param string|null $indexers + * @return void + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function test( + Indexer $cli, + Category $category, + FixtureFactory $fixtureFactory, + CatalogProductSimple $productSearch, + CatalogProductAttribute $attributeEnable, + CatalogProductAttribute $attributeDisable, + CatalogProductAttribute $attributeGlobalStatus, + $attributeForSearch, + array $isVisibleInAdvancedSearch, + array $productDropDownList, + array $products, + $indexers = null + ) { + $this->cli = $cli; + $this->products = $products; + $this->attributeDisable = $attributeDisable; + $this->attributeForSearch = $attributeForSearch; + $this->indexers = explode(',', $indexers); + + $category->persist(); + + // Indexers Update bu Schedule + $this->indexManagement->open(); + $this->indexManagement->getMainBlock()->updateBySchedule($this->indexers); + //Assert attribute Update by Schedule + $this->assertSuccessSaveMessage->processAssert($this->indexManagement, $this->indexers); + + // Full indexers reindex + $cli->reindex(); + // Assert indexers status + $this->assertIndexerStatus->processAssert($this->indexManagement, $this->indexers); + $this->productAttributePage->open(); + $this->productAttributePage->getGrid()->searchAndOpen(['attribute_code' => $attributeForSearch['name']]); + $this->attributeNewPage->getAttributeForm()->fill($attributeEnable); + $this->attributeNewPage->getPageActions()->save(); + // Assert attribute status + $this->assertAttributeStatus->processAssert($this->productAttributePage); + + // Assert indexers status + $this->assertIndexerStatus->processAssert($this->indexManagement, $this->indexers, false); + + $this->assertSearchAttributeTest->processAssert($this->advancedSearch, $attributeForSearch); + $cli->reindex(); + + // Change attribute 'scope mode' + $this->productAttributePage->open(); + $this->productAttributePage->getGrid()->searchAndOpen(['attribute_code' => $attributeForSearch['name']]); + $this->attributeNewPage->getAttributeForm()->fill($attributeGlobalStatus); + $this->attributeNewPage->getPageActions()->save(); + // Assert attribute status + $this->assertAttributeStatus->processAssert($this->productAttributePage); + + // Assert indexers status + $this->assertIndexerStatus->processAssert($this->indexManagement, $this->indexers, false); + + // Assert advanced attribute is present(or absent) in Advanced Search Page. + $this->assertSearchAttributeTest->processAssert($this->advancedSearch, $attributeForSearch); + $cli->reindex(); + + // Create Products + $allProducts = []; + foreach ($products as $key => $product) { + list($fixtureCode, $dataset) = explode('::', $product); + $this->productGrid->open(); + $this->productGrid->getGridPageActionBlock()->addProduct($productDropDownList[$key]); + $product = $fixtureFactory->createByCode($fixtureCode, ['dataset' => $dataset]); + $this->newProductPage->getProductForm()->fill($product, null, $category); + $this->newProductPage->getFormPageActions()->save($product); + + $this->assertCreateProducts->processAssert($this->productEdit); + $allProducts[] = $product; + } + + $cli->reindex(); + $this->advancedSearch->open(); + $this->advancedSearch->getForm()->fill($productSearch)->submit(); + + // Assert that Advanced Search result page contains only product(s) according to requested from fixture + $this->assertAdvancedSearchResult->processAssert($isVisibleInAdvancedSearch, $allProducts, $this->resultPage); + $this->productAttributePage->open(); + $this->productAttributePage->getGrid()->searchAndOpen(['attribute_code' => $this->attributeForSearch['name']]); + $this->attributeNewPage->getAttributeForm()->fill($this->attributeDisable); + $this->attributeNewPage->getPageActions()->save(); + // Assert attribute status + $this->assertAttributeStatus->processAssert($this->productAttributePage); + + $this->assertIndexerStatus->processAssert($this->indexManagement, $this->indexers, false); + $cli->reindex(); + unset($this->attributeForSearch['isVisible']); + $this->assertSearchAttributeTest->processAssert($this->advancedSearch, $this->attributeForSearch); + } + + /** + * Set attribute default value. + * + * @return void + */ + protected function tearDown() + { + $this->productAttributePage->open(); + $this->productAttributePage->getGrid()->searchAndOpen(['attribute_code' => $this->attributeForSearch['name']]); + $this->attributeNewPage->getAttributeForm()->fill($this->attributeDisable); + $this->attributeNewPage->getPageActions()->save(); + $this->indexManagement->open(); + $this->indexManagement->getMainBlock()->massaction([], 'Update on Save', false, 'Select All'); + $this->cli->reindex(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/AdvancedSearchWithAttributeTest.xml b/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/AdvancedSearchWithAttributeTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..930818fcb5ccd14f7ad22464f29179789af49ffd --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/AdvancedSearchWithAttributeTest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Search\Test\TestCase\AdvancedSearchWithAttributeTest" summary="Use Advanced Search by Decimal indexable attribute if Edit/Add Attribute" ticketId="MAGETWO-25931"> + <variation name="AdvancedSearchWithWeightAttributeTestVariation1"> + <data name="productDropDownList/0" xsi:type="string">configurable</data> + <data name="productDropDownList/1" xsi:type="string">simple</data> + <data name="productDropDownList/2" xsi:type="string">bundle</data> + <data name="productDropDownList/3" xsi:type="string">grouped</data> + <data name="isVisibleInAdvancedSearch/0" xsi:type="string">No</data> + <data name="isVisibleInAdvancedSearch/1" xsi:type="string">Yes</data> + <data name="isVisibleInAdvancedSearch/2" xsi:type="string">Yes</data> + <data name="isVisibleInAdvancedSearch/3" xsi:type="string">Yes</data> + <data name="attributeEnable/data/is_searchable" xsi:type="string">Yes</data> + <data name="attributeEnable/data/is_visible_in_advanced_search" xsi:type="string">Yes</data> + <data name="attributeDisable/data/is_global" xsi:type="string">Global</data> + <data name="attributeDisable/data/is_searchable" xsi:type="string">No</data> + <data name="attributeGlobalStatus/data/is_global" xsi:type="string">Website</data> + <data name="products/0" xsi:type="string">configurableProduct::one_simple_product</data> + <data name="products/1" xsi:type="string">catalogProductSimple::default</data> + <data name="products/2" xsi:type="string">bundleProduct::default_with_one_simple_product</data> + <data name="products/3" xsi:type="string">groupedProduct::withSimpleProduct_without_category</data> + <data name="productSearch/data/additional_attributes/weight_from" xsi:type="string">1</data> + <data name="productSearch/data/additional_attributes/weight_to" xsi:type="string">1</data> + <data name="indexers" xsi:type="string">Product EAV</data> + <data name="attributeForSearch" xsi:type="array"> + <item name="name" xsi:type="string">Weight</item> + <item name="isVisible" xsi:type="boolean">true</item> + </data> + <data name="product/data/category" xsi:type="string">category_%isolation%</data> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/CustomAclPermissionTest.xml b/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/CustomAclPermissionTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..d09025d4f4ef35b32539bc918c3dda385ded5535 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Search/Test/TestCase/CustomAclPermissionTest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\User\Test\TestCase\CustomAclPermissionTest" summary="Custom ACL Permission" ticketId="MAGETWO-58541"> + <variation name="CustomSynonymAclPermissionTestVariation2" summary="Global synonyms grid access control" ticketId="MAGETWO-47568"> + <data name="user/dataset" xsi:type="string">custom_admin_with_role_without_synonym</data> + <data name="menuItem" xsi:type="string">Marketing > Search Synonyms</data> + <constraint name="Magento\Backend\Test\Constraint\AssertMenuItemNotVisible" /> + <constraint name="Magento\Search\Test\Constraint\AssertSynonymRestrictedAccess" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertCustomerPasswordRequiredClasses.php b/dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertCustomerPasswordRequiredClasses.php new file mode 100644 index 0000000000000000000000000000000000000000..272a98cb298eba58d4d858ec35500924e8b6db27 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/Constraint/AssertCustomerPasswordRequiredClasses.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Security\Test\Constraint; + +use Magento\Customer\Test\Page\CustomerAccountCreate; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Config\Test\Fixture\ConfigData; + +/** + * Assert error message is displayed after customer enter password. + */ +class AssertCustomerPasswordRequiredClasses extends AbstractConstraint +{ + const EXPECTED_MAX_CHARACTERS = 'Minimum of different classes of characters in password is %s.'; + const EXPECTED_MESSAGE = ' Classes of characters: Lower Case, Upper Case, Digits, Special Characters.'; + + /** + * Assert error message is displayed after customer enter password. + * + * @param CustomerAccountCreate $registerPage + * @param ConfigData $config + * @return void + */ + public function processAssert(CustomerAccountCreate $registerPage, ConfigData $config) + { + $errorMessage = $registerPage->getRegisterForm()->getPasswordError(); + $characterClassesNumber = $config + ->getData('section')['customer/password/required_character_classes_number']['value']; + + \PHPUnit_Framework_Assert::assertEquals( + sprintf(self::EXPECTED_MAX_CHARACTERS, $characterClassesNumber) . self::EXPECTED_MESSAGE, + $errorMessage, + 'Wrong expected message is displayed.' + . "\nExpected: " . sprintf(self::EXPECTED_MAX_CHARACTERS, $characterClassesNumber) . self::EXPECTED_MESSAGE + . "\nActual: " . $errorMessage + ); + } + + /** + * Text of success register message is displayed. + * + * @return string + */ + public function toString() + { + return "Customer's password is not correct."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Security/Test/Repository/ConfigData.xml index 8369c43b91a1a87e06c69489985931fb4e97182b..0e65237595ce4b87832b95b0113d7cc0f5cdc0f6 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/Repository/ConfigData.xml @@ -23,6 +23,14 @@ <item name="value" xsi:type="string">10</item> </field> </dataset> + <dataset name="default_required_character_classes_number"> + <field name="customer/password/required_character_classes_number" xsi:type="array"> + <item name="scope" xsi:type="string">customer</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">10</item> + <item name="value" xsi:type="string">3</item> + </field> + </dataset> <dataset name="user_lockout_failures"> <field name="admin/security/lockout_failures" xsi:type="array"> <item name="scope" xsi:type="string">admin</item> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/RegisterCustomerEntityWithDifferentPasswordClassesTest.php b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/RegisterCustomerEntityWithDifferentPasswordClassesTest.php new file mode 100644 index 0000000000000000000000000000000000000000..94b12d6c78b112a27d7d839a47b051c2b83a4b81 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/RegisterCustomerEntityWithDifferentPasswordClassesTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Security\Test\TestCase; + +use Magento\Customer\Test\Fixture\Customer; +use Magento\Customer\Test\Page\CustomerAccountCreate; +use Magento\Cms\Test\Page\CmsIndex; +use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\TestStep\TestStepFactory; +use Magento\Config\Test\Fixture\ConfigData; + +/** + * Test Flow: + * 1. Go to frontend. + * 2. Click Register link. + * 3. Fill registry form. + * 4. Click 'Create account' button. + * 5. Perform assertions. + * + * @ZephyrId MAGETWO-49045 + */ +class RegisterCustomerEntityWithDifferentPasswordClassesTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + const SEVERITY = 'S1'; + /* end tags */ + + /** + * Customer registry page. + * + * @var CustomerAccountCreate + */ + protected $customerAccountCreate; + + /** + * Cms page. + * + * @var CmsIndex $cmsIndex + */ + protected $cmsIndex; + + /** + * Test step factory. + * + * @var TestStepFactory + */ + protected $testStepFactory; + + /** + * Inject data. + * + * @param CustomerAccountCreate $customerAccountCreate + * @param CmsIndex $cmsIndex + * @param TestStepFactory $testStepFactory + * @return void + */ + public function __inject( + CustomerAccountCreate $customerAccountCreate, + CmsIndex $cmsIndex, + TestStepFactory $testStepFactory + ) { + $this->customerAccountCreate = $customerAccountCreate; + $this->cmsIndex = $cmsIndex; + $this->testStepFactory = $testStepFactory; + } + + /** + * Create Customer account on Storefront. + * + * @param Customer $customer + * @param ConfigData $config + * @return void + */ + public function test(Customer $customer, ConfigData $config) + { + // Preconditions + $config->persist(); + // Steps + $this->cmsIndex->open(); + $this->cmsIndex->getLinksBlock()->openLink('Create an Account'); + $this->customerAccountCreate->getRegisterForm()->registerCustomer($customer); + + $characterClassesNumber = $config + ->getData('section')['customer/password/required_character_classes_number']['value']; + + return ['characterClassesNumber' => $characterClassesNumber]; + } + + /** + * Set default settings and logout customer. + * + * @return void + */ + protected function tearDown() + { + //Set default required character classes for the password + $this->objectManager->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => 'default_required_character_classes_number'] + )->run(); + // Logout customer + $this->testStepFactory->create( + \Magento\Customer\Test\TestStep\LogoutCustomerOnFrontendStep::class + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/RegisterCustomerEntityWithDifferentPasswordClassesTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/RegisterCustomerEntityWithDifferentPasswordClassesTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..e11ed99dd8e3ec355ab84f4af8a1f7223af2c101 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/RegisterCustomerEntityWithDifferentPasswordClassesTest.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Security\Test\TestCase\RegisterCustomerEntityWithDifferentPasswordClassesTest" summary="Register customer with different required character classes for the password" ticketId="MAGETWO-49045"> + <variation name="RegisterCustomerEntityWithDifferentPasswordClassesVariation1"> + <data name="config/dataset" xsi:type="string">default_required_character_classes_number</data> + <data name="config/data" xsi:type="array"> + <item name="customer/password/required_character_classes_number" xsi:type="array"> + <item name="value" xsi:type="string">1</item> + </item> + </data> + <data name="customer/dataset" xsi:type="string">register_customer</data> + <data name="customer/data/password" xsi:type="string">12345678</data> + <data name="customer/data/password_confirmation" xsi:type="string">12345678</data> + <constraint name="Magento\Customer\Test\Constraint\AssertCustomerSuccessRegisterMessage" /> + </variation> + <variation name="RegisterCustomerEntityWithDifferentPasswordClassesVariation2"> + <data name="config/dataset" xsi:type="string">default_required_character_classes_number</data> + <data name="config/data" xsi:type="array"> + <item name="customer/password/required_character_classes_number" xsi:type="array"> + <item name="value" xsi:type="string">2</item> + </item> + </data> + <data name="customer/dataset" xsi:type="string">register_customer</data> + <data name="customer/data/password" xsi:type="string">abc12345</data> + <data name="customer/data/password_confirmation" xsi:type="string">abc12345</data> + <constraint name="Magento\Customer\Test\Constraint\AssertCustomerSuccessRegisterMessage" /> + </variation> + <variation name="RegisterCustomerEntityWithDifferentPasswordClassesVariation3"> + <data name="config/dataset" xsi:type="string">default_required_character_classes_number</data> + <data name="config/data" xsi:type="array"> + <item name="customer/password/required_character_classes_number" xsi:type="array"> + <item name="value" xsi:type="string">2</item> + </item> + </data> + <data name="customer/dataset" xsi:type="string">register_customer</data> + <data name="customer/data/password" xsi:type="string">12345678</data> + <data name="customer/data/password_confirmation" xsi:type="string">12345678</data> + <constraint name="Magento\Security\Test\Constraint\AssertCustomerPasswordRequiredClasses" /> + </variation> + <variation name="RegisterCustomerEntityWithDifferentPasswordClassesVariation4"> + <data name="config/dataset" xsi:type="string">default_required_character_classes_number</data> + <data name="config/data" xsi:type="array"> + <item name="customer/password/required_character_classes_number" xsi:type="array"> + <item name="value" xsi:type="string">3</item> + </item> + </data> + <data name="customer/dataset" xsi:type="string">register_customer</data> + <data name="customer/data/password" xsi:type="string">abcXYZ123</data> + <data name="customer/data/password_confirmation" xsi:type="string">abcXYZ123</data> + <constraint name="Magento\Customer\Test\Constraint\AssertCustomerSuccessRegisterMessage" /> + </variation> + <variation name="RegisterCustomerEntityWithDifferentPasswordClassesVariation5"> + <data name="config/dataset" xsi:type="string">default_required_character_classes_number</data> + <data name="config/data" xsi:type="array"> + <item name="customer/password/required_character_classes_number" xsi:type="array"> + <item name="value" xsi:type="string">3</item> + </item> + </data> + <data name="customer/dataset" xsi:type="string">register_customer</data> + <data name="customer/data/password" xsi:type="string">abc12345</data> + <data name="customer/data/password_confirmation" xsi:type="string">abc12345</data> + <constraint name="Magento\Security\Test\Constraint\AssertCustomerPasswordRequiredClasses" /> + </variation> + <variation name="RegisterCustomerEntityWithDifferentPasswordClassesVariation6"> + <data name="config/dataset" xsi:type="string">default_required_character_classes_number</data> + <data name="config/data" xsi:type="array"> + <item name="customer/password/required_character_classes_number" xsi:type="array"> + <item name="value" xsi:type="string">4</item> + </item> + </data> + <data name="customer/dataset" xsi:type="string">register_customer</data> + <data name="customer/data/password" xsi:type="string">abcXYZ12^</data> + <data name="customer/data/password_confirmation" xsi:type="string">abcXYZ12^</data> + <constraint name="Magento\Customer\Test\Constraint\AssertCustomerSuccessRegisterMessage" /> + </variation> + <variation name="RegisterCustomerEntityWithDifferentPasswordClassesVariation7"> + <data name="config/dataset" xsi:type="string">default_required_character_classes_number</data> + <data name="config/data" xsi:type="array"> + <item name="customer/password/required_character_classes_number" xsi:type="array"> + <item name="value" xsi:type="string">4</item> + </item> + </data> + <data name="customer/dataset" xsi:type="string">register_customer</data> + <data name="customer/data/password" xsi:type="string">abcXYZ123</data> + <data name="customer/data/password_confirmation" xsi:type="string">abcXYZ123</data> + <constraint name="Magento\Security\Test\Constraint\AssertCustomerPasswordRequiredClasses" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/Block/Adminhtml/Shipment/Grid.php b/dev/tests/functional/tests/app/Magento/Shipping/Test/Block/Adminhtml/Shipment/Grid.php index d3bd91eddc735b2138b45d4f6b3476cfad34d656..0eccfe7af2929865c41173d6765ffca400e896ff 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/Block/Adminhtml/Shipment/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/Block/Adminhtml/Shipment/Grid.php @@ -33,10 +33,10 @@ class Grid extends GridInterface 'selector' => 'input[name="order_increment_id"]', ], 'total_qty_from' => [ - 'selector' => 'input[name="total_qty[from]"', + 'selector' => 'input[name="total_qty[from]"]', ], 'total_qty_to' => [ - 'selector' => 'input[name="total_qty][to]"', + 'selector' => 'input[name="total_qty[to]"]', ], ]; } diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertShipmentInShipmentsTab.php b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertShipmentInShipmentsTab.php index ad4a73f1036d12688891e862c540507e3abf4678..d01ebe62432f142ef79722a1d15d61de4445cfb2 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertShipmentInShipmentsTab.php +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertShipmentInShipmentsTab.php @@ -44,7 +44,11 @@ class AssertShipmentInShipmentsTab extends AbstractConstraint 'qty_to' => $totalQty[$key], ]; \PHPUnit_Framework_Assert::assertTrue( - $salesOrderView->getOrderForm()->getTab('shipments')->getGridBlock()->isRowVisible($filter), + $salesOrderView + ->getOrderForm() + ->getTab('shipments') + ->getGridBlock() + ->isRowVisible($filter, true, false), 'Shipment is absent on shipments tab.' ); } diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertShipmentNotInShipmentsGrid.php b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertShipmentNotInShipmentsGrid.php new file mode 100644 index 0000000000000000000000000000000000000000..c46130907ac5d8ab63228731c8e0c897f45edd37 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/Constraint/AssertShipmentNotInShipmentsGrid.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Shipping\Test\Constraint; + +use Magento\Sales\Test\Fixture\OrderInjectable; +use Magento\Shipping\Test\Page\Adminhtml\ShipmentIndex; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert shipment with corresponding shipment/order ID is absent in shipments grid. + */ +class AssertShipmentNotInShipmentsGrid extends AbstractConstraint +{ + /** + * Assert shipment with corresponding shipment/order ID is absent in shipments grid. + * + * @param ShipmentIndex $shipmentIndex + * @param OrderInjectable $order + * @param array $ids + * @return void + */ + public function processAssert(ShipmentIndex $shipmentIndex, OrderInjectable $order, array $ids) + { + $shipmentIndex->open(); + $orderId = $order->getId(); + $totalQty = $order->getTotalQtyOrdered(); + foreach ($ids['shipmentIds'] as $key => $shipmentIds) { + $filter = [ + 'id' => $shipmentIds, + 'order_id' => $orderId + ]; + $filterQty = [ + 'total_qty_from' => $totalQty[$key], + 'total_qty_to' => $totalQty[$key], + ]; + $shipmentIndex->getShipmentsGrid()->search($filter + $filterQty); + \PHPUnit_Framework_Assert::assertFalse( + $shipmentIndex->getShipmentsGrid()->isRowVisible($filter, false), + 'Shipment is present in shipment grid on shipment index page.' + ); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Shipment is absent in the shipment grid on shipment index page.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreCodeInUrl.php b/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreCodeInUrl.php new file mode 100644 index 0000000000000000000000000000000000000000..c871866a337967c04a397f5abe665e9d3b329049 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreCodeInUrl.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Test\Constraint; + +use Magento\Cms\Test\Page\CmsIndex; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Client\BrowserInterface; + +/** + * Assert that store code is present in the url. + */ +class AssertStoreCodeInUrl extends AbstractConstraint +{ + /** + * Assert store code in the home page url. + * + * @param CmsIndex $cmsIndex + * @param BrowserInterface $browser + * @param string $storeCode + * @return void + */ + public function processAssert(CmsIndex $cmsIndex, BrowserInterface $browser, $storeCode) + { + $cmsIndex->open(); + $cmsIndex->getLogoBlock()->clickOnLogo(); + \PHPUnit_Framework_Assert::assertEquals( + $_ENV['app_frontend_url'] . $storeCode . '/', + $browser->getUrl(), + sprintf('Store code \'%s\' is not present in the url: %s', $storeCode, $browser->getUrl()) + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Store code is present in the url.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreDisabledErrorSaveMessage.php b/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreDisabledErrorSaveMessage.php new file mode 100644 index 0000000000000000000000000000000000000000..ed74a913b247edf6f4ff19692bd552ce4592987e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreDisabledErrorSaveMessage.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Test\Constraint; + +use Magento\Backend\Test\Page\Adminhtml\EditStore; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that after Store View save disabled error message appears. + */ +class AssertStoreDisabledErrorSaveMessage extends AbstractConstraint +{ + /** + * Disabled error message. + */ + const ERROR_MESSAGE = 'The default store cannot be disabled'; + + /** + * Assert that after Store View save disabled error message appears. + * + * @param EditStore $editStore + * @return void + */ + public function processAssert(EditStore $editStore) + { + \PHPUnit_Framework_Assert::assertEquals( + self::ERROR_MESSAGE, + $editStore->getMessagesBlock()->getErrorMessage(), + 'Wrong error message is displayed.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Store View disabled error create message is present.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreGroupNoDeleteButton.php b/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreGroupNoDeleteButton.php new file mode 100644 index 0000000000000000000000000000000000000000..d77fa2e3134f82faddbd40e30743697554c3e52e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreGroupNoDeleteButton.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Test\Constraint; + +use Magento\Backend\Test\Page\Adminhtml\NewGroupIndex; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that 'Delete' button on StoreGroup view edit page is absent. + */ +class AssertStoreGroupNoDeleteButton extends AbstractConstraint +{ + /** + * Assert that 'Delete' button on StoreGroup view edit page is absent. + * + * @param NewGroupIndex $newGroupIndex + * @return void + */ + public function processAssert(NewGroupIndex $newGroupIndex) + { + \PHPUnit_Framework_Assert::assertFalse( + $newGroupIndex->getFormPageActions()->checkDeleteButton(), + '\'Delete\' button on StoreGroup view edit page is present when it should not.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return '\'Delete\' button on StoreGroup view edit page is absent.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreNoDeleteButton.php b/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreNoDeleteButton.php new file mode 100644 index 0000000000000000000000000000000000000000..fb54b353328e14d93e27151a06e61519ef8f8bcc --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Constraint/AssertStoreNoDeleteButton.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Test\Constraint; + +use Magento\Backend\Test\Page\Adminhtml\StoreNew; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that 'Delete' button on Store view edit page is absent. + */ +class AssertStoreNoDeleteButton extends AbstractConstraint +{ + /** + * Assert that 'Delete' button on Store view edit page is absent. + * + * @param StoreNew $storePage + * @return void + */ + public function processAssert(StoreNew $storePage) + { + \PHPUnit_Framework_Assert::assertFalse( + $storePage->getFormPageActions()->checkDeleteButton(), + '\'Delete\' button on Store view edit page is present when it should not.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return '\'Delete\' button on Store view edit page is absent.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/Store/GroupId.php b/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/Store/GroupId.php index a5fa6d4a83cbe63cbcf6d6b15e5935787d5d2694..34b93054fd282b3c7ccab5767ae8fc215d388b17 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/Store/GroupId.php +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/Store/GroupId.php @@ -31,6 +31,13 @@ class GroupId extends DataSource public function __construct(FixtureFactory $fixtureFactory, array $params, array $data = []) { $this->params = $params; + + if (isset($data['storeGroup']) && $data['storeGroup'] instanceof StoreGroup) { + $this->storeGroup = $data['storeGroup']; + $this->data = $data['storeGroup']->getWebsiteId() . "/" . $data['storeGroup']->getName(); + return; + } + if (isset($data['dataset'])) { $storeGroup = $fixtureFactory->createByCode('storeGroup', ['dataset' => $data['dataset']]); /** @var StoreGroup $storeGroup */ @@ -44,11 +51,6 @@ class GroupId extends DataSource $this->data = $this->storeGroup->getWebsiteId() . "/" . $this->storeGroup->getName(); } - if (isset($data['storeGroup']) && $data['storeGroup'] instanceof StoreGroup) { - $this->storeGroup = $data['storeGroup']; - $this->data = $data['storeGroup']->getWebsiteId() . "/" . $data['storeGroup']->getName(); - } - if (isset($data['value'])) { $this->data = $data['value']; } diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/StoreGroup/CategoryId.php b/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/StoreGroup/CategoryId.php index ccdc7b204894eb00a6afd255acbd3c56fc9fd0f6..32983d35c46e2ce318e64a3fcc7cd134bdfb8278 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/StoreGroup/CategoryId.php +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/StoreGroup/CategoryId.php @@ -31,7 +31,10 @@ class CategoryId extends DataSource public function __construct(FixtureFactory $fixtureFactory, array $params, array $data = []) { $this->params = $params; - if (isset($data['dataset'])) { + if (isset($data['fixture']) || isset($data['category'])) { + $this->category = isset($data['fixture']) ? $data['fixture'] : $data['category']; + $this->data = $this->category->getName(); + } elseif (isset($data['dataset'])) { $category = $fixtureFactory->createByCode('category', ['dataset' => $data['dataset']]); /** @var Category $category */ if (!$category->getId()) { diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/StoreGroup/WebsiteId.php b/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/StoreGroup/WebsiteId.php index 985b4a91c35f8412eb614b1903a5e56ab8a410e1..1a25d3d6b6f83dd100d4e1699823e47b883432a5 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/StoreGroup/WebsiteId.php +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Fixture/StoreGroup/WebsiteId.php @@ -31,7 +31,10 @@ class WebsiteId extends DataSource public function __construct(FixtureFactory $fixtureFactory, array $params, array $data = []) { $this->params = $params; - if (isset($data['dataset'])) { + if (isset($data['fixture'])) { + $this->website = $data['fixture']; + $this->data = $this->website->getName(); + } elseif (isset($data['dataset'])) { $website = $fixtureFactory->createByCode('website', ['dataset' => $data['dataset']]); /** @var Website $website */ if (!$website->getWebsiteId()) { @@ -39,9 +42,6 @@ class WebsiteId extends DataSource } $this->website = $website; $this->data = $website->getName(); - } elseif (isset($data['fixture'])) { - $this->website = $data['fixture']; - $this->data = $this->website->getName(); } } diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/ConfigData.xml new file mode 100644 index 0000000000000000000000000000000000000000..fbaa409165f14632e81f27009612d3a5a8c82470 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/ConfigData.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> + <repository class="Magento\Config\Test\Repository\ConfigData"> + <dataset name="add_store_code_to_urls"> + <field name="web/url/use_store" xsi:type="array"> + <item name="scope" xsi:type="string">admin</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">Yes</item> + <item name="value" xsi:type="number">1</item> + </field> + </dataset> + + <dataset name="add_store_code_to_urls_rollback"> + <field name="web/url/use_store" xsi:type="array"> + <item name="scope" xsi:type="string">admin</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">No</item> + <item name="value" xsi:type="number">0</item> + </field> + </dataset> + </repository> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Store.xml b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Store.xml index 37faba3d75524304919c65cf391ee4211ef9fdae..5a7691042fde8dda5b2bd998dc6cef2a75bbe059 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Store.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Store.xml @@ -44,6 +44,7 @@ <field name="code" xsi:type="string">de%isolation%</field> <field name="is_active" xsi:type="string">Enabled</field> </dataset> + <dataset name="custom_store"> <field name="group_id" xsi:type="array"> <item name="dataset" xsi:type="string">custom_new_group</item> @@ -61,5 +62,23 @@ <field name="code" xsi:type="string">code_%isolation%</field> <field name="is_active" xsi:type="string">Enabled</field> </dataset> + + <dataset name="store_new_1"> + <field name="group_id" xsi:type="array"> + <item name="dataset" xsi:type="string">store_group_new_1</item> + </field> + <field name="name" xsi:type="string">New_Store_%isolation%</field> + <field name="code" xsi:type="string">store_group_new_1_%isolation%</field> + <field name="is_active" xsi:type="string">Enabled</field> + </dataset> + + <dataset name="store_new_2"> + <field name="group_id" xsi:type="array"> + <item name="dataset" xsi:type="string">store_group_new_2</item> + </field> + <field name="name" xsi:type="string">New_Store_2_%isolation%</field> + <field name="code" xsi:type="string">store_group_new_2_%isolation%</field> + <field name="is_active" xsi:type="string">Enabled</field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/StoreGroup.xml b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/StoreGroup.xml index f07c79a8967d56b5b241feb049cd402f6d90ddef..b3fa6a52b2f59ee1c4421395b1cc900fd3ac4708 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/StoreGroup.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/StoreGroup.xml @@ -37,5 +37,25 @@ <item name="dataset" xsi:type="string">default_category</item> </field> </dataset> + + <dataset name="store_group_new_1"> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">website_new_1</item> + </field> + <field name="name" xsi:type="string">New_Store_Group_%isolation%</field> + <field name="root_category_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default_category</item> + </field> + </dataset> + + <dataset name="store_group_new_2"> + <field name="website_id" xsi:type="array"> + <item name="dataset" xsi:type="string">website_new_2</item> + </field> + <field name="name" xsi:type="string">New_Store_Group_2_%isolation%</field> + <field name="root_category_id" xsi:type="array"> + <item name="dataset" xsi:type="string">root_category</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Website.xml b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Website.xml index 5c2ec69efca98b581708ffd2eb4441d8275ee073..0a7552e44b9950127e185bfd810c3264bc36b511 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Website.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Website.xml @@ -29,5 +29,15 @@ <field name="name" xsi:type="string">Web_Site_%isolation%</field> <field name="code" xsi:type="string">code_%isolation%</field> </dataset> + + <dataset name="website_new_1"> + <field name="name" xsi:type="string">New_Website_%isolation%</field> + <field name="code" xsi:type="string">new_%isolation%</field> + </dataset> + + <dataset name="website_new_2"> + <field name="name" xsi:type="string">New_Website_2_%isolation%</field> + <field name="code" xsi:type="string">new2_%isolation%</field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.php b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.php new file mode 100644 index 0000000000000000000000000000000000000000..079cf1384ef812c1bb8b5a1f7ce39ed6158774d4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Test\TestCase; + +use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\TestStep\TestStepFactory; + +/** + * Steps: + * 1. Enable 'Add Store Code to Urls'. + * 2. Log out from Admin. + * 3. Perform all assertions. + * + * @ZephyrId MAGETWO-42720 + */ +class AccessAdminWithStoreCodeInUrlTest extends Injectable +{ + /* tags */ + const MVP = 'no'; + /* end tags */ + + /** + * Configuration setting. + * + * @var string + */ + private $configData; + + /** + * Step factory. + * + * @var TestStepFactory + */ + private $stepFactory; + + /** + * Inject step factory. + * + * @param TestStepFactory $stepFactory + * @return void + */ + public function __inject(TestStepFactory $stepFactory) + { + $this->stepFactory = $stepFactory; + } + + /** + * Set config and log out from Admin. + * + * @param string $configData + * @return void + */ + public function test($configData) + { + $this->configData = $configData; + $this->stepFactory->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData] + )->run(); + $this->stepFactory->create( + \Magento\User\Test\TestStep\LogoutUserOnBackendStep::class, + ['configData' => $this->configData] + )->run(); + } + + /** + * Reset config settings to default. + * + * @return void + */ + public function tearDown() + { + $this->stepFactory->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $this->configData, 'rollback' => true] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..432d0e24d2fa2726af2c6990715f7a7207a9b109 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Store\Test\TestCase\AccessAdminWithStoreCodeInUrlTest" summary="Access admin when store code added to URL" ticketId="MAGETWO-42720"> + <variation name="AccessAdminWithStoreCodeInUrlTestVariation1" summary="Access admin when store code added to URL" ticketId="MAGETWO-42720"> + <data name="configData" xsi:type="string">add_store_code_to_urls</data> + <data name="user/dataset" xsi:type="string">default</data> + <data name="storeCode" xsi:type="string">default</data> + <constraint name="Magento\User\Test\Constraint\AssertUserSuccessLogin" /> + <constraint name="Magento\Store\Test\Constraint\AssertStoreCodeInUrl" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateStoreEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateStoreEntityTest.xml index 26d9aa9beba0acc098558d8fd1f6aacb9083cb5d..fb43cc3f08c06f3bf1ab9a8a94c7739ffc9f301f 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateStoreEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateStoreEntityTest.xml @@ -50,5 +50,18 @@ <constraint name="Magento\Store\Test\Constraint\AssertStoreSuccessSaveMessage" /> <constraint name="Magento\Store\Test\Constraint\AssertStoreInGrid" /> </variation> + <variation name="CreateStoreEntityTestVariation5" summary="Check the absence of delete button" ticketId="MAGETWO-17475"> + <data name="store/dataset" xsi:type="string">custom_store</data> + <constraint name="Magento\Store\Test\Constraint\AssertStoreSuccessSaveMessage" /> + <constraint name="Magento\Store\Test\Constraint\AssertStoreForm" /> + <constraint name="Magento\Store\Test\Constraint\AssertStoreNoDeleteButton" /> + </variation> + <variation name="CreateDisabledStoreView" summary="Create disabled store view" ticketId="MAGETWO-17475"> + <data name="store/data/group_id/dataset" xsi:type="string">custom_new_group</data> + <data name="store/data/name" xsi:type="string">store_name_%isolation%</data> + <data name="store/data/code" xsi:type="string">storecode_%isolation%</data> + <data name="store/data/is_active" xsi:type="string">Disabled</data> + <constraint name="Magento\Store\Test\Constraint\AssertStoreDisabledErrorSaveMessage" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateStoreGroupEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateStoreGroupEntityTest.xml index 39adbb9c418403469cfd5f82697619c5d89218be..b083214841cca0253f7448572d9214ca2adeb305 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateStoreGroupEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateStoreGroupEntityTest.xml @@ -26,5 +26,11 @@ <constraint name="Magento\Store\Test\Constraint\AssertStoreGroupForm" /> <constraint name="Magento\Store\Test\Constraint\AssertStoreGroupOnStoreViewForm" /> </variation> + <variation name="CreateStoreGroupEntityTestVariation3" summary="Check the absence of delete button" ticketId="MAGETWO-17475"> + <data name="storeGroup/dataset" xsi:type="string">custom_new_group</data> + <constraint name="Magento\Store\Test\Constraint\AssertStoreGroupSuccessSaveMessage" /> + <constraint name="Magento\Store\Test\Constraint\AssertStoreGroupForm" /> + <constraint name="Magento\Store\Test\Constraint\AssertStoreGroupNoDeleteButton" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/Constraint/AssertSwatchConfigurableProductPage.php b/dev/tests/functional/tests/app/Magento/Swatches/Test/Constraint/AssertSwatchConfigurableProductPage.php index 460a13ce49d704f30f513af5c8f3189c45f3ffc4..8f910174273420944afb5587d85f1a9ffa16afd4 100644 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/Constraint/AssertSwatchConfigurableProductPage.php +++ b/dev/tests/functional/tests/app/Magento/Swatches/Test/Constraint/AssertSwatchConfigurableProductPage.php @@ -26,12 +26,7 @@ class AssertSwatchConfigurableProductPage extends AssertProductPage ) { $this->product = $product; $this->productView = $catalogProductView->getProductViewWithSwatchesBlock(); - $this->objectManager->create( - \Magento\Swatches\Test\TestStep\AddProductToCartFromCatalogCategoryPageStep::class, - [ - 'product' => $product - ] - )->run(); + // we need this line for waiti until page will be fully loaded $this->productView->getSelectedSwatchOptions($this->product); $errors = $this->verify(); diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShopingCartTest.php b/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShopingCartTest.php deleted file mode 100644 index 60cc61f7f0f79319336d57d0d81289de6fe31cab..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShopingCartTest.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Swatches\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * Preconditions: - * 1. Configure text swatch attribute. - * 2. Create configurable product with this attribute - * 3. Open it on catalog page - * 4. Click on 'Add to Cart' button - * - * Steps: - * 1. Go to Frontend. - * 2. Open category page with created product - * 3. Click on 'Add to Cart' button - * 4. Perform asserts - * - * @group Configurable_Product - * @ZephyrId MAGETWO-59958 - */ -class AddConfigurableProductWithSwatchToShopingCartTest extends Scenario -{ - /** - * Runs add configurable product with swatches attributes test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShopingCartTest.xml b/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShopingCartTest.xml deleted file mode 100644 index adf8d71395ccb9c4b714ad58887ec741391871a2..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShopingCartTest.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Swatches\Test\TestCase\AddConfigurableProductWithSwatchToShopingCartTest" summary="Create text swatch attribute" ticketId="MAGETWO-47017"> - <variation name="AddConfigurableProductWithSwatchToShopingCartTest1"> - <data name="attributeTypeAction" xsi:type="string">addOptions</data> - <data name="product" xsi:type="string">configurableProductSwatch::product_with_text_swatch</data> - <constraint name="Magento\Checkout\Test\Constraint\AssertCartItemsOptions" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShoppingCartTest.php b/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShoppingCartTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6f05c1a7cfa597042c68aa1fbfa1c010242473f3 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShoppingCartTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Swatches\Test\TestCase; + +use Magento\Mtf\TestCase\Injectable; +use Magento\ConfigurableProduct\Test\Fixture\ConfigurableProduct; +use Magento\Mtf\TestStep\TestStepFactory; +use Magento\Catalog\Test\Page\Category\CatalogCategoryView; + +/** + * Preconditions: + * 1. Configure text swatch attribute. + * 2. Create configurable product with this attribute + * 3. Open it on catalog page + * 4. Click on 'Add to Cart' button + * + * Steps: + * 1. Go to Frontend. + * 2. Open category page with created product + * 3. Click on 'Add to Cart' button + * 4. Perform asserts + * + * @group Configurable_Product + * @ZephyrId MAGETWO-59958, MAGETWO-59979 + */ +class AddConfigurableProductWithSwatchToShoppingCartTest extends Injectable +{ + /** + * Factory for Test Steps. + * + * @var TestStepFactory + */ + private $testStepFactory; + + /** + * Page of catalog category view. + * + * @var CatalogCategoryView + */ + private $categoryView; + + /** + * Injection data. + * + * @param TestStepFactory $testStepFactory + * @param CatalogCategoryView $categoryView + * @return void + */ + public function __inject( + TestStepFactory $testStepFactory, + CatalogCategoryView $categoryView + ) { + $this->testStepFactory = $testStepFactory; + $this->categoryView = $categoryView; + } + + /** + * Runs add configurable product with swatches attributes test. + * + * @param ConfigurableProduct $product + * @param bool $addToCart + * @return array + */ + public function test(ConfigurableProduct $product, $addToCart) + { + $product->persist(); + $cart = $this->testStepFactory->create( + \Magento\Swatches\Test\TestStep\AddProductToCartFromCatalogCategoryPageStep::class, + [ + 'product' => $product + ] + )->run()['cart']; + if ($addToCart) { + $this->categoryView->getMessagesBlock()->waitSuccessMessage(); + } + + return ['cart' => $cart]; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShoppingCartTest.xml b/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShoppingCartTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..a786094693960aba7fe1ae4a40b9b144b05f050d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/AddConfigurableProductWithSwatchToShoppingCartTest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Swatches\Test\TestCase\AddConfigurableProductWithSwatchToShoppingCartTest" summary="Create text swatch attribute" ticketId="MAGETWO-47017"> + <variation name="AddConfigurableProductWithSwatchToShoppingCartTest1" summary="Add configurable product to cart with swatch options only" ticketId="MAGETWO-59958"> + <data name="attributeTypeAction" xsi:type="string">addOptions</data> + <data name="product/dataset" xsi:type="string">product_with_text_swatch</data> + <data name="addToCart" xsi:type="boolean">true</data> + <constraint name="Magento\Checkout\Test\Constraint\AssertCartItemsOptions" /> + </variation> + <variation name="AddConfigurableProductWithSwatchToShoppingCartTest2" summary="Add configurable product to cart with mixed option types" ticketId="MAGETWO-59979"> + <data name="attributeTypeAction" xsi:type="string">addOptions</data> + <data name="product/dataset" xsi:type="string">product_with_text_swatch_and_size</data> + <data name="addToCart" xsi:type="boolean">false</data> + <constraint name="Magento\Swatches\Test\Constraint\AssertSwatchConfigurableProductPage" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/TryToAddConfigurableProductWithSwatchToShopingCartTest.php b/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/TryToAddConfigurableProductWithSwatchToShopingCartTest.php deleted file mode 100644 index ac3016ade3f016b82a856677715db6c93736c1ef..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/TryToAddConfigurableProductWithSwatchToShopingCartTest.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Swatches\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * Preconditions: - * 1. Configure text swatch attribute. - * 2. Create configurable product with this attribute - * 3. Open it on catalog page - * 4. Click on 'Add to Cart' button - * - * Steps: - * 1. Go to Frontend. - * 2. Open category page with created product - * 3. Click on 'Add to Cart' button - * 4. Perform asserts - * - * @group Configurable_Product - * @ZephyrId TODO: MAGETWO-59979 - */ -class TryToAddConfigurableProductWithSwatchToShopingCartTest extends Scenario -{ - /** - * Runs add configurable product with swatches attributes test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/TryToAddConfigurableProductWithSwatchToShopingCartTest.xml b/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/TryToAddConfigurableProductWithSwatchToShopingCartTest.xml deleted file mode 100644 index 4cb7a3e01676f09c6c3e6b9ffcd3d85fb9abbe4a..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/TestCase/TryToAddConfigurableProductWithSwatchToShopingCartTest.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Swatches\Test\TestCase\TryToAddConfigurableProductWithSwatchToShopingCartTest" summary="Create text swatch attribute" ticketId="MAGETWO-47017"> - <variation name="TryToAddConfigurableProductWithSwatchToShopingCartTest1"> - <data name="attributeTypeAction" xsi:type="string">addOptions</data> - <data name="product" xsi:type="string">configurableProductSwatch::product_with_text_swatch_and_size</data> - <constraint name="Magento\Swatches\Test\Constraint\AssertSwatchConfigurableProductPage" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Swatches/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Swatches/Test/etc/testcase.xml deleted file mode 100644 index f2e30c42a7a859e52ad129eda213aa8b1e597aad..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/Swatches/Test/etc/testcase.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/TestCase/etc/testcase.xsd"> - <scenario name="AddConfigurableProductWithSwatchToShopingCartTest" firstStep="createProduct"> - <step name="createProduct" module="Magento_Catalog" next="addProductToCartFromCatalogCategoryPage" /> - <step name="addProductToCartFromCatalogCategoryPage" module="Magento_Swatches" /> - </scenario> - <scenario name="TryToAddConfigurableProductWithSwatchToShopingCartTest" firstStep="createProduct"> - <step name="createProduct" module="Magento_Catalog" /> - </scenario> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/ConfigData.xml index 33ecac40bfe3aaba2f5953b3e2c2cdbdbc4a03e4..5a51be9a9879a66c05b4b1e4e40309f24c80de92 100644 --- a/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/ConfigData.xml @@ -164,7 +164,7 @@ <field name="tax/calculation/apply_after_discount" xsi:type="array"> <item name="scope" xsi:type="string">tax</item> <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Before Discount</item> + <item name="label" xsi:type="string">After Discount</item> <item name="value" xsi:type="number">0</item> </field> <field name="tax/calculation/discount_tax" xsi:type="array"> @@ -541,6 +541,15 @@ </field> </dataset> + <dataset name="row_cat_incl_ship_excl_before_disc_on_incl_rollback"> + <field name="tax/calculation/apply_after_discount" xsi:type="array"> + <item name="scope" xsi:type="string">tax</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">After Discount</item> + <item name="value" xsi:type="number">1</item> + </field> + </dataset> + <dataset name="unit_cat_incl_ship_incl_before_disc_on_incl"> <field name="tax/calculation/algorithm" xsi:type="array"> <item name="scope" xsi:type="string">tax</item> @@ -580,6 +589,15 @@ </field> </dataset> + <dataset name="unit_cat_incl_ship_incl_before_disc_on_incl_rollback"> + <field name="tax/calculation/apply_after_discount" xsi:type="array"> + <item name="scope" xsi:type="string">tax</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">After Discount</item> + <item name="value" xsi:type="number">1</item> + </field> + </dataset> + <dataset name="total_cat_excl_ship_incl_before_disc_on_incl"> <field name="tax/calculation/algorithm" xsi:type="array"> <item name="scope" xsi:type="string">tax</item> @@ -619,6 +637,15 @@ </field> </dataset> + <dataset name="total_cat_excl_ship_incl_before_disc_on_incl_rollback"> + <field name="tax/calculation/apply_after_discount" xsi:type="array"> + <item name="scope" xsi:type="string">tax</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">After Discount</item> + <item name="value" xsi:type="number">1</item> + </field> + </dataset> + <dataset name="unit_cat_excl_ship_excl_after_disc_on_excl"> <field name="tax/calculation/algorithm" xsi:type="array"> <item name="scope" xsi:type="string">tax</item> @@ -697,6 +724,15 @@ </field> </dataset> + <dataset name="total_cat_incl_ship_excl_before_disc_on_excl_rollback"> + <field name="tax/calculation/apply_after_discount" xsi:type="array"> + <item name="scope" xsi:type="string">tax</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="label" xsi:type="string">After Discount</item> + <item name="value" xsi:type="number">1</item> + </field> + </dataset> + <dataset name="total_cat_excl_ship_incl_after_disc_on_incl"> <field name="tax/calculation/algorithm" xsi:type="array"> <item name="scope" xsi:type="string">tax</item> diff --git a/dev/tests/functional/tests/app/Magento/Theme/Test/Block/Html/Logo.php b/dev/tests/functional/tests/app/Magento/Theme/Test/Block/Html/Logo.php new file mode 100644 index 0000000000000000000000000000000000000000..d05882dc9405387a082d3dff6ee81cfe26c38118 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Theme/Test/Block/Html/Logo.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Theme\Test\Block\Html; + +use Magento\Mtf\Block\Block; + +/** + * Logo block. + */ +class Logo extends Block +{ + /** + * Click on logo element. + * + * @return void + */ + public function clickOnLogo() + { + $this->_rootElement->click(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php index b913a1c6804e63b136fdf1b497071dd173f35088..be7a650d77e1c3ebe56409b5e38f9db8ef8e97ab 100644 --- a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php +++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php @@ -95,16 +95,27 @@ class DataGrid extends Grid protected $columnHeader = './/*[@data-role="grid-wrapper"]//th/span[.="%s"]'; /** + * Grid row xpath locator. + * * @var string */ - protected $rowById = "//tr[//input[@data-action='select-row' and @value='%s']]"; + protected $rowById = ".//tr[td//input[@data-action='select-row' and @value='%s']]"; - // @codingStandardsIgnoreStart /** + * Column header number. + * * @var string */ - private $cellByHeader = "//td[count(//th[span[.='%s']][not(ancestor::*[@class='sticky-header'])]/preceding-sibling::th)+1]"; + protected $columnNumber = ".//th[span[.='%s']][not(ancestor::*[@class='sticky-header'])]/preceding-sibling::th"; + /** + * Cell number. + * + * @var string + */ + protected $cellByHeader = "//td[%s+1]"; + + // @codingStandardsIgnoreStart /** * Admin data grid header selector. * @@ -437,7 +448,11 @@ class DataGrid extends Grid { $this->waitLoader(); $this->getTemplateBlock()->waitForElementNotVisible($this->loader); - $selector = sprintf($this->rowById, $id) . sprintf($this->cellByHeader, $headerLabel); + $columnNumber = count( + $this->_rootElement->getElements(sprintf($this->columnNumber, $headerLabel), Locator::SELECTOR_XPATH) + ); + $selector = sprintf($this->rowById, $id) . sprintf($this->cellByHeader, $columnNumber); + return $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->getText(); } diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Messages.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Messages.php new file mode 100644 index 0000000000000000000000000000000000000000..c29e56d421bc340690989bae377148453e662619 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Messages.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Ui\Test\Block; + +use Magento\Mtf\Block\Block; +use Magento\Mtf\Client\Locator; + +/** + * Global messages block. + */ +class Messages extends Block +{ + /** + * Success message selector. + * + * @var string + */ + protected $successMessage = '[data-ui-id$=message-success]'; + + /** + * Last success message selector. + * + * @var string + */ + protected $lastSuccessMessage = '[data-ui-id$=message-success]:last-child'; + + /** + * Error message selector. + * + * @var string + */ + protected $errorMessage = '[data-ui-id$=message-error]'; + + /** + * Notice message selector. + * + * @var string + */ + protected $noticeMessage = '[data-ui-id$=message-notice]'; + + /** + * Warning message selector. + * + * @var string + */ + protected $warningMessage = '[data-ui-id$=message-warning]'; + + /** + * Wait for success message. + * + * @return bool + */ + public function waitSuccessMessage() + { + return $this->waitForElementVisible($this->successMessage, Locator::SELECTOR_CSS); + } + + /** + * Get all success messages which are present on the page. + * + * @return array + */ + public function getSuccessMessages() + { + $this->waitForElementVisible($this->successMessage); + $elements = $this->_rootElement->getElements($this->successMessage); + + $messages = []; + foreach ($elements as $element) { + $messages[] = $element->getText(); + } + + return $messages; + } + + /** + * Get all notice messages which are present on the page. + * + * @return array + */ + public function getNoticeMessages() + { + $this->waitForElementVisible($this->noticeMessage); + $elements = $this->_rootElement->getElements($this->noticeMessage); + + $messages = []; + foreach ($elements as $element) { + $messages[] = $element->getText(); + } + + return $messages; + } + + /** + * Get last success message which is present on the page. + * + * @return string + */ + public function getSuccessMessage() + { + $this->waitForElementVisible($this->successMessage); + + return $this->_rootElement->find($this->lastSuccessMessage)->getText(); + } + + /** + * Wait for element is visible in the page. + * + * @param string $selector + * @param string $strategy + * @return bool|null + */ + public function waitForElementVisible($selector, $strategy = Locator::SELECTOR_CSS) + { + $browser = $this->browser; + return $browser->waitUntil( + function () use ($browser, $selector, $strategy) { + $message = $browser->find($selector, $strategy); + return $message->isVisible() ? true : null; + } + ); + } + + /** + * Get all error message which is present on the page. + * + * @return string + */ + public function getErrorMessage() + { + return $this->_rootElement + ->find($this->errorMessage, Locator::SELECTOR_CSS) + ->getText(); + } + + /** + * Get notice message which is present on the page. + * + * @return string + */ + public function getNoticeMessage() + { + $this->waitForElementVisible($this->noticeMessage); + return $this->_rootElement->find($this->noticeMessage)->getText(); + } + + /** + * Get warning message which is present on the page. + * + * @return string + */ + public function getWarningMessage() + { + $this->waitForElementVisible($this->warningMessage); + return $this->_rootElement->find($this->warningMessage)->getText(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Grid.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Grid.php index 54de30e734d902d8bddf9be41059e23fe42b21ad..19ca7c2b520dc4f97b21a5a36dd6b9ea945d1e1a 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Grid.php +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Block/Adminhtml/Catalog/Category/Grid.php @@ -26,5 +26,13 @@ class Grid extends ParentGrid 'target_path' => [ 'selector' => 'input[name="target_path"]', ], + 'store_id' => [ + 'selector' => 'select[name="store_id"]', + 'input' => 'select', + ], + 'redirect_type' => [ + 'selector' => 'select[name="redirect_type"]', + 'input' => 'select', + ], ]; } diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteAfterDeletingCategory.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteAfterDeletingCategory.php new file mode 100644 index 0000000000000000000000000000000000000000..c2611b67febb8d582072c0d5edf67c145832c84c --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteAfterDeletingCategory.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\UrlRewrite\Test\Constraint; + +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\UrlRewrite\Test\Fixture\UrlRewrite; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Catalog\Test\Page\Adminhtml\CatalogCategoryEdit; +use Magento\Catalog\Test\Page\Adminhtml\CatalogCategoryIndex; +use Magento\UrlRewrite\Test\Page\Adminhtml\UrlRewriteIndex; + +/** + * Assert that url rewrites for product and category are deleted after deleting category. + */ +class AssertUrlRewriteAfterDeletingCategory extends AbstractConstraint +{ + /** + * Assert that url rewrites are not present in grid. + * + * @param UrlRewrite $urlRewrite + * @param CatalogProductSimple $product + * @param CatalogCategoryIndex $catalogCategoryIndex + * @param CatalogCategoryEdit $catalogCategoryEdit + * @param UrlRewriteIndex $urlRewriteIndex + * @param AssertUrlRewriteNotInGrid $assertUrlRewrite + * @param AssertUrlRewriteCategoryNotInGrid $assertCategoryUrlRewrite + * @return void + */ + public function processAssert( + UrlRewrite $urlRewrite, + CatalogProductSimple $product, + CatalogCategoryIndex $catalogCategoryIndex, + CatalogCategoryEdit $catalogCategoryEdit, + UrlRewriteIndex $urlRewriteIndex, + AssertUrlRewriteNotInGrid $assertUrlRewrite, + AssertUrlRewriteCategoryNotInGrid $assertCategoryUrlRewrite + ) { + $category = $product->getDataFieldConfig('category_ids')['source']->getCategories()[0]; + $catalogCategoryIndex->open(); + $catalogCategoryIndex->getTreeCategories()->selectCategory($category); + $catalogCategoryEdit->getFormPageActions()->delete(); + $catalogCategoryEdit->getModalBlock()->acceptAlert(); + + $assertCategoryUrlRewrite->processAssert($urlRewriteIndex, $category); + $assertUrlRewrite->processAssert($urlRewriteIndex, $urlRewrite); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'URL rewrites are deleted.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteCategoryInGrid.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteCategoryInGrid.php index 3630bd697b8593a966f4ed93b67794b80b6da185..1e01ea5c6e32225ede8164cf355387a42b793f6a 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteCategoryInGrid.php +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteCategoryInGrid.php @@ -9,34 +9,171 @@ namespace Magento\UrlRewrite\Test\Constraint; use Magento\Catalog\Test\Fixture\Category; use Magento\Mtf\Constraint\AbstractConstraint; use Magento\UrlRewrite\Test\Page\Adminhtml\UrlRewriteIndex; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** - * Class AssertUrlRewriteCategoryInGrid - * Assert that url category in grid + * Assert that url rewrite category in grid. */ class AssertUrlRewriteCategoryInGrid extends AbstractConstraint { + /** + * Value for no redirect type in grid. + */ + const REDIRECT_TYPE_NO = 'No'; + + /** + * Curl transport on webapi. + * + * @var WebapiDecorator + */ + private $webApi; + + /** + * Url rewrite index page. + * + * @var UrlRewriteIndex + */ + private $urlRewriteIndex; + /** * Assert that url rewrite category in grid. * * @param Category $category + * @param WebapiDecorator $webApi * @param UrlRewriteIndex $urlRewriteIndex + * @param Category $parentCategory + * @param Category $childCategory + * @param string|null $nestingLevel * @param string $filterByPath + * @param string $redirectType * @return void */ public function processAssert( Category $category, + WebapiDecorator $webApi, UrlRewriteIndex $urlRewriteIndex, - $filterByPath = 'target_path' + Category $parentCategory = null, + Category $childCategory = null, + $nestingLevel = null, + $filterByPath = 'target_path', + $redirectType = 'Permanent (301)' ) { + $this->urlRewriteIndex = $urlRewriteIndex; + $this->webApi = $webApi; + $urlRewriteIndex->open(); - $filter = [$filterByPath => strtolower($category->getUrlKey())]; + $categoryId = $this->getCategoryId($category, $childCategory); + $nestingPath = $this->getNestingPath($category, $nestingLevel); + + $filter = [ + 'request_path' => $nestingPath, + 'target_path' => 'catalog/category/view/id/' . $categoryId, + 'redirect_type' => self::REDIRECT_TYPE_NO + ]; + if ($parentCategory && $childCategory) { + $filter['request_path'] = + strtolower($parentCategory->getUrlKey() . '/' . $childCategory->getUrlKey() . '.html'); + } + $this->rowVisibleAssertion($filter); + + if ($redirectType != self::REDIRECT_TYPE_NO) { + if ($parentCategory && $childCategory) { + $urlPath = strtolower($parentCategory->getUrlKey() . '/' . $childCategory->getUrlKey() . '.html'); + $filter = [ + 'request_path' => $nestingPath, + 'target_path' => $urlPath, + 'redirect_type' => $redirectType + ]; + } else { + $filter = [$filterByPath => strtolower($category->getUrlKey())]; + } + $this->rowVisibleAssertion($filter); + } + } + + /** + * Get category id. + * + * @param Category $category + * @param Category|null $childCategory + * @return int + */ + private function getCategoryId(Category $category, Category $childCategory = null) + { + return ($childCategory ? $childCategory->getId() : $category->getId()) + ? $category->getId() + : $this->retrieveCategory($category)['id']; + } + + /** + * Assert that url rewrite category in grid. + * + * @param array $filter + * @return void + */ + private function rowVisibleAssertion(array $filter) + { + $filterRow = implode(', ', $filter); \PHPUnit_Framework_Assert::assertTrue( - $urlRewriteIndex->getUrlRedirectGrid()->isRowVisible($filter, true, false), - 'URL Rewrite with request path "' . $category->getUrlKey() . '" is absent in grid.' + $this->urlRewriteIndex->getUrlRedirectGrid()->isRowVisible($filter, true, false), + 'URL Rewrite with request path "' . $filterRow . '" is absent in grid.' ); } + /** + * Return nesting url path. + * + * @param Category $category + * @param int $nestingLevel + * @return string + */ + private function getNestingPath(Category $category, $nestingLevel) + { + if ($nestingLevel === null) { + return strtolower($category->getUrlKey() . '.html'); + } + $filterByRequestPathCondition = []; + for ($nestingIterator = 0; $nestingIterator < $nestingLevel; $nestingIterator++) { + $filterByRequestPathCondition[] = $category->getUrlKey(); + $category = $category->getDataFieldConfig('parent_id')['source']->getParentCategory(); + } + + return strtolower(implode('/', array_reverse($filterByRequestPathCondition)) . '.html'); + } + + /** + * Retrieve category. + * + * @param Category $category + * @return array + */ + private function retrieveCategory(Category $category) + { + $childrenIds = explode(',', $this->getResponse($category->getData('parent_id'))['children']); + while ($id = array_pop($childrenIds)) { + $retrieveCategory = $this->getResponse($id); + if ($retrieveCategory['name'] == $category->getData('name')) { + return $retrieveCategory; + } + } + return ['id' => null]; + } + + /** + * Return category data by category id. + * + * @param int $categoryId + * @return array + */ + private function getResponse($categoryId) + { + $url = $_ENV['app_frontend_url'] . 'rest/all/V1/categories/' . $categoryId; + $this->webApi->write($url, [], WebapiDecorator::GET); + $response = json_decode($this->webApi->read(), true); + $this->webApi->close(); + return $response; + } + /** * URL rewrite category present in grid. * diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductInGrid.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductInGrid.php new file mode 100644 index 0000000000000000000000000000000000000000..8277df90cb646208132782a05ad51ea4e4797051 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductInGrid.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\UrlRewrite\Test\Constraint; + +use Magento\Mtf\Fixture\FixtureInterface; +use Magento\UrlRewrite\Test\Page\Adminhtml\UrlRewriteIndex; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that url rewrite product in grid. + */ +class AssertUrlRewriteProductInGrid extends AbstractConstraint +{ + /** + * Assert that url rewrite product in grid. + * + * @param UrlRewriteIndex $urlRewriteIndex + * @param FixtureInterface $product + * @return void + */ + public function processAssert( + UrlRewriteIndex $urlRewriteIndex, + FixtureInterface $product + ) { + $urlRewriteIndex->open(); + $categories = $product->getDataFieldConfig('category_ids')['source']->getCategories(); + $rootCategoryArray = []; + foreach ($categories as $category) { + $parentName = $category->getDataFieldConfig('parent_id')['source']->getParentCategory()->getName(); + $rootCategoryArray[$parentName] = $category->getUrlKey(); + } + + $stores = $product->getDataFieldConfig('website_ids')['source']->getStores(); + foreach ($stores as $store) { + $rootCategoryName = $store->getDataFieldConfig('group_id')['source'] + ->getStoreGroup() + ->getDataFieldConfig('root_category_id')['source'] + ->getCategory() + ->getName(); + + $storeName = $store->getName(); + $filters = [ + [ + 'request_path' => $product->getUrlKey() . '.html', + 'store_id' => $storeName + ], + [ + 'request_path' => $rootCategoryArray[$rootCategoryName] . '.html', + 'store_id' => $storeName + ], + [ + 'request_path' => $rootCategoryArray[$rootCategoryName] . '/' . $product->getUrlKey() . '.html', + 'store_id' => $storeName + ], + ]; + foreach ($filters as $filter) { + \PHPUnit_Framework_Assert::assertTrue( + $urlRewriteIndex->getUrlRedirectGrid()->isRowVisible($filter, true, false), + 'URL Rewrite with request path \'' . $filter['request_path'] . '\' is absent in grid.' + ); + + } + } + } + + /** + * URL rewrite product present in grid. + * + * @return string + */ + public function toString() + { + return 'URL Rewrite is present in grid.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductNotInGrid.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductNotInGrid.php new file mode 100644 index 0000000000000000000000000000000000000000..c32e9925ad0ac54d2bca1d85f52318359c75085a --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/Constraint/AssertUrlRewriteProductNotInGrid.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\UrlRewrite\Test\Constraint; + +use Magento\Mtf\Fixture\FixtureInterface; +use Magento\UrlRewrite\Test\Page\Adminhtml\UrlRewriteIndex; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that url rewrite product not in grid. + */ +class AssertUrlRewriteProductNotInGrid extends AbstractConstraint +{ + /** + * Assert that url rewrite not in grid + * + * @param UrlRewriteIndex $urlRewriteIndex + * @param FixtureInterface $product + * @return void + */ + public function processAssert(UrlRewriteIndex $urlRewriteIndex, FixtureInterface $product) + { + $urlRewriteIndex->open(); + $requestPath = $product->getUrlKey() . '.html'; + $filter = ['request_path' => $requestPath]; + \PHPUnit_Framework_Assert::assertFalse( + $urlRewriteIndex->getUrlRedirectGrid()->isRowVisible($filter), + 'URL Rewrite with request path \'' . $requestPath . '\' is present in grid.' + ); + } + + /** + * URL rewrite product not present in grid. + * + * @return string + */ + public function toString() + { + return 'URL Rewrite is not present in grid.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest.xml index dc8ba167417e98aeaa1abdba243e1a6f0b2ab922..7153ce42f5fb677e56c887d93da01be64eeef447 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductUrlRewriteEntityTest.xml @@ -47,5 +47,15 @@ <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteSaveMessage" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteProductRedirect" /> </variation> + <variation name="CreateProductUrlRewriteEntityTestVariation5" summary="Autoupdate URL Rewrites if Subcategories deleted" ticketId="MAGETWO-27325"> + <data name="urlRewrite/data/entity_type" xsi:type="string">For Product</data> + <data name="product/dataset" xsi:type="string">product_with_category</data> + <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> + <data name="urlRewrite/data/request_path" xsi:type="string">cat%isolation%/simp_redirect%isolation%.html</data> + <data name="urlRewrite/data/redirect_type" xsi:type="string">Temporary (302)</data> + <data name="urlRewrite/data/description" xsi:type="string">description_%isolation%</data> + <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteSaveMessage" /> + <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteAfterDeletingCategory" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.php b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2bb0ebe63650265aa63ad8e851070a757418f61c --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\UrlRewrite\Test\TestCase; + +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; +use Magento\Catalog\Test\Page\Adminhtml\CatalogProductNew; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Mtf\TestCase\Injectable; + +/** + * Steps: + * 1. Login to the backend. + * 2. Navigate to Products > Catalog. + * 3. Start to create simple product. + * 4. Fill in data according to data set. + * 5. Save Product. + * 6. Perform appropriate assertions. + * + * @ZephyrId MAGETWO-27238 + */ +class CreateProductWithSeveralWebsitesUrlRewriteTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + /* end tags */ + + /** + * Run create product with several websites url rewrite test. + * + * @param CatalogProductSimple $product + * @param CatalogProductIndex $productGrid + * @param CatalogProductNew $newProductPage + * @param FixtureFactory $fixtureFactory + * @param array $websiteCategories + * @return array + */ + public function testCreate( + CatalogProductSimple $product, + CatalogProductIndex $productGrid, + CatalogProductNew $newProductPage, + FixtureFactory $fixtureFactory, + array $websiteCategories + ) { + $categoryParent = []; + $categoryList = []; + $storeList = []; + + // Preconditions + foreach ($websiteCategories as $websiteCategory) { + list($storeGroup, $store, $category) = explode('::', $websiteCategory); + if (!isset($categoryParent[$category])) { + $categoryListItem = $fixtureFactory->createByCode('category', ['dataset' => $category]); + $categoryListItem->persist(); + $categoryParent[$category] = $categoryListItem->getDataFieldConfig('parent_id')['source'] + ->getParentCategory(); + $categoryList[] = $categoryListItem; + } + $storeGroup = $fixtureFactory->createByCode('storeGroup', [ + 'dataset' => $storeGroup, + 'data' => [ + 'root_category_id' => [ + 'category' => $categoryParent[$category] + ] + ] + ]); + $storeGroup->persist(); + $store = $fixtureFactory->createByCode('store', [ + 'dataset' => $store, + 'data' => [ + 'group_id' => [ + 'storeGroup' => $storeGroup + ] + ] + ]); + $store->persist(); + $storeList[] = $store; + } + + $productData = $product->getData(); + $productData['website_ids'] = $storeList; + $productData['category_ids'] = $categoryList; + + $product = $fixtureFactory->createByCode( + 'catalogProductSimple', + [ + 'dataset' => 'default', + 'data' => $productData, + ] + ); + + // Steps + $productGrid->open(); + $productGrid->getGridPageActionBlock()->addProduct('simple'); + $newProductPage->getProductForm()->fill($product); + $newProductPage->getFormPageActions()->save(); + + return ['product' => $product]; + } +} diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..afde5d24637ac751f7bc8d599be2e0e9720c114d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateProductWithSeveralWebsitesUrlRewriteTest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\UrlRewrite\Test\TestCase\CreateProductWithSeveralWebsitesUrlRewriteTest" summary="Test product url rewrites when it is created in several websites"> + <variation name="CreateSimpleProductEntityWithSeveralWebsites" summary="Create product with several websites and check URL Rewites" ticketId="MAGETWO-27238"> + <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> + <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> + <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> + <data name="product/data/price/value" xsi:type="string">42</data> + <data name="product/data/weight" xsi:type="string">50</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">345</data> + <data name="websiteCategories" xsi:type="array"> + <item name="0" xsi:type="string">default::default::default</item> + <item name="1" xsi:type="string">store_group_new_1::store_new_1::default</item> + <item name="2" xsi:type="string">store_group_new_2::store_new_2::root_subcategory</item> + </data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> + <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteProductInGrid" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertIncorrectUserPassword.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertIncorrectUserPassword.php index ffc549a6211913210bf5a2af67425579bdb8e160..62a3c64142949067c18d830411c13054e539b71f 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertIncorrectUserPassword.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertIncorrectUserPassword.php @@ -6,11 +6,11 @@ namespace Magento\User\Test\Constraint; -use Magento\User\Test\Page\Adminhtml\UserRoleIndex; +use Magento\Backend\Test\Page\Adminhtml\Dashboard; use Magento\Mtf\Constraint\AbstractConstraint; /** - * Class AssertIncorrectUserPassword + * Assert that message about incorrect user password is displayed. */ class AssertIncorrectUserPassword extends AbstractConstraint { @@ -19,12 +19,12 @@ class AssertIncorrectUserPassword extends AbstractConstraint /** * Asserts that invalid password message equals to expected message. * - * @param UserRoleIndex $rolePage + * @param Dashboard $dashboard * @return void */ - public function processAssert(UserRoleIndex $rolePage) + public function processAssert(Dashboard $dashboard) { - $errorMessage = $rolePage->getMessagesBlock()->getErrorMessage(); + $errorMessage = $dashboard->getMessagesBlock()->getErrorMessage(); \PHPUnit_Framework_Assert::assertEquals( self::ERROR_MESSAGE, $errorMessage, diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Repository/Role.xml b/dev/tests/functional/tests/app/Magento/User/Test/Repository/Role.xml index 6d256383ba8f874be836671b2cc20e2be4b17e37..6d19e0488c287f8016b3c33ecfdfe4cde807f836 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/Repository/Role.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/Repository/Role.xml @@ -47,5 +47,35 @@ <item name="Send Sales Emails" xsi:type="string">Magento_Sales::emails</item> </field> </dataset> + + <dataset name="role_without_variable"> + <field name="rolename" xsi:type="string">RoleName%isolation%</field> + <field name="resource_access" xsi:type="string">Custom</field> + <field name="current_password" xsi:type="string">%current_password%</field> + <field name="roles_resources" xsi:type="array"> + <item name="Dashboard" xsi:type="string">Magento_Backend::dashboard</item> + <item name="AdminNotification" xsi:type="string">Magento_AdminNotification::adminnotification</item> + <item name="EncryptionKey" xsi:type="string">Magento_EncryptionKey::crypt_key</item> + <item name="Content" xsi:type="string">Magento_Backend::content</item> + <item name="ContentElements" xsi:type="string">Magento_Backend::content_elements</item> + <item name="Page" xsi:type="string">Magento_Cms::page</item> + <item name="SavePage" xsi:type="string">Magento_Cms::save</item> + <item name="DeletePage" xsi:type="string">Magento_Cms::page_delete</item> + </field> + </dataset> + + <dataset name="role_without_synonym"> + <field name="rolename" xsi:type="string">RoleName%isolation%</field> + <field name="resource_access" xsi:type="string">Custom</field> + <field name="current_password" xsi:type="string">%current_password%</field> + <field name="roles_resources" xsi:type="array"> + <item name="Dashboard" xsi:type="string">Magento_Backend::dashboard</item> + <item name="Marketing" xsi:type="string">Magento_Backend::marketing</item> + <item name="Seo" xsi:type="string">Magento_Backend::marketing_seo</item> + <item name="SearchTerms" xsi:type="string">Magento_Search::search</item> + <item name="UrlRewrite" xsi:type="string">Magento_UrlRewrite::urlrewrite</item> + <item name="Sitemap" xsi:type="string">Magento_Sitemap::sitemap</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Repository/User.xml b/dev/tests/functional/tests/app/Magento/User/Test/Repository/User.xml index dbb7a4aeb53bb667bf22e99e0198d1b0e3cab25c..29c61d931d73090e9f8fd3a0823f66451596d540 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/Repository/User.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/Repository/User.xml @@ -45,5 +45,33 @@ <dataset name="system_admin"> <field name="current_password" xsi:type="string">123123q</field> </dataset> + + <dataset name="custom_admin_with_role_without_variable"> + <field name="username" xsi:type="string">AdminUser%isolation%</field> + <field name="firstname" xsi:type="string">FirstName%isolation%</field> + <field name="lastname" xsi:type="string">LastName%isolation%</field> + <field name="email" xsi:type="string">email%isolation%@example.com</field> + <field name="password" xsi:type="string">123123q</field> + <field name="password_confirmation" xsi:type="string">123123q</field> + <field name="role_id" xsi:type="array"> + <item name="dataset" xsi:type="string">role::role_without_variable</item> + </field> + <field name="current_password" xsi:type="string">%current_password%</field> + <field name="is_active" xsi:type="string">Active</field> + </dataset> + + <dataset name="custom_admin_with_role_without_synonym"> + <field name="username" xsi:type="string">AdminUser%isolation%</field> + <field name="firstname" xsi:type="string">FirstName%isolation%</field> + <field name="lastname" xsi:type="string">LastName%isolation%</field> + <field name="email" xsi:type="string">email%isolation%@example.com</field> + <field name="password" xsi:type="string">123123q</field> + <field name="password_confirmation" xsi:type="string">123123q</field> + <field name="role_id" xsi:type="array"> + <item name="dataset" xsi:type="string">role::role_without_synonym</item> + </field> + <field name="current_password" xsi:type="string">%current_password%</field> + <field name="is_active" xsi:type="string">Active</field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserEntityTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserEntityTest.php index fdf10565117764fa85727d38a4d10c44e4047261..27c38a27df12f1203ee19fb2b1d3bc3ef79efe62 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserEntityTest.php @@ -91,10 +91,10 @@ class CreateAdminUserEntityTest extends Injectable * @param string $isDuplicated * @return array */ - public function test(User $user, User $adminUser, $isDuplicated) + public function test(User $user, User $adminUser, $isDuplicated = null) { // Prepare data - if ($isDuplicated != '-') { + if ($isDuplicated !== null) { $data = $user->getData(); $data[$isDuplicated] = $adminUser->getData($isDuplicated); $data['role_id'] = ['role' => $user->getDataFieldConfig('role_id')['source']->getRole()]; diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserEntityTest.xml index f2005174d24dbda5791633790519ca1c3a8c4a76..b6c346fea8dfbd66803cf7b1098fe85cce65c43e 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CreateAdminUserEntityTest.xml @@ -17,7 +17,6 @@ <data name="user/data/password_confirmation" xsi:type="string">123123q</data> <data name="user/data/is_active" xsi:type="string">Active</data> <data name="user/data/role_id/dataset" xsi:type="string">role::Administrators</data> - <data name="isDuplicated" xsi:type="string">-</data> <data name="user/data/current_password" xsi:type="string">%current_password%</data> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessSaveMessage" /> <constraint name="Magento\User\Test\Constraint\AssertUserInGrid" /> @@ -33,7 +32,6 @@ <data name="user/data/password_confirmation" xsi:type="string">123123q</data> <data name="user/data/is_active" xsi:type="string">Inactive</data> <data name="user/data/role_id/dataset" xsi:type="string">role::Administrators</data> - <data name="isDuplicated" xsi:type="string">-</data> <data name="user/data/current_password" xsi:type="string">123123q</data> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessSaveMessage" /> <constraint name="Magento\User\Test\Constraint\AssertUserInGrid" /> @@ -72,7 +70,6 @@ <data name="user/data/password" xsi:type="string">123123q</data> <data name="user/data/password_confirmation" xsi:type="string">123123q</data> <data name="user/data/is_active" xsi:type="string">Active</data> - <data name="isDuplicated" xsi:type="string">-</data> <data name="user/data/current_password" xsi:type="string">%current_password%</data> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessSaveMessage" /> <constraint name="Magento\User\Test\Constraint\AssertUserInGrid" /> @@ -87,9 +84,18 @@ <data name="user/data/password" xsi:type="string">123123q</data> <data name="user/data/password_confirmation" xsi:type="string">123123q</data> <data name="user/data/is_active" xsi:type="string">Active</data> - <data name="isDuplicated" xsi:type="string">-</data> <data name="user/data/current_password" xsi:type="string">%current_password%</data> <constraint name="Magento\User\Test\Constraint\AssertUserInvalidEmailHostnameMessage" /> </variation> + <variation name="CreateAdminUserWithIncorrectCurrentPassword" summary="Try to create admin user with incorrect current password" ticketId="MAGETWO-49034"> + <data name="user/data/username" xsi:type="string">AdminUser%isolation%</data> + <data name="user/data/firstname" xsi:type="string">FirstName%isolation%</data> + <data name="user/data/lastname" xsi:type="string">LastName%isolation%</data> + <data name="user/data/email" xsi:type="string">email%isolation%@example.com</data> + <data name="user/data/password" xsi:type="string">123123q</data> + <data name="user/data/password_confirmation" xsi:type="string">123123q</data> + <data name="user/data/current_password" xsi:type="string">incorrect-password</data> + <constraint name="Magento\User\Test\Constraint\AssertIncorrectUserPassword" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CustomAclPermissionTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CustomAclPermissionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8378efa6122adade9016a2c42f0fbc1e745a417e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/CustomAclPermissionTest.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Test\TestCase; + +use Magento\User\Test\Fixture\User; +use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\TestStep\TestStepFactory; + +/** + * Preconditions: + * 1. Create custom admin with custom role. + * + * Steps: + * 1. Login to the admin panel with the newly created admin user. + * 2. Perform all assertions. + * + * @ZephyrId MAGETWO-41214, MAGETWO-58541, MAGETWO-47568 + */ +class CustomAclPermissionTest extends Injectable +{ + /* tags */ + const MVP = 'no'; + /* end tags */ + + /** + * Test step factory. + * + * @var TestStepFactory + */ + protected $testStepFactory; + + /** + * Setup necessary data for test. + * + * @param TestStepFactory $testStepFactory + * @return void + */ + public function __inject( + TestStepFactory $testStepFactory + ) { + $this->testStepFactory = $testStepFactory; + } + + /** + * Test acl permissions. + * + * @param User $user + * @return array + */ + public function test(User $user) + { + $user->persist(); + $this->testStepFactory->create( + \Magento\User\Test\TestStep\LoginUserOnBackendStep::class, + ['user' => $user] + )->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Variable/Test/Constraint/AssertCustomVariableRestrictedAccess.php b/dev/tests/functional/tests/app/Magento/Variable/Test/Constraint/AssertCustomVariableRestrictedAccess.php new file mode 100644 index 0000000000000000000000000000000000000000..cee2e597216a78619be4df109b85fb474cb125d2 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Variable/Test/Constraint/AssertCustomVariableRestrictedAccess.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Variable\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Cms\Test\Page\Adminhtml\CmsPageNew; +use Magento\Variable\Test\Fixture\SystemVariable; + +/** + * Check that access to variables block on CMS page in admin panel is restricted. + */ +class AssertCustomVariableRestrictedAccess extends AbstractConstraint +{ + /** + * Assert that variables block is not visible. + * + * @param CmsPageNew $cmsPageNew + * @param SystemVariable $systemVariable + * @return void + */ + public function processAssert( + CmsPageNew $cmsPageNew, + SystemVariable $systemVariable + ) { + $systemVariable->persist(); + $cmsPageNew->open(); + + \PHPUnit_Framework_Assert::assertFalse( + $cmsPageNew->getPageForm()->isVariablesBlockVisible(), + 'Access to system variables block is supposed to be restricted.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return 'Access to system variables block is restricted and block is not visible.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Variable/Test/TestCase/CustomAclPermissionTest.xml b/dev/tests/functional/tests/app/Magento/Variable/Test/TestCase/CustomAclPermissionTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd4d623de6325d567c12f8d3aaae40ee017b7c85 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Variable/Test/TestCase/CustomAclPermissionTest.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\User\Test\TestCase\CustomAclPermissionTest" summary="Custom ACL Permission" ticketId="MAGETWO-58541"> + <variation name="CustomVariableAclPermissionTestVariation1" summary="Custom variable ACL permission" ticketId="MAGETWO-41214"> + <data name="user/dataset" xsi:type="string">custom_admin_with_role_without_variable</data> + <data name="systemVariable/dataset" xsi:type="string">default</data> + <constraint name="Magento\Variable\Test\Constraint\AssertCustomVariableRestrictedAccess" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php index bf890b2448e23f7a134574b9fb2fb2d7eca1151e..d23985bc38ef961b6b7881a02cc35b6811012cc8 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php @@ -40,6 +40,7 @@ class DocBlock * * Note: order of registering (and applying) annotations is important. * To allow config fixtures to deal with fixture stores, data fixtures should be processed first. + * ConfigFixture applied twice because data fixtures could clean config and clean custom settings * * @param \Magento\TestFramework\Application $application * @return array @@ -68,6 +69,7 @@ class DocBlock new \Magento\TestFramework\Annotation\AppArea($application), new \Magento\TestFramework\Annotation\Cache($application), new \Magento\TestFramework\Annotation\AdminConfigFixture(), + new \Magento\TestFramework\Annotation\ConfigFixture(), ]; } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/ObjectManager.php b/dev/tests/integration/framework/Magento/TestFramework/ObjectManager.php index ad685845cde004bd54c8499671e9e22faa72d0e5..03fb9aa9ddae60d356158905a412500f12f8b616 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/ObjectManager.php +++ b/dev/tests/integration/framework/Magento/TestFramework/ObjectManager.php @@ -24,8 +24,6 @@ class ObjectManager extends \Magento\Framework\App\ObjectManager * @var array */ protected $persistedInstances = [ - \Magento\TestFramework\App\Config::class, - \Magento\Framework\App\Config\ScopeConfigInterface::class, \Magento\Framework\App\ResourceConnection::class, \Magento\Framework\Config\Scope::class, \Magento\Framework\ObjectManager\RelationsInterface::class, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Helper/DataTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Helper/DataTest.php index 071c2499bb33262f1f1df9a6bcbd1f37340516ed..c2db58728d33d426a2e87736a8bbd76f212b1e91 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Helper/DataTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Helper/DataTest.php @@ -248,6 +248,7 @@ class DataTest extends \PHPUnit_Framework_TestCase * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDbIsolation enabled + * @magentoAppIsolation enabled * @dataProvider getTaxPriceDataProvider */ public function testGetTaxPrice( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites.php index fa64de38e43114d74389f2c4ee27a3e0c0b7606d..426ee922f261426758e1ea2361ecf1d4716e9cb9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites.php @@ -9,25 +9,23 @@ use Magento\Catalog\Api\Data\ProductInterface; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); /** @var Magento\Store\Model\Website $website */ $website = $objectManager->get(Magento\Store\Model\Website::class); -$website->load('second_website', 'code'); -if (!$website->getId()) { - $website->setData( - [ - 'code' => 'second_website', - 'name' => 'Test Website', - ] - ); +$website->setData( + [ + 'code' => 'second_website', + 'name' => 'Test Website', + ] +); - $website->save(); -} +$website->save(); + +$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores(); /** @var $product \Magento\Catalog\Model\Product */ $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(ProductInterface::class); $product ->setTypeId('simple') - ->setId(1) ->setAttributeSetId(4) ->setWebsiteIds([1, $website->getId()]) ->setName('Simple Product') diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites_rollback.php new file mode 100644 index 0000000000000000000000000000000000000000..68f1106dcfe3098041c58726d72f2d7f949ddaea --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites_rollback.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\Data\ProductInterface; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager + ->get(\Magento\Framework\Registry::class); +$registry->unregister("isSecureArea"); +$registry->register("isSecureArea", true); + +/** @var Magento\Store\Model\Website $website */ +$website = $objectManager->create(\Magento\Store\Model\Website::class); +$website->load('second_website'); +$website->delete(); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +try { + $firstProduct = $productRepository->get('unique-simple-azaza'); + $firstProduct->delete(); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website.php index 6749d421e79981f0b3b917f21b975c9ad2c74e26..4dc026d6fad4d58a3533653bc0bc4cb070683313 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website.php @@ -19,3 +19,5 @@ if (!$website->getId()) { $website->save(); } + +$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website_rollback.php new file mode 100644 index 0000000000000000000000000000000000000000..173793a1caa49bbc772916f23d6e24c2e6c87342 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website_rollback.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry'); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$website = $objectManager->get(Magento\Store\Model\Website::class); +$website->load('test_website', 'code'); + +if ($website->getId()) { + $website->delete(); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/IndexSwitcherMock.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/IndexSwitcherMock.php new file mode 100644 index 0000000000000000000000000000000000000000..955ccce3bb59e5fc5b23ed57dbf917941ace1eb7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/IndexSwitcherMock.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Indexer; + + +use Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface; + +/** + * The proxy class around index switcher which allows to ensure that the IndexSwitcher was actually used + */ +class IndexSwitcherMock extends \PHPUnit_Framework_Assert implements IndexSwitcherInterface +{ + private $isSwitched = false; + + /** + * @var \Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface + */ + private $indexSwitcher; + + /** + * @param \Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface $indexSwitcher + */ + public function __construct( + IndexSwitcherInterface $indexSwitcher + ) { + $this->indexSwitcher = $indexSwitcher; + } + + /** + * Switch current index with temporary index + * + * It will drop current index table and rename temporary index table to the current index table. + * + * @param array $dimensions + * @return void + */ + public function switchIndex(array $dimensions) + { + $this->isSwitched |= true; + $this->indexSwitcher->switchIndex($dimensions); + } + + /** + * @return bool + */ + public function isSwitched() + { + return (bool) $this->isSwitched; + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/SwitcherUsedInFulltextTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/SwitcherUsedInFulltextTest.php new file mode 100644 index 0000000000000000000000000000000000000000..afcfef5fc4c8e085549c46568d47aa45041b7a6a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/SwitcherUsedInFulltextTest.php @@ -0,0 +1,214 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogSearch\Model\Indexer; + +use Magento\Catalog\Model\Product; +use Magento\CatalogSearch\Model\Indexer\Fulltext; +use Magento\CatalogSearch\Model\Indexer\IndexSwitcherMock; +use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/CatalogSearch/_files/indexer_fulltext.php + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SwitcherUsedInFulltextTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var IndexSwitcherInterface + */ + private $indexSwitcher; + + /** + * @var \Magento\Framework\Indexer\IndexerInterface + */ + protected $indexer; + + /** + * @var \Magento\CatalogSearch\Model\ResourceModel\Engine + */ + protected $engine; + + /** + * @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext + */ + protected $resourceFulltext; + + /** + * @var \Magento\CatalogSearch\Model\Fulltext + */ + protected $fulltext; + + /** + * @var \Magento\Search\Model\QueryFactory + */ + protected $queryFactory; + + /** + * @var \Magento\Catalog\Model\Product + */ + protected $productApple; + + /** + * @var \Magento\Catalog\Model\Product + */ + protected $productBanana; + + /** + * @var \Magento\Catalog\Model\Product + */ + protected $productOrange; + + /** + * @var \Magento\Catalog\Model\Product + */ + protected $productPapaya; + + /** + * @var \Magento\Catalog\Model\Product + */ + protected $productCherry; + + /** + * @var \Magento\Framework\Search\Request\Dimension + */ + protected $dimension; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + + $objectManager->configure( + [ + ltrim(Fulltext::class, '\\') => [ + 'arguments' => [ + 'indexSwitcher' => [ + 'instance' => ltrim(IndexSwitcherMock::class, '\\'), + ], + ], + ], + ] + ); + + /** @var \Magento\Framework\Indexer\IndexerInterface indexer */ + $this->indexer = $objectManager->create( + \Magento\Indexer\Model\Indexer::class + ); + $this->indexer->load('catalogsearch_fulltext'); + + $this->engine = $objectManager->get( + \Magento\CatalogSearch\Model\ResourceModel\Engine::class + ); + + $this->resourceFulltext = $objectManager->get( + \Magento\CatalogSearch\Model\ResourceModel\Fulltext::class + ); + + $this->queryFactory = $objectManager->get( + \Magento\Search\Model\QueryFactory::class + ); + + $this->dimension = $objectManager->create( + \Magento\Framework\Search\Request\Dimension::class, + ['name' => 'scope', 'value' => '1'] + ); + + $this->indexSwitcher = Bootstrap::getObjectManager()->get( + IndexSwitcherMock::class + ); + + $this->productApple = $this->getProductBySku('fulltext-1'); + $this->productBanana = $this->getProductBySku('fulltext-2'); + $this->productOrange = $this->getProductBySku('fulltext-3'); + $this->productPapaya = $this->getProductBySku('fulltext-4'); + $this->productCherry = $this->getProductBySku('fulltext-5'); + } + + /** + * @magentoAppIsolation enabled + */ + public function testReindexAll() + { + $this->indexer->reindexAll(); + + /** @var IndexSwitcherMock $indexSwitcher */ + $indexSwitcher = Bootstrap::getObjectManager()->get( + IndexSwitcherMock::class + ); + $this->assertTrue($indexSwitcher->isSwitched()); + } + + /** + * @magentoAppIsolation enabled + */ + public function testReindexList() + { + $this->indexer->reindexList([$this->productApple->getId(), $this->productBanana->getId()]); + + /** @var IndexSwitcherMock $indexSwitcher */ + $indexSwitcher = Bootstrap::getObjectManager()->get( + IndexSwitcherMock::class + ); + $this->assertFalse($indexSwitcher->isSwitched()); + } + + /** + * @magentoAppIsolation enabled + */ + public function testReindexRow() + { + $this->indexer->reindexRow($this->productPapaya->getId()); + + /** @var IndexSwitcherMock $indexSwitcher */ + $indexSwitcher = Bootstrap::getObjectManager()->get( + IndexSwitcherMock::class + ); + $this->assertFalse($indexSwitcher->isSwitched()); + } + + /** + * Search the text and return result collection + * + * @param string $text + * @return Product[] + */ + protected function search($text) + { + $this->resourceFulltext->resetSearchResults(); + $query = $this->queryFactory->get(); + $query->unsetData(); + $query->setQueryText($text); + $query->saveIncrementalPopularity(); + $products = []; + $collection = Bootstrap::getObjectManager()->create( + Collection::class, + [ + 'searchRequestName' => 'quick_search_container' + ] + ); + $collection->addSearchFilter($text); + foreach ($collection as $product) { + $products[] = $product; + } + return $products; + } + + /** + * Return product by SKU + * + * @param string $sku + * @return Product + */ + protected function getProductBySku($sku) + { + /** @var Product $product */ + $product = Bootstrap::getObjectManager()->get( + \Magento\Catalog\Model\Product::class + ); + return $product->loadByAttribute('sku', $sku); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php index 67a6e416973493887912e2e62674108b1694c315..7f50638f469de0f8a5c392f97c74094582a432b9 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php @@ -331,6 +331,10 @@ class AdapterTest extends \PHPUnit_Framework_TestCase * * @magentoConfigFixture current_store catalog/search/engine mysql * @dataProvider advancedSearchDataProvider + * @param string $nameQuery + * @param string $descriptionQuery + * @param array $rangeFilter + * @param int $expectedRecordsCount */ public function testSimpleAdvancedSearch( $nameQuery, @@ -445,6 +449,37 @@ class AdapterTest extends \PHPUnit_Framework_TestCase $this->assertEquals(1, $queryResponse->count()); } + /** + * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_disabled_child.php + * @magentoConfigFixture current_store catalog/search/engine mysql + */ + public function testAdvancedSearchCompositeProductWithDisabledChild() + { + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */ + $attribute = $this->objectManager->get(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + ->loadByCode(\Magento\Catalog\Model\Product::ENTITY, 'test_configurable'); + /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection $selectOptions */ + $selectOptions = $this->objectManager + ->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class) + ->setAttributeFilter($attribute->getId()); + + $firstOption = $selectOptions->getFirstItem(); + $firstOptionId = $firstOption->getId(); + $this->requestBuilder->bind('test_configurable', $firstOptionId); + $this->requestBuilder->setRequestName('filter_out_of_stock_child'); + + $queryResponse = $this->executeQuery(); + $this->assertEquals(0, $queryResponse->count()); + + $secondOption = $selectOptions->getLastItem(); + $secondOptionId = $secondOption->getId(); + $this->requestBuilder->bind('test_configurable', $secondOptionId); + $this->requestBuilder->setRequestName('filter_out_of_stock_child'); + + $queryResponse = $this->executeQuery(); + $this->assertEquals(0, $queryResponse->count()); + } + public function dateDataProvider() { return [ diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php index 590f3c8041c6d819fafa14ef7ddc4d1f2ebccbeb..37b8731fc1d4c377ffc26a16be2d4100997f7247 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php @@ -15,7 +15,7 @@ use Magento\ConfigurableProduct\Model\Product\Type\Configurable; use Magento\Eav\Api\Data\AttributeOptionInterface; use Magento\TestFramework\Helper\Bootstrap; -\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(); +Bootstrap::getInstance()->reinitialize(); require __DIR__ . '/configurable_attribute.php'; @@ -33,7 +33,7 @@ $options = $attribute->getOptions(); $attributeValues = []; $attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); $associatedProductIds = []; -$productIds = [10, 20]; +$productIds = [1010, 1020]; array_shift($options); //remove the first option which is empty $isFirstOption = true; @@ -108,29 +108,8 @@ $extensionConfigurableAttributes->setConfigurableProductLinks($associatedProduct $product->setExtensionAttributes($extensionConfigurableAttributes); -// Remove any previously created product with the same id. -/** @var \Magento\Framework\Registry $registry */ -$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); -$registry->unregister('isSecureArea'); -$registry->register('isSecureArea', true); -try { - $productToDelete = $productRepository->getById(1); - $productRepository->delete($productToDelete); - - /** @var \Magento\Quote\Model\ResourceModel\Quote\Item $itemResource */ - $itemResource = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\ResourceModel\Quote\Item::class); - $itemResource->getConnection()->delete( - $itemResource->getMainTable(), - 'product_id = ' . $productToDelete->getId() - ); -} catch (\Exception $e) { - // Nothing to remove -} -$registry->unregister('isSecureArea'); -$registry->register('isSecureArea', false); - $product->setTypeId(Configurable::TYPE_CODE) - ->setId(1) + ->setId(1001) ->setAttributeSetId($attributeSetId) ->setWebsiteIds([1]) ->setName('Configurable Product') @@ -140,8 +119,3 @@ $product->setTypeId(Configurable::TYPE_CODE) ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); $productRepository->save($product); -// -///** @var \Magento\Catalog\Model\Indexer\Product\Eav\Processor $eavIndexer */ -//$eavIndexer = Bootstrap::getObjectManager() -// ->get(\Magento\Catalog\Model\Indexer\Product\Eav\Processor::class); -//$eavIndexer->reindexAll(); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php index 8ba4e3abe21cc14a75a66270d22ea262dffeb788..cc585d177aedb9201faafcdef8bd57d5ea6dec73 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php @@ -16,7 +16,7 @@ $registry->register('isSecureArea', true); $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); -foreach (['simple_10', 'simple_20', 'configurable'] as $sku) { +foreach (['simple_1010', 'simple_1020', 'configurable'] as $sku) { try { $product = $productRepository->get($sku, false, null, true); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child.php new file mode 100644 index 0000000000000000000000000000000000000000..39c9782ba712a7ed2204a61bdcd549863e202ed8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_configurable.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->create(ProductRepositoryInterface::class); + +$product = $productRepository->get('simple_1020'); +$product->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child_rollback.php new file mode 100644 index 0000000000000000000000000000000000000000..58d1dcd79acde15c2d493625ca38c656ad286bb7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child_rollback.php @@ -0,0 +1,7 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/product_configurable_rollback.php'; diff --git a/lib/internal/Magento/Framework/Indexer/Action/Base.php b/lib/internal/Magento/Framework/Indexer/Action/Base.php index e7edcae5d4391e27e06020eb60467b093fec05f3..d761f388456b65114ffe0e85698a1e441e82467e 100644 --- a/lib/internal/Magento/Framework/Indexer/Action/Base.php +++ b/lib/internal/Magento/Framework/Indexer/Action/Base.php @@ -37,11 +37,13 @@ class Base implements ActionInterface /** * @var AdapterInterface + * @deprecated */ protected $connection; /** * @var SourceProviderInterface[] + * @deprecated */ protected $sources; @@ -52,6 +54,7 @@ class Base implements ActionInterface /** * @var HandlerInterface[] + * @deprecated */ protected $handlers; @@ -62,6 +65,7 @@ class Base implements ActionInterface /** * @var array + * @deprecated */ protected $columnTypesMap = [ 'varchar' => ['type' => Table::TYPE_TEXT, 'size' => 255], @@ -71,11 +75,13 @@ class Base implements ActionInterface /** * @var array + * @deprecated */ protected $filterColumns; /** * @var array + * @deprecated */ protected $searchColumns; @@ -96,6 +102,7 @@ class Base implements ActionInterface /** * @var String + * @deprecated */ protected $string; @@ -106,11 +113,13 @@ class Base implements ActionInterface /** * @var array + * @deprecated */ protected $filterable = []; /** * @var array + * @deprecated */ protected $searchable = []; @@ -272,6 +281,7 @@ class Base implements ActionInterface protected function createResultCollection() { $select = $this->getPrimaryResource()->getSelect(); + $select->reset(\Magento\Framework\DB\Select::COLUMNS); $select->columns($this->getPrimaryResource()->getIdFieldName()); foreach ($this->data['fieldsets'] as $fieldset) { if (isset($fieldset['references'])) { @@ -342,6 +352,8 @@ class Base implements ActionInterface * * @param array $field * @return void + * + * @deprecated */ protected function saveFieldByType($field) { diff --git a/lib/internal/Magento/Framework/Indexer/Action/Entity.php b/lib/internal/Magento/Framework/Indexer/Action/Entity.php index e88d281fd4b819f2ff80e7bca44cd0593b300154..b8390b9d5a0022f26f7b87da980565ea4b092829 100644 --- a/lib/internal/Magento/Framework/Indexer/Action/Entity.php +++ b/lib/internal/Magento/Framework/Indexer/Action/Entity.php @@ -24,6 +24,6 @@ class Entity extends Base { return !count($ids) ? $this->createResultCollection() - : $this->createResultCollection()->addFieldToFilter($this->getPrimaryResource()->getRowIdFieldName(), $ids); + : $this->createResultCollection()->addFieldToFilter($this->getPrimaryResource()->getIdFieldName(), $ids); } } diff --git a/lib/internal/Magento/Framework/Indexer/SaveHandler/Batch.php b/lib/internal/Magento/Framework/Indexer/SaveHandler/Batch.php index 31883fa8516ea112312eb897f3d0f8c0a93cfced..7d53e558287bd424afe723acf5320fb7f9a07185 100644 --- a/lib/internal/Magento/Framework/Indexer/SaveHandler/Batch.php +++ b/lib/internal/Magento/Framework/Indexer/SaveHandler/Batch.php @@ -10,27 +10,23 @@ class Batch /** * @param \Traversable $documents * @param int $size - * @return array + * @return \Generator */ public function getItems(\Traversable $documents, $size) { - if (count($documents) == 0) { - return []; - } - $i = 0; - $batch = $items = []; + $batch = []; + foreach ($documents as $documentName => $documentValue) { $batch[$documentName] = $documentValue; - if (++$i >= $size) { - $items[] = $batch; + if (++$i == $size) { + yield $batch; $i = 0; $batch = []; } } if (count($batch) > 0) { - $items[] = $batch; + yield $batch; } - return $items; } } diff --git a/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php b/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php index eaab236bbca5bd3dddf6386f7490417bc8be584a..8bcb4acaa11a314e3bd937468cd202fd17ec81b2 100644 --- a/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php +++ b/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php @@ -52,6 +52,7 @@ class IndexScopeResolver implements IndexScopeResolverInterface $tableNameParts[] = $dimension->getName() . $dimension->getValue(); } } + return $this->resource->getTableName(implode('_', $tableNameParts)); } @@ -63,10 +64,12 @@ class IndexScopeResolver implements IndexScopeResolverInterface */ private function getScopeId($dimension) { - if (is_numeric($dimension->getValue())) { - return $dimension->getValue(); - } else { - return $this->scopeResolver->getScope($dimension->getValue())->getId(); + $scopeId = $dimension->getValue(); + + if (!is_numeric($scopeId)) { + $scopeId = $this->scopeResolver->getScope($scopeId)->getId(); } + + return $scopeId; } } diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/BatchTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/BatchTest.php index 0df98125f9a08b7dd9a63e43fdf1070a5af693de..1571bba0a7d23ce224a7d403aa63f624f58bc313 100644 --- a/lib/internal/Magento/Framework/Indexer/Test/Unit/BatchTest.php +++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/BatchTest.php @@ -27,7 +27,8 @@ class BatchTest extends \PHPUnit_Framework_TestCase public function testGetItems(array $itemsData, $size, array $expected) { $items = new \ArrayObject($itemsData); - $this->assertSame($expected, $this->object->getItems($items, $size)); + $data = $this->object->getItems($items, $size); + $this->assertSame($expected, iterator_to_array($data)); } /**