diff --git a/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php index b9ffb332e1cdd93e8c1597880e9a90b66281b2ee..ec1aeee42d175e30a30a4c2099b11f0d69215f89 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php @@ -210,7 +210,11 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { $this->validationResult->addFailedItem( $id, - __('Requested store is not found.') + __( + 'Requested store is not found. Row ID: SKU = %SKU, Store ID: %storeId.', + ['SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] + ), + ['SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] ); } } diff --git a/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php index d5f9d08e9115ddb72351cde957df3b8dce7bee57..6b2575d5eaf7faaed793b225ba4b6b8fd51e7574 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php @@ -190,10 +190,10 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface $this->validationResult->addFailedItem( $id, __( - 'Invalid attribute %fieldName = %fieldValue.', - ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + 'Invalid attribute Cost = %cost. Row ID: SKU = %SKU, Store ID: %storeId.', + ['cost' => $price->getCost(), 'SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] ), - ['fieldName' => 'Cost', 'fieldValue' => $price->getCost()] + ['cost' => $price->getCost(), 'SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] ); } try { @@ -201,7 +201,11 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { $this->validationResult->addFailedItem( $id, - __('Requested store is not found.') + __( + 'Requested store is not found. Row ID: SKU = %SKU, Store ID: %storeId.', + ['SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] + ), + ['SKU' => $price->getSku(), 'storeId' => $price->getStoreId()] ); } } diff --git a/app/code/Magento/Catalog/Model/Product/Price/InvalidSkuChecker.php b/app/code/Magento/Catalog/Model/Product/Price/InvalidSkuChecker.php index 8eb7c1415c69367d984dfdb6a25d47091c626959..c6a34bb803980268c93625b3e1e627fbff46b849 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/InvalidSkuChecker.php +++ b/app/code/Magento/Catalog/Model/Product/Price/InvalidSkuChecker.php @@ -34,7 +34,12 @@ class InvalidSkuChecker public function retrieveInvalidSkuList(array $skus, array $allowedProductTypes, $allowedPriceTypeValue = false) { $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus); - $skuDiff = array_diff($skus, array_keys($idsBySku)); + $existingSkus = array_keys($idsBySku); + $skuDiff = array_udiff( + $skus, + $existingSkus, + 'strcasecmp' + ); foreach ($idsBySku as $sku => $ids) { foreach ($ids as $type) { @@ -42,7 +47,7 @@ class InvalidSkuChecker if ($allowedPriceTypeValue && $type == \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE - && $this->productRepository->get($sku)->getPriceType() != $allowedProductTypes + && $this->productRepository->get($sku)->getPriceType() != $allowedPriceTypeValue ) { $valueTypeIsAllowed = true; } diff --git a/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php index d3ec22571094d203b4a4fbc9ac9c578214d2725d..59220aaf22a1de0407ed355033474a7579f1327f 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php @@ -143,21 +143,47 @@ class SpecialPriceStorage implements \Magento\Catalog\Api\SpecialPriceStorageInt $this->validationResult->addFailedItem( $key, __( - 'Requested product doesn\'t exist: %sku', - ['sku' => '%sku'] + 'Requested product doesn\'t exist. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] ), - ['sku' => $price->getSku()] + [ + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] ); } - $this->checkPrice($price->getPrice(), $key); - $this->checkDate($price->getPriceFrom(), 'Price From', $key); - $this->checkDate($price->getPriceTo(), 'Price To', $key); + $this->checkPrice($price, $key); + $this->checkDate($price, $price->getPriceFrom(), 'Price From', $key); + $this->checkDate($price, $price->getPriceTo(), 'Price To', $key); try { $this->storeRepository->getById($price->getStoreId()); } catch (NoSuchEntityException $e) { $this->validationResult->addFailedItem( $key, - __('Requested store is not found.') + __( + 'Requested store is not found. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] + ), + [ + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] ); } } @@ -172,21 +198,35 @@ class SpecialPriceStorage implements \Magento\Catalog\Api\SpecialPriceStorageInt /** * Check that date value is correct and add error to aggregator if it contains incorrect data. * + * @param \Magento\Catalog\Api\Data\SpecialPriceInterface $price * @param string $value * @param string $label * @param int $key * @return void */ - private function checkDate($value, $label, $key) + private function checkDate(\Magento\Catalog\Api\Data\SpecialPriceInterface $price, $value, $label, $key) { if ($value && !$this->isCorrectDateValue($value)) { $this->validationResult->addFailedItem( $key, __( - 'Invalid attribute %fieldName = %fieldValue.', - ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + 'Invalid attribute %label = %priceTo. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'label' => $label, + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] ), - ['fieldName' => $label, 'fieldValue' => $value] + [ + 'label' => $label, + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] ); } } @@ -195,20 +235,33 @@ class SpecialPriceStorage implements \Magento\Catalog\Api\SpecialPriceStorageInt * Check that provided price value is not empty and not lower then zero and add error to aggregator if price * contains not valid data. * - * @param float $price + * @param \Magento\Catalog\Api\Data\SpecialPriceInterface $price * @param int $key * @return void */ - private function checkPrice($price, $key) + private function checkPrice(\Magento\Catalog\Api\Data\SpecialPriceInterface $price, $key) { - if (null === $price || $price < 0) { + if (null === $price->getPrice() || $price->getPrice() < 0) { $this->validationResult->addFailedItem( $key, __( - 'Invalid attribute %fieldName = %fieldValue.', - ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue'] + 'Invalid attribute Price = %price. ' + . 'Row ID: SKU = %SKU, Store ID: %storeId, Price From: %priceFrom, Price To: %priceTo.', + [ + 'price' => $price->getPrice(), + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] ), - ['fieldName' => 'Price', 'fieldValue' => $price] + [ + 'price' => $price->getPrice(), + 'SKU' => $price->getSku(), + 'storeId' => $price->getStoreId(), + 'priceFrom' => $price->getPriceFrom(), + 'priceTo' => $price->getPriceTo() + ] ); } } @@ -216,14 +269,14 @@ class SpecialPriceStorage implements \Magento\Catalog\Api\SpecialPriceStorageInt /** * Retrieve SKU by product ID. * - * @param int $id + * @param int $productId * @param array $skus - * @return int|null + * @return string|null */ - private function retrieveSkuById($id, array $skus) + private function retrieveSkuById($productId, array $skus) { foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) { - if (false !== array_key_exists($id, $ids)) { + if (isset($ids[$productId])) { return $sku; } } diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php index b8e78380bbc31bcb1f4c1cc525146f6e4314adef..8f2b16542b1ec6706ea5066408884459cd77bdab 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -259,7 +259,7 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface * * @param array $price * @param array $existingPrices - * @return int|void + * @return int|null */ private function retrievePriceId(array $price, array $existingPrices) { @@ -275,6 +275,8 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface return $existingPrice['value_id']; } } + + return null; } /** diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php index 3cc6f931ec4cd6172f667469e23be42aa272394a..4a25d2603da38c165f0d9bec3a179999a29a16f5 100644 --- a/app/code/Magento/Catalog/Model/ProductIdLocator.php +++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php @@ -95,6 +95,10 @@ class ProductIdLocator implements \Magento\Catalog\Model\ProductIdLocatorInterfa } } - return array_intersect_key($this->idsBySku, array_flip($skus)); + return array_intersect_ukey( + $this->idsBySku, + array_flip($skus), + 'strcasecmp' + ); } } 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 index 78f6c6540b6cb3487711c2d70949b2039ead5b14..a7c82b553365d4c8290e8b459a05a130d9d886eb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php @@ -252,7 +252,7 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase public function testUpdateWithNegativeCost() { $sku = 'sku_1'; - $this->costInterface->expects($this->exactly(3))->method('getSku')->willReturn($sku); + $this->costInterface->expects($this->exactly(5))->method('getSku')->willReturn($sku); $this->invalidSkuChecker ->expects($this->exactly(1)) ->method('retrieveInvalidSkuList') @@ -267,7 +267,7 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase ->with(['attributeCode' => 'cost']) ->willReturn($this->pricePersistence); $this->pricePersistence->expects($this->atLeastOnce())->method('update'); - $this->costInterface->expects($this->exactly(3))->method('getCost')->willReturn(-15); + $this->costInterface->expects($this->exactly(4))->method('getCost')->willReturn(-15); $this->validationResult ->expects($this->atLeastOnce()) ->method('getFailedItems'); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php index 249a3642024592c67d16eb056a78b7efa41f918e..0505d7c1b8175e6ef476d8846be7743c1a3a1181 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php @@ -6,14 +6,23 @@ namespace Magento\Catalog\Test\Unit\Model\Product\Price\Validation; -use Magento\Catalog\Api\Data\TierPriceInterface; - /** - * Class TierPriceValidatorTest. + * Test for \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TierPriceValidatorTest extends \PHPUnit_Framework_TestCase { + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator + */ + private $tierPriceValidator; + /** * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -45,9 +54,9 @@ class TierPriceValidatorTest extends \PHPUnit_Framework_TestCase private $tierPricePersistence; /** - * @var \Magento\Catalog\Api\Data\TierPriceInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Model\Product\Price\Validation\Result|\PHPUnit_Framework_MockObject_MockObject */ - private $tierPriceInterface; + private $validationResult; /** * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker|\PHPUnit_Framework_MockObject_MockObject @@ -55,90 +64,52 @@ class TierPriceValidatorTest extends \PHPUnit_Framework_TestCase private $invalidSkuChecker; /** - * @var \Magento\Catalog\Model\Product\Price\Validation\Result|\PHPUnit_Framework_MockObject_MockObject - */ - private $validationResult; - - /** - * @var \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator + * @var \Magento\Catalog\Api\Data\TierPriceInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $model; + private $tierPrice; /** - * Set up. - * - * @return void + * {@inheritdoc} */ 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->filterBuilder->method('setField')->willReturnSelf(); - $this->filterBuilder->method('setValue')->willReturnSelf(); - $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'] - ); + $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class) + ->setMethods(['retrieveProductIdsBySkus']) + ->disableOriginalConstructor()->getMockForAbstractClass(); + + $this->searchCriteriaBuilder = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteriaBuilder::class) + ->setMethods(['addFilters', 'create']) + ->disableOriginalConstructor()->getMock(); + + $this->filterBuilder = $this->getMockBuilder(\Magento\Framework\Api\FilterBuilder::class) + ->setMethods(['setField', 'setValue', 'create']) + ->disableOriginalConstructor()->getMock(); + + $this->customerGroupRepository = $this->getMockBuilder(\Magento\Customer\Api\GroupRepositoryInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + + $this->websiteRepository = $this->getMockBuilder(\Magento\Store\Api\WebsiteRepositoryInterface::class) + ->setMethods(['getById']) + ->disableOriginalConstructor()->getMockForAbstractClass(); + + $this->tierPricePersistence = $this + ->getMockBuilder(\Magento\Catalog\Model\Product\Price\TierPricePersistence::class) + ->disableOriginalConstructor()->getMock(); + $this->validationResult = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class) - ->disableOriginalConstructor() - ->getMock(); + ->setMethods(['addFailedItem']) + ->disableOriginalConstructor()->getMock(); + $this->invalidSkuChecker = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\InvalidSkuChecker::class) - ->disableOriginalConstructor() - ->getMock(); + ->setMethods(['isSkuListValid', 'retrieveInvalidSkuList']) + ->disableOriginalConstructor()->getMock(); + + $this->tierPrice = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class) + ->setMethods(['getSku', 'getPrice', 'getPriceType', 'getQuantity', 'getWebsiteId']) + ->disableOriginalConstructor()->getMockForAbstractClass(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->model = $objectManager->getObject( + $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->tierPriceValidator = $this->objectManagerHelper->getObject( \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator::class, [ 'productIdLocator' => $this->productIdLocator, @@ -148,186 +119,182 @@ class TierPriceValidatorTest extends \PHPUnit_Framework_TestCase 'websiteRepository' => $this->websiteRepository, 'tierPricePersistence' => $this->tierPricePersistence, 'validationResult' => $this->validationResult, - 'invalidSkuChecker' => $this->invalidSkuChecker, - 'allowedProductTypes' => ['simple', 'virtual', 'bundle', 'downloadable'], + 'invalidSkuChecker' => $this->invalidSkuChecker ] ); } /** - * Test retrieveValidPrices method. + * Prepare CustomerGroupRepository mock. * + * @param array $returned * @return void */ - public function testRetrieveValidPrices() + private function prepareCustomerGroupRepositoryMock(array $returned) { - $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->productIdLocator->expects($this->once())->method('retrieveProductIdsBySkus')->willReturn($idsBySku); - $productPrice = 15; - $this->tierPriceInterface->expects($this->exactly(10))->method('getSku')->willReturn($sku); - $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]); - $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->retrieveValidationResult([$this->tierPriceInterface], []); + $searchCriteria = $this + ->getMockBuilder(\Magento\Framework\Api\Search\SearchCriteriaInterface::class) + ->disableOriginalConstructor()->getMock(); + + $filter = $this->getMockBuilder(\Magento\Framework\Api\AbstractSimpleObject::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + + $this->filterBuilder->expects($this->atLeastOnce())->method('setField')->willReturnSelf(); + $this->filterBuilder->expects($this->atLeastOnce())->method('setValue')->willReturnSelf(); + $this->filterBuilder->expects($this->atLeastOnce())->method('create')->willReturn($filter); + + $this->searchCriteriaBuilder->expects($this->atLeastOnce())->method('addFilters')->willReturnSelf(); + $this->searchCriteriaBuilder->expects($this->atLeastOnce())->method('create')->willReturn($searchCriteria); + + $customerGroupSearchResults = $this + ->getMockBuilder(\Magento\Customer\Api\Data\GroupSearchResultsInterface::class) + ->disableOriginalConstructor()->getMock(); + $customerGroupSearchResults->expects($this->once())->method('getItems') + ->willReturn($returned['customerGroupSearchResults_getItems']); + + $this->customerGroupRepository->expects($this->atLeastOnce())->method('getList') + ->willReturn($customerGroupSearchResults); } /** - * Test retrieveValidPrices method with downloadable product. + * Prepare retrieveValidationResult(). + * + * @param string $sku + * @param array $returned + * @return void */ - public function testRetrieveValidPricesWithDownloadableProduct() + private function prepareRetrieveValidationResultMethod($sku, array $returned) { + $this->tierPrice->expects($this->atLeastOnce())->method('getSku')->willReturn($sku); + $tierPriceValue = 104; + $this->tierPrice->expects($this->atLeastOnce())->method('getPrice')->willReturn($tierPriceValue); + $this->tierPrice->expects($this->atLeastOnce())->method('getPriceType') + ->willReturn($returned['tierPrice_getPriceType']); + $qty = 0; + $this->tierPrice->expects($this->atLeastOnce())->method('getQuantity')->willReturn($qty); + $websiteId = 0; + $invalidWebsiteId = 4; + $this->tierPrice->expects($this->atLeastOnce())->method('getWebsiteId') + ->willReturnOnConsecutiveCalls($websiteId, $websiteId, $websiteId, $invalidWebsiteId, $websiteId); + $this->tierPrice->expects($this->atLeastOnce())->method('getCustomerGroup') + ->willReturn($returned['tierPrice_getCustomerGroup']); + + $skuDiff = [$sku]; + $this->invalidSkuChecker->expects($this->atLeastOnce())->method('retrieveInvalidSkuList')->willReturn($skuDiff); + + $productId = 3346346; + $productType = \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE; $idsBySku = [ - 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE], - 'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], + $sku => [$productId => $productType] ]; - $this->productIdLocator->expects($this->once())->method('retrieveProductIdsBySkus')->willReturn($idsBySku); - $this->tierPriceInterface->expects($this->exactly(10))->method('getSku')->willReturn('sku_1'); - $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]); - $productPrice = 15; - $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->validationResult - ->expects($this->never()) - ->method('addFailedItem'); - $this->model->retrieveValidationResult([$this->tierPriceInterface], []); + $this->productIdLocator->expects($this->atLeastOnce())->method('retrieveProductIdsBySkus') + ->willReturn($idsBySku); } /** - * Test method call with invalid values. + * Test for validateSkus(). + * + * @return void */ - public function testRetrieveValidPricesWithInvalidCall() + public function testValidateSkus() { - $idsBySku = [ - 'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE], - 'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL], - 'invalid' => [3 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE], - ]; + $skus = ['SDFS234234']; - $this->tierPriceInterface->expects($this->exactly(10))->method('getSku')->willReturn('sku_1'); - $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn(['invalid']); - $this->productIdLocator->expects($this->once())->method('retrieveProductIdsBySkus')->willReturn($idsBySku); - $this->validationResult - ->expects($this->exactly(5)) - ->method('addFailedItem'); - $this->tierPriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn('-90'); - $this->tierPriceInterface->expects($this->exactly(2))->method('getPriceType')->willReturn('unknown'); - $this->tierPriceInterface->expects($this->exactly(4))->method('getQuantity')->willReturn('-90'); - $this->websiteRepository->method('getById') - ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); - $searchCriteria = $this->getMockForAbstractClass( - \Magento\Framework\Api\SearchCriteriaInterface::class, - [], - '', - false, - true, - true, - ['create'] - ); - $searchCriteria->method('create')->willReturnSelf(); - $this->searchCriteriaBuilder->method('addFilters')->willReturn($searchCriteria); - $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')->willReturnSelf(); - $searchResults = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\GroupSearchResultsInterface::class, - [], - '', - false, - true, - true, - ['getItems'] - ); - $this->filterBuilder->expects($this->atLeastOnce())->method('create')->willReturnSelf(); - $searchResults->expects($this->atLeastOnce())->method('getItems')->willReturn([]); - $this->customerGroupRepository - ->expects($this->atLeastOnce()) - ->method('getList') - ->willReturn($searchResults); - $this->model->retrieveValidationResult([$this->tierPriceInterface], []); + $this->invalidSkuChecker->expects($this->atLeastOnce())->method('isSkuListValid'); + + $expected = null; + $this->assertEquals($expected, $this->tierPriceValidator->validateSkus($skus)); } /** - * Check website. + * Test for retrieveValidationResult(). * - * @param \PHPUnit_Framework_MockObject_MockObject $price + * @param array $returned + * @dataProvider retrieveValidationResultDataProvider + * @return void */ - private function checkWebsite(\PHPUnit_Framework_MockObject_MockObject $price) + public function testRetrieveValidationResult(array $returned) { - $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); + $sku = 'ASDF234234'; + + $prices = [$this->tierPrice]; + $existingPrices = [$this->tierPrice]; + + $this->prepareRetrieveValidationResultMethod($sku, $returned); + + $website = $this->getMockBuilder(\Magento\Store\Api\Data\WebsiteInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $this->websiteRepository->expects($this->atLeastOnce())->method('getById')->willReturn($website); + + $this->prepareCustomerGroupRepositoryMock($returned); + + $expects = $this->validationResult; + $this->assertEquals($expects, $this->tierPriceValidator->retrieveValidationResult($prices, $existingPrices)); } /** - * Check group. + * Data provider for retrieveValidationResult() test. * - * @param \PHPUnit_Framework_MockObject_MockObject $price + * @return array */ - private function checkGroup(\PHPUnit_Framework_MockObject_MockObject $price) + public function retrieveValidationResultDataProvider() { - $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'] - ); + $customerGroupName = 'test_Group'; + + $customerGroup = $this->getMockBuilder(\Magento\Customer\Api\Data\GroupInterface::class) + ->setMethods(['getCode', 'getId']) + ->disableOriginalConstructor()->getMockForAbstractClass(); + + $customerGroup->expects($this->atLeastOnce())->method('getCode')->willReturn($customerGroupName); + $customerGroupId = 23; + $customerGroup->expects($this->atLeastOnce())->method('getId')->willReturn($customerGroupId); + + return [ + [ + [ + 'tierPrice_getCustomerGroup' => $customerGroupName, + 'tierPrice_getPriceType' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT, + 'customerGroupSearchResults_getItems' => [$customerGroup] + ] + ], + [ + [ + 'tierPrice_getCustomerGroup' => $customerGroupName, + 'tierPrice_getPriceType' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED, + 'customerGroupSearchResults_getItems' => [] + ] + ] + ]; + } + + /** + * Test for retrieveValidationResult() with Exception. + * + * @return void + */ + public function testRetrieveValidationResultWithException() + { + $sku = 'ASDF234234'; + $customerGroupName = 'test_Group'; + + $prices = [$this->tierPrice]; + $existingPrices = [$this->tierPrice]; + + $returned = [ + 'tierPrice_getPriceType' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT, + 'customerGroupSearchResults_getItems' => [], + 'tierPrice_getCustomerGroup' => $customerGroupName, + ]; + + $this->prepareRetrieveValidationResultMethod($sku, $returned); + + $exception = new \Magento\Framework\Exception\NoSuchEntityException(); + + $this->websiteRepository->expects($this->atLeastOnce())->method('getById')->willThrowException($exception); + + $this->prepareCustomerGroupRepositoryMock($returned); - $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); + $expects = $this->validationResult; + $this->assertEquals($expects, $this->tierPriceValidator->retrieveValidationResult($prices, $existingPrices)); } } diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml index a4ce77aa0db0226332905ae1cb8df5d744a03dcc..fcd8b838c1b5d24467d90bcbfd9c2e1dd7435922 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml @@ -16,26 +16,28 @@ </strong> </div> <div class="fieldset-wrapper-content in collapse" id="manage-titles-content"> - <fieldset class="fieldset"> - <table class="admin__control-table" id="attribute-labels-table"> - <thead> - <tr> - <?php foreach ($block->getStores() as $_store): ?> - <th class="col-store-view"><?php /* @escapeNotVerified */ echo $_store->getName() ?></th> - <?php endforeach; ?> - </tr> - </thead> - <tbody> - <tr> - <?php $_labels = $block->getLabelValues() ?> - <?php foreach ($block->getStores() as $_store): ?> - <td class="col-store-view"> - <input class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option<?php endif; ?>" type="text" name="frontend_label[<?php /* @escapeNotVerified */ echo $_store->getId() ?>]" value="<?php echo $block->escapeHtml($_labels[$_store->getId()]) ?>"<?php if ($block->getReadOnly()):?> disabled="disabled"<?php endif;?>/> - </td> - <?php endforeach; ?> - </tr> - </tbody> - </table> + <fieldset class="admin__fieldset fieldset"> + <div class="admin__control-table-wrapper"> + <table class="admin__control-table" id="attribute-labels-table"> + <thead> + <tr> + <?php foreach ($block->getStores() as $_store): ?> + <th class="col-store-view"><?php /* @escapeNotVerified */ echo $_store->getName() ?></th> + <?php endforeach; ?> + </tr> + </thead> + <tbody> + <tr> + <?php $_labels = $block->getLabelValues() ?> + <?php foreach ($block->getStores() as $_store): ?> + <td class="col-store-view"> + <input class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option<?php endif; ?>" type="text" name="frontend_label[<?php /* @escapeNotVerified */ echo $_store->getId() ?>]" value="<?php echo $block->escapeHtml($_labels[$_store->getId()]) ?>"<?php if ($block->getReadOnly()):?> disabled="disabled"<?php endif;?>/> + </td> + <?php endforeach; ?> + </tr> + </tbody> + </table> + </div> </fieldset> </div> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml index 932d3dc50dc7f879453a1ef0671140c553017742..1f1dc0925f2c0199c49d3e244fd69d07eb8a697f 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml @@ -10,11 +10,11 @@ $stores = $block->getStoresSortedBySortOrder(); ?> -<fieldset class="fieldset"> +<fieldset class="admin__fieldset fieldset"> <legend class="legend"> <span><?php echo $block->escapeHtml(__('Manage Options (Values of Your Attribute)')); ?></span> - </legend> - <div id="manage-options-panel" data-index="attribute_options_select_container"> + </legend><br /> + <div class="admin__control-table-wrapper" id="manage-options-panel" data-index="attribute_options_select_container"> <table class="admin__control-table" data-index="attribute_options_select"> <thead> <tr id="attribute-options-table"> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index f7475d0bb816828dd3c214600e8eeb48d03e39a8..c2aa0b8c6962258f253241d440703067b9c196e3 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -23,6 +23,9 @@ <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item> <item name="update_url" xsi:type="url" path="mui/index/render"/> + <item name="storageConfig" xsi:type="array"> + <item name="dataScope" xsi:type="string">filters.store_id</item> + </item> </item> </argument> </argument> diff --git a/app/code/Magento/CatalogInventory/Block/Plugin/ProductView.php b/app/code/Magento/CatalogInventory/Block/Plugin/ProductView.php index e9ca9c5ca646cb0f2ad106e0b1e084eff479671d..fb988ddde594a7d26cc9f0bfeb7f649397bb60f2 100644 --- a/app/code/Magento/CatalogInventory/Block/Plugin/ProductView.php +++ b/app/code/Magento/CatalogInventory/Block/Plugin/ProductView.php @@ -38,7 +38,7 @@ class ProductView ); $params = []; - $params['minAllowed'] = max((float)$stockItem->getQtyMinAllowed(), 1); + $params['minAllowed'] = (float)$stockItem->getMinSaleQty(); if ($stockItem->getQtyMaxAllowed()) { $params['maxAllowed'] = $stockItem->getQtyMaxAllowed(); } diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Block/Plugin/ProductViewTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Block/Plugin/ProductViewTest.php index c0ba42d1981ca2a12f3ea6aa063854ce465461bb..267ff26bb0ccbf7d4e8b84d8a4b09c256c5123d1 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Block/Plugin/ProductViewTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Block/Plugin/ProductViewTest.php @@ -28,7 +28,7 @@ class ProductViewTest extends \PHPUnit_Framework_TestCase $this->stockItem = $this->getMockBuilder(\Magento\CatalogInventory\Model\Stock\Item::class) ->disableOriginalConstructor() - ->setMethods(['getQtyMinAllowed', 'getQtyMaxAllowed', 'getQtyIncrements']) + ->setMethods(['getMinSaleQty', 'getQtyMaxAllowed', 'getQtyIncrements']) ->getMock(); $this->stockRegistry = $this->getMockBuilder(\Magento\CatalogInventory\Api\StockRegistryInterface::class) @@ -47,7 +47,7 @@ class ProductViewTest extends \PHPUnit_Framework_TestCase $result = [ 'validate-item-quantity' => [ - 'minAllowed' => 2, + 'minAllowed' => 0.5, 'maxAllowed' => 5, 'qtyIncrements' => 3 ] @@ -73,7 +73,7 @@ class ProductViewTest extends \PHPUnit_Framework_TestCase ->method('getStockItem') ->with('productId', 'websiteId') ->willReturn($this->stockItem); - $this->stockItem->expects($this->once())->method('getQtyMinAllowed')->willReturn(2); + $this->stockItem->expects($this->once())->method('getMinSaleQty')->willReturn(0.5); $this->stockItem->expects($this->any())->method('getQtyMaxAllowed')->willReturn(5); $this->stockItem->expects($this->any())->method('getQtyIncrements')->willReturn(3); diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml index 31a5dcea3a18f8b41eff7215fbfcdd499d5d4dac..4b486cd346bde1514dccdd27f0e9ac89ed9f8de4 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml +++ b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml @@ -179,7 +179,6 @@ <item name="dataScope" xsi:type="string">min_sale_qty</item> <item name="validation" xsi:type="array"> <item name="validate-number" xsi:type="boolean">true</item> - <item name="validate-digits" xsi:type="boolean">true</item> </item> <item name="sortOrder" xsi:type="string">0</item> <item name="value" xsi:type="object">Magento\CatalogInventory\Model\Source\StockConfiguration</item> diff --git a/app/code/Magento/Checkout/Model/Cart.php b/app/code/Magento/Checkout/Model/Cart.php index 0e8df9e08f11e82ddf68896b48254fb673eacb1d..5d7cd9e51a862897e445dba02b2ad1d8957cf001 100644 --- a/app/code/Magento/Checkout/Model/Cart.php +++ b/app/code/Magento/Checkout/Model/Cart.php @@ -358,11 +358,10 @@ class Cart extends DataObject implements CartInterface if ($productId) { $stockItem = $this->stockRegistry->getStockItem($productId, $product->getStore()->getWebsiteId()); $minimumQty = $stockItem->getMinSaleQty(); - //If product was not found in cart and there is set minimal qty for it + //If product quantity is not specified in request and there is set minimal qty for it if ($minimumQty && $minimumQty > 0 && !$request->getQty() - && !$this->getQuote()->hasProductId($productId) ) { $request->setQty($minimumQty); } diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 10f969db430f7aed0759cc4e14fbec63870223fa..d709e9667eac34d0cadfe55554da18d3311b50e4 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -145,9 +145,13 @@ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInte public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $passwordHash = null) { $prevCustomerData = null; + $prevCustomerDataArr = null; if ($customer->getId()) { $prevCustomerData = $this->getById($customer->getId()); + $prevCustomerDataArr = $prevCustomerData->__toArray(); } + /** @var $customer \Magento\Customer\Model\Data\Customer */ + $customerArr = $customer->__toArray(); $customer = $this->imageProcessor->save( $customer, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, @@ -185,6 +189,20 @@ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInte $customerModel->setRpToken(null); $customerModel->setRpTokenCreatedAt(null); } + if (!array_key_exists('default_billing', $customerArr) && + null !== $prevCustomerDataArr && + array_key_exists('default_billing', $prevCustomerDataArr) + ) { + $customerModel->setDefaultBilling($prevCustomerDataArr['default_billing']); + } + + if (!array_key_exists('default_shipping', $customerArr) && + null !== $prevCustomerDataArr && + array_key_exists('default_shipping', $prevCustomerDataArr) + ) { + $customerModel->setDefaultShipping($prevCustomerDataArr['default_shipping']); + } + $customerModel->save(); $this->customerRegistry->push($customerModel); $customerId = $customerModel->getId(); diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index 54a0b8040d2db35521010102a3c896a58ac3f58d..3f6dd14f5aa1842637653004b7f91d94ca62abe0 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -101,13 +101,8 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase $this->getMock(\Magento\Customer\Model\ResourceModel\Customer::class, [], [], '', false); $this->customerRegistry = $this->getMock(\Magento\Customer\Model\CustomerRegistry::class, [], [], '', false); $this->dataObjectHelper = $this->getMock(\Magento\Framework\Api\DataObjectHelper::class, [], [], '', false); - $this->customerFactory = $this->getMock( - \Magento\Customer\Model\CustomerFactory::class, - ['create'], - [], - '', - false - ); + $this->customerFactory = + $this->getMock(\Magento\Customer\Model\CustomerFactory::class, ['create'], [], '', false); $this->customerSecureFactory = $this->getMock( \Magento\Customer\Model\Data\CustomerSecureFactory::class, ['create'], @@ -115,7 +110,6 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase '', false ); - $this->addressRepository = $this->getMock( \Magento\Customer\Model\ResourceModel\AddressRepository::class, [], @@ -123,7 +117,6 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase '', false ); - $this->customerMetadata = $this->getMockForAbstractClass( \Magento\Customer\Api\CustomerMetadataInterface::class, [], @@ -172,11 +165,15 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase \Magento\Customer\Api\Data\CustomerInterface::class, [], '', - false + true, + true, + true, + [ + '__toArray' + ] ); $this->collectionProcessorMock = $this->getMockBuilder(CollectionProcessorInterface::class) ->getMock(); - $this->model = new \Magento\Customer\Model\ResourceModel\CustomerRepository( $this->customerFactory, $this->customerSecureFactory, @@ -254,6 +251,11 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase '', false ); + + $this->customer->expects($this->atLeastOnce()) + ->method('__toArray') + ->willReturn(['default_billing', 'default_shipping']); + $customerAttributesMetaData = $this->getMockForAbstractClass( \Magento\Framework\Api\CustomAttributesDataInterface::class, [], @@ -495,6 +497,11 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase 'getId' ] ); + + $this->customer->expects($this->atLeastOnce()) + ->method('__toArray') + ->willReturn(['default_billing', 'default_shipping']); + $customerModel = $this->getMock( \Magento\Customer\Model\Customer::class, [ diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index e46f9384bdfda1e9c79260d360aa3e29089f6ceb..b83cc0c8cf2f587dd4368496a04ebebd70bf4df0 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -395,6 +395,8 @@ class Multishipping extends \Magento\Framework\DataObject } } + $this->prepareShippingAssignment($quote); + /** * Delete all not virtual quote items which are not added to shipping address * MultishippingQty should be defined for each quote item when it processed with _addShippingItem diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php index a8930f1d0d58d4c392091a0ecd97c4262c8bf7e9..24a6f32b37a8d3d81c740427ca82775975e376da 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php @@ -5,130 +5,148 @@ */ namespace Magento\Multishipping\Test\Unit\Model\Checkout\Type; +use Magento\Checkout\Model\Session; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\AddressSearchResultsInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Data\Address; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Framework\Session\Generic; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Multishipping\Helper\Data; +use Magento\Multishipping\Model\Checkout\Type\Multishipping; +use Magento\Payment\Model\Method\SpecificationInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartExtension; +use Magento\Quote\Api\Data\CartExtensionFactory; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address as QuoteAddress; +use Magento\Quote\Model\Quote\Address\Item as AddressItem; +use Magento\Quote\Model\Quote\Address\ToOrder; +use Magento\Quote\Model\Quote\Address\ToOrderAddress; +use Magento\Quote\Model\Quote\AddressFactory; +use Magento\Quote\Model\Quote\Item; +use Magento\Quote\Model\Quote\Item\ToOrderItem; +use Magento\Quote\Model\Quote\Payment\ToOrderPayment; +use Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentProcessor; +use Magento\Quote\Model\Quote\TotalsCollector; +use Magento\Quote\Model\Shipping; +use Magento\Quote\Model\ShippingAssignment; +use Magento\Sales\Model\Order\Email\Sender\OrderSender; +use Magento\Sales\Model\OrderFactory; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit_Framework_MockObject_MockObject; +use PHPUnit_Framework_TestCase; + /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class MultishippingTest extends \PHPUnit_Framework_TestCase +class MultishippingTest extends PHPUnit_Framework_TestCase { /** - * @var \Magento\Multishipping\Model\Checkout\Type\Multishipping + * @var Multishipping */ protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $checkoutSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $customerSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $customerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $quoteMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $helperMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $filterBuilderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $addressRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $searchCriteriaBuilderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ protected $totalsCollectorMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ private $cartExtensionFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ private $shippingAssignmentProcessorMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var PHPUnit_Framework_MockObject_MockObject */ private $quoteRepositoryMock; protected function setUp() { - $this->checkoutSessionMock = $this->getMock(\Magento\Checkout\Model\Session::class, [], [], '', false); - $this->customerSessionMock = $this->getMock(\Magento\Customer\Model\Session::class, [], [], '', false); - $orderFactoryMock = $this->getMock(\Magento\Sales\Model\OrderFactory::class, [], [], '', false); - $eventManagerMock = $this->getMock(\Magento\Framework\Event\ManagerInterface::class, [], [], '', false); - $scopeConfigMock = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class, [], [], '', false); - $sessionMock = $this->getMock(\Magento\Framework\Session\Generic::class, [], [], '', false); - $addressFactoryMock = $this->getMock(\Magento\Quote\Model\Quote\AddressFactory::class, [], [], '', false); - $toOrderMock = $this->getMock(\Magento\Quote\Model\Quote\Address\ToOrder::class, [], [], '', false); - $toOrderAddressMock = - $this->getMock(\Magento\Quote\Model\Quote\Address\ToOrderAddress::class, [], [], '', false); - $toOrderPaymentMock = - $this->getMock(\Magento\Quote\Model\Quote\Payment\ToOrderPayment::class, [], [], '', false); - $toOrderItemMock = $this->getMock(\Magento\Quote\Model\Quote\Item\ToOrderItem::class, [], [], '', false); - $storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class, [], [], '', false); - $paymentSpecMock = - $this->getMock(\Magento\Payment\Model\Method\SpecificationInterface::class, [], [], '', false); - $this->helperMock = $this->getMock(\Magento\Multishipping\Helper\Data::class, [], [], '', false); - $orderSenderMock = - $this->getMock(\Magento\Sales\Model\Order\Email\Sender\OrderSender::class, [], [], '', false); - $priceMock = $this->getMock(\Magento\Framework\Pricing\PriceCurrencyInterface::class, [], [], '', false); - $this->quoteRepositoryMock = $this->getMock(\Magento\Quote\Api\CartRepositoryInterface::class); - $this->filterBuilderMock = $this->getMock(\Magento\Framework\Api\FilterBuilder::class, [], [], '', false); - $this->searchCriteriaBuilderMock = $this->getMock( - \Magento\Framework\Api\SearchCriteriaBuilder::class, - [], - [], - '', - false - ); - $this->addressRepositoryMock = $this->getMock( - \Magento\Customer\Api\AddressRepositoryInterface::class, - [], - [], - '', - false - ); + $this->checkoutSessionMock = $this->createSimpleMock(Session::class); + $this->customerSessionMock = $this->createSimpleMock(CustomerSession::class); + $orderFactoryMock = $this->createSimpleMock(OrderFactory::class); + $eventManagerMock = $this->createSimpleMock(ManagerInterface::class); + $scopeConfigMock = $this->createSimpleMock(ScopeConfigInterface::class); + $sessionMock = $this->createSimpleMock(Generic::class); + $addressFactoryMock = $this->createSimpleMock(AddressFactory::class); + $toOrderMock = $this->createSimpleMock(ToOrder::class); + $toOrderAddressMock = $this->createSimpleMock(ToOrderAddress::class); + $toOrderPaymentMock = $this->createSimpleMock(ToOrderPayment::class); + $toOrderItemMock = $this->createSimpleMock(ToOrderItem::class); + $storeManagerMock = $this->createSimpleMock(StoreManagerInterface::class); + $paymentSpecMock = $this->createSimpleMock(SpecificationInterface::class); + $this->helperMock = $this->createSimpleMock(Data::class); + $orderSenderMock = $this->createSimpleMock(OrderSender::class); + $priceMock = $this->createSimpleMock(PriceCurrencyInterface::class); + $this->quoteRepositoryMock = $this->createSimpleMock(CartRepositoryInterface::class); + $this->filterBuilderMock = $this->createSimpleMock(FilterBuilder::class); + $this->searchCriteriaBuilderMock = $this->createSimpleMock(SearchCriteriaBuilder::class); + $this->addressRepositoryMock = $this->createSimpleMock(AddressRepositoryInterface::class); /** This is used to get past _init() which is called in construct. */ $data['checkout_session'] = $this->checkoutSessionMock; - $this->quoteMock = $this->getMock(\Magento\Quote\Model\Quote::class, [], [], '', false); - $this->customerMock = $this->getMock(\Magento\Customer\Api\Data\CustomerInterface::class, [], [], '', false); + $this->quoteMock = $this->createSimpleMock(Quote::class); + $this->customerMock = $this->createSimpleMock(CustomerInterface::class); $this->customerMock->expects($this->atLeastOnce())->method('getId')->willReturn(null); $this->checkoutSessionMock->expects($this->atLeastOnce())->method('getQuote')->willReturn($this->quoteMock); $this->customerSessionMock->expects($this->atLeastOnce())->method('getCustomerDataObject') ->willReturn($this->customerMock); - $this->totalsCollectorMock = $this->getMock( - \Magento\Quote\Model\Quote\TotalsCollector::class, - [], - [], - '', - false - ); - $this->model = new \Magento\Multishipping\Model\Checkout\Type\Multishipping( + $this->totalsCollectorMock = $this->createSimpleMock(TotalsCollector::class); + $this->model = new Multishipping( $this->checkoutSessionMock, $this->customerSessionMock, $orderFactoryMock, @@ -152,21 +170,14 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase $this->totalsCollectorMock, $data ); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->cartExtensionFactoryMock = $this->getMock( - \Magento\Quote\Api\Data\CartExtensionFactory::class, - ['create'], - [], - '', - false - ); - $this->shippingAssignmentProcessorMock = $this->getMock( - \Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentProcessor::class, - [], - [], - '', - false - ); + + $this->cartExtensionFactoryMock = $this->getMockBuilder(CartExtensionFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->shippingAssignmentProcessorMock = $this->createSimpleMock(ShippingAssignmentProcessor::class); + + $objectManager = new ObjectManager($this); $objectManager->setBackwardCompatibleProperty( $this->model, 'cartExtensionFactory', @@ -204,23 +215,21 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase $this->filterBuilderMock->expects($this->atLeastOnce())->method('setConditionType')->willReturnSelf(); $this->filterBuilderMock->expects($this->atLeastOnce())->method('create')->willReturnSelf(); - $searchCriteriaMock = $this->getMock(\Magento\Framework\Api\SearchCriteria::class, [], [], '', false); + $searchCriteriaMock = $this->createSimpleMock(SearchCriteria::class); $this->searchCriteriaBuilderMock->expects($this->atLeastOnce())->method('addFilters')->willReturnSelf(); - $this->searchCriteriaBuilderMock->expects($this->atLeastOnce())->method('create') + $this->searchCriteriaBuilderMock + ->expects($this->atLeastOnce()) + ->method('create') ->willReturn($searchCriteriaMock); - $resultMock = $this->getMock( - \Magento\Customer\Api\Data\AddressSearchResultsInterface::class, - [], - [], - '', - false - ); + $resultMock = $this->createSimpleMock(AddressSearchResultsInterface::class); $this->addressRepositoryMock->expects($this->atLeastOnce())->method('getList')->willReturn($resultMock); - $addressItemMock = $this->getMock(\Magento\Customer\Api\Data\AddressInterface::class, [], [], '', false); + $addressItemMock = $this->createSimpleMock(AddressInterface::class); $resultMock->expects($this->atLeastOnce())->method('getItems')->willReturn([$addressItemMock]); $addressItemMock->expects($this->atLeastOnce())->method('getId')->willReturn(null); + $this->mockShippingAssignment(); + $this->assertEquals($this->model, $this->model->setShippingItemsInformation($info)); } @@ -240,11 +249,11 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase ]; $customerAddressId = 42; - $customerAddressMock = $this->getMock(\Magento\Customer\Model\Data\Address::class, [], [], '', false); - $customerAddressMock->expects($this->atLeastOnce())->method('getId')->willReturn($customerAddressId); - $customerAddresses = [$customerAddressMock]; + $customerAddresses = [ + $this->getCustomerAddressMock($customerAddressId) + ]; - $quoteItemMock = $this->getMock(\Magento\Quote\Model\Quote\Item::class, [], [], '', false); + $quoteItemMock = $this->createSimpleMock(Item::class); $this->quoteMock->expects($this->once())->method('getItemById')->willReturn($quoteItemMock); $this->quoteMock->expects($this->once())->method('getAllShippingAddresses')->willReturn([]); @@ -260,11 +269,11 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase $addressId = 42; $customerAddressId = 42; - $customerAddressMock = $this->getMock(\Magento\Customer\Model\Data\Address::class, [], [], '', false); - $customerAddressMock->expects($this->atLeastOnce())->method('getId')->willReturn($customerAddressId); - $customerAddresses = [$customerAddressMock]; - $this->customerMock->expects($this->once())->method('getAddresses')->willReturn($customerAddresses); + $customerAddresses = [ + $this->getCustomerAddressMock($customerAddressId) + ]; + $this->customerMock->expects($this->once())->method('getAddresses')->willReturn($customerAddresses); $this->addressRepositoryMock->expects($this->once())->method('getById')->willReturn(null); $this->assertEquals($this->model, $this->model->updateQuoteCustomerShippingAddress($addressId)); @@ -279,9 +288,9 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase $addressId = 43; $customerAddressId = 42; - $customerAddressMock = $this->getMock(\Magento\Customer\Model\Data\Address::class, [], [], '', false); - $customerAddressMock->expects($this->atLeastOnce())->method('getId')->willReturn($customerAddressId); - $customerAddresses = [$customerAddressMock]; + $customerAddresses = [ + $this->getCustomerAddressMock($customerAddressId) + ]; $this->customerMock->expects($this->once())->method('getAddresses')->willReturn($customerAddresses); $this->assertEquals($this->model, $this->model->updateQuoteCustomerShippingAddress($addressId)); @@ -292,9 +301,9 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase $addressId = 42; $customerAddressId = 42; - $customerAddressMock = $this->getMock(\Magento\Customer\Model\Data\Address::class, [], [], '', false); - $customerAddressMock->expects($this->atLeastOnce())->method('getId')->willReturn($customerAddressId); - $customerAddresses = [$customerAddressMock]; + $customerAddresses = [ + $this->getCustomerAddressMock($customerAddressId) + ]; $this->customerMock->expects($this->once())->method('getAddresses')->willReturn($customerAddresses); $this->assertEquals($this->model, $this->model->setQuoteCustomerBillingAddress($addressId)); @@ -309,9 +318,9 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase $addressId = 43; $customerAddressId = 42; - $customerAddressMock = $this->getMock(\Magento\Customer\Model\Data\Address::class, [], [], '', false); - $customerAddressMock->expects($this->atLeastOnce())->method('getId')->willReturn($customerAddressId); - $customerAddresses = [$customerAddressMock]; + $customerAddresses = [ + $this->getCustomerAddressMock($customerAddressId) + ]; $this->customerMock->expects($this->once())->method('getAddresses')->willReturn($customerAddresses); $this->assertEquals($this->model, $this->model->setQuoteCustomerBillingAddress($addressId)); @@ -319,7 +328,9 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase public function testGetQuoteShippingAddressesItems() { - $quoteItem = $this->getMock(\Magento\Quote\Model\Quote\Address\Item::class, [], [], '', false); + $quoteItem = $this->getMockBuilder(AddressItem::class) + ->disableOriginalConstructor() + ->getMock(); $this->checkoutSessionMock->expects($this->once())->method('getQuote')->willReturn($this->quoteMock); $this->quoteMock->expects($this->once())->method('getShippingAddressesItems')->willReturn($quoteItem); $this->model->getQuoteShippingAddressesItems(); @@ -327,57 +338,113 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase public function testSetShippingMethods() { - $methodsArray = [1 => 'flatrate_flatrate', 2 => 'tablerate_bestway']; $addressId = 1; - $addressMock = $this->getMock( - \Magento\Quote\Model\Quote\Address::class, - ['getId', 'setShippingMethod'], - [], - '', - false - ); - $extensionAttributesMock = $this->getMock( - \Magento\Quote\Api\Data\CartExtension::class, - ['setShippingAssignments'], - [], - '', - false - ); - $shippingAssignmentMock = $this->getMock( - \Magento\Quote\Model\ShippingAssignment::class, - ['getShipping', 'setShipping'], - [], - '', - false - ); - $shippingMock = $this->getMock(\Magento\Quote\Model\Shipping::class, ['setMethod'], [], '', false); + $addressMock = $this->getMockBuilder(QuoteAddress::class) + ->setMethods(['getId', 'setShippingMethod']) + ->disableOriginalConstructor() + ->getMock(); $addressMock->expects($this->once())->method('getId')->willReturn($addressId); $this->quoteMock->expects($this->once())->method('getAllShippingAddresses')->willReturn([$addressMock]); $addressMock->expects($this->once())->method('setShippingMethod')->with($methodsArray[$addressId]); - //mock for prepareShippingAssignment - $this->quoteMock + + $this->mockShippingAssignment(); + + //save mock + $this->quoteMock->expects($this->once())->method('collectTotals')->willReturnSelf(); + $this->quoteRepositoryMock->expects($this->once())->method('save')->with($this->quoteMock); + $this->model->setShippingMethods($methodsArray); + } + + /** + * @param ShippingAssignment $shippingAssignmentMock + * @return CartExtension|PHPUnit_Framework_MockObject_MockObject + */ + private function getExtensionAttributesMock(ShippingAssignment $shippingAssignmentMock) + { + $extensionAttributesMock = $this->getMockBuilder(CartExtension::class) + ->setMethods(['setShippingAssignments']) + ->getMock(); + + $extensionAttributesMock ->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($extensionAttributesMock); + ->method('setShippingAssignments') + ->with([$shippingAssignmentMock]) + ->willReturnSelf(); + + return $extensionAttributesMock; + } + + /** + * @return ShippingAssignment | PHPUnit_Framework_MockObject_MockObject + */ + private function getShippingAssignmentMock() + { + $shippingAssignmentMock = $this->getMockBuilder(ShippingAssignment::class) + ->disableOriginalConstructor() + ->setMethods(['getShipping', 'setShipping']) + ->getMock(); + $shippingMock = $this->getMockBuilder(Shipping::class) + ->disableOriginalConstructor() + ->setMethods(['setMethod']) + ->getMock(); + + $shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn($shippingMock); + $shippingMock->expects($this->once())->method('setMethod')->with(null)->willReturnSelf(); + $shippingAssignmentMock->expects($this->once())->method('setShipping')->with($shippingMock); + + return $shippingAssignmentMock; + } + + private function mockShippingAssignment() + { + $shippingAssignmentMock = $this->getShippingAssignmentMock(); + + $extensionAttributesMock = $this->getExtensionAttributesMock($shippingAssignmentMock); + $this->shippingAssignmentProcessorMock ->expects($this->once()) ->method('create') ->with($this->quoteMock) ->willReturn($shippingAssignmentMock); - $shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn($shippingMock); - $shippingMock->expects($this->once())->method('setMethod')->with(null)->willReturnSelf(); - $shippingAssignmentMock->expects($this->once())->method('setShipping')->with($shippingMock); - $extensionAttributesMock + + $this->quoteMock ->expects($this->once()) - ->method('setShippingAssignments') - ->with([$shippingAssignmentMock]) - ->willReturnSelf(); - $this->quoteMock->expects($this->once())->method('setExtensionAttributes')->with($extensionAttributesMock); - //save mock - $this->quoteMock->expects($this->once())->method('collectTotals')->willReturnSelf(); - $this->quoteRepositoryMock->expects($this->once())->method('save')->with($this->quoteMock); - $this->model->setShippingMethods($methodsArray); + ->method('getExtensionAttributes') + ->willReturn($extensionAttributesMock); + + $this->quoteMock + ->expects($this->once()) + ->method('setExtensionAttributes') + ->with($extensionAttributesMock); + } + + /** + * @param $customerAddressId + * @return Address | PHPUnit_Framework_MockObject_MockObject + */ + private function getCustomerAddressMock($customerAddressId) + { + $customerAddressMock = $this->getMockBuilder(Address::class) + ->setMethods(['getId']) + ->disableOriginalConstructor() + ->getMock(); + $customerAddressMock + ->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn($customerAddressId); + return $customerAddressMock; + } + + /** + * @param string $className + * @return PHPUnit_Framework_MockObject_MockObject + */ + private function createSimpleMock($className) + { + return $this->getMockBuilder($className) + ->disableOriginalConstructor() + ->getMock(); } } diff --git a/app/code/Magento/Paypal/Model/Config/Structure/PaymentSectionModifier.php b/app/code/Magento/Paypal/Model/Config/Structure/PaymentSectionModifier.php new file mode 100644 index 0000000000000000000000000000000000000000..7c758dba4acbde030cc825059ae332f892160ef2 --- /dev/null +++ b/app/code/Magento/Paypal/Model/Config/Structure/PaymentSectionModifier.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Paypal\Model\Config\Structure; + +/** + * PayPal change structure of payment methods configuration in admin panel. + */ +class PaymentSectionModifier +{ + /** + * Identifiers of special payment method configuration groups + * + * @var array + */ + private static $specialGroups = [ + 'account', + 'recommended_solutions', + 'other_paypal_payment_solutions', + 'other_payment_methods', + ]; + + /** + * Returns changed section structure. + * + * Payment configuration has predefined special blocks: + * - Account information (id = account), + * - Recommended Solutions (id = recommended_solutions), + * - Other PayPal paymnt solution (id = other_paypal_payment_solutions), + * - Other payment methods (id = other_payment_methods). + * All payment methods configuration should be moved to one of this group. + * To move payment method to specific configuration group specify "displayIn" + * attribute in system.xml file equals to any id of predefined special group. + * If "displayIn" attribute is not specified then payment method moved to "Other payment methods" group + * + * @param array $initialStructure + * @return array + */ + public function modify(array $initialStructure) + { + $changedStructure = array_fill_keys(self::$specialGroups, []); + + foreach ($initialStructure as $childSection => $childData) { + if (in_array($childSection, self::$specialGroups)) { + if (isset($changedStructure[$childSection]['children'])) { + $children = $changedStructure[$childSection]['children']; + if (isset($childData['children'])) { + $children += $childData['children']; + } + $childData['children'] = $children; + unset($children); + } + $changedStructure[$childSection] = $childData; + } else { + $moveInstructions = $this->getMoveInstructions($childSection, $childData); + if (!empty($moveInstructions)) { + foreach ($moveInstructions as $moveInstruction) { + unset($childData['children'][$moveInstruction['section']]); + unset($moveInstruction['data']['displayIn']); + $changedStructure + [$moveInstruction['parent']] + ['children'] + [$moveInstruction['section']] = $moveInstruction['data']; + } + } + if (!isset($moveInstructions[$childSection])) { + $changedStructure['other_payment_methods']['children'][$childSection] = $childData; + } + } + } + + return $changedStructure; + } + + /** + * Recursively collect groups that should be moved to special section + * + * @param string $section + * @param array $data + * @return array + */ + private function getMoveInstructions($section, $data) + { + $moved = []; + + if (array_key_exists('children', $data)) { + foreach ($data['children'] as $childSection => $childData) { + $movedChildren = $this->getMoveInstructions($childSection, $childData); + if (isset($movedChildren[$childSection])) { + unset($data['children'][$childSection]); + } + $moved = array_merge($moved, $movedChildren); + } + } + + if (isset($data['displayIn']) && in_array($data['displayIn'], self::$specialGroups)) { + $moved = array_merge( + [ + $section => [ + 'parent' => $data['displayIn'], + 'section' => $section, + 'data' => $data + ] + ], + $moved + ); + } + + return $moved; + } +} diff --git a/app/code/Magento/Paypal/Model/Config/StructurePlugin.php b/app/code/Magento/Paypal/Model/Config/StructurePlugin.php index b026aa09692930fea7a58c1afc2946b280bad9fa..0d07abfb8047fc1c94bd9aba04cb15a0c6fdabfa 100644 --- a/app/code/Magento/Paypal/Model/Config/StructurePlugin.php +++ b/app/code/Magento/Paypal/Model/Config/StructurePlugin.php @@ -10,6 +10,8 @@ use Magento\Config\Model\Config\Structure; use Magento\Config\Model\Config\Structure\Element\Section; use Magento\Config\Model\Config\Structure\ElementInterface; use Magento\Paypal\Helper\Backend as BackendHelper; +use Magento\Framework\App\ObjectManager; +use Magento\Paypal\Model\Config\Structure\PaymentSectionModifier; /** * Plugin for \Magento\Config\Model\Config\Structure @@ -31,6 +33,11 @@ class StructurePlugin */ private $scopeDefiner; + /** + * @var PaymentSectionModifier + */ + private $paymentSectionModifier; + /** * @var string[] */ @@ -50,12 +57,18 @@ class StructurePlugin /** * @param ScopeDefiner $scopeDefiner - * @param BackendHelper $helper + * @param BackendHelper $backendHelper + * @param PaymentSectionModifier|null $paymentSectionModifier */ - public function __construct(ScopeDefiner $scopeDefiner, BackendHelper $helper) - { + public function __construct( + ScopeDefiner $scopeDefiner, + BackendHelper $backendHelper, + PaymentSectionModifier $paymentSectionModifier = null + ) { $this->scopeDefiner = $scopeDefiner; - $this->backendHelper = $helper; + $this->backendHelper = $backendHelper; + $this->paymentSectionModifier = $paymentSectionModifier + ?: ObjectManager::getInstance()->get(PaymentSectionModifier::class); } /** @@ -118,62 +131,17 @@ class StructurePlugin } /** - * Change payment config structure - * - * Groups which have `displayIn` element, transfer to appropriate group. - * Groups without `displayIn` transfer to other payment methods group. + * Changes payment config structure. * * @param Section $result * @return void */ private function restructurePayments(Section $result) { - $sectionMap = [ - 'account' => [], - 'recommended_solutions' => [], - 'other_paypal_payment_solutions' => [], - 'other_payment_methods' => [] - ]; - - $configuration = $result->getData(); - - foreach ($configuration['children'] as $section => $data) { - if (array_key_exists($section, $sectionMap)) { - $sectionMap[$section] = $data; - } elseif ($displayIn = $this->getDisplayInSection($section, $data)) { - $sectionMap[$displayIn['parent']]['children'][$displayIn['section']] = $displayIn['data']; - } else { - $sectionMap['other_payment_methods']['children'][$section] = $data; - } - } - - $configuration['children'] = $sectionMap; - $result->setData($configuration, $this->scopeDefiner->getScope()); - } - - /** - * Recursive search of `displayIn` element in node children - * - * @param string $section - * @param array $data - * @return array|null - */ - private function getDisplayInSection($section, $data) - { - if (is_array($data) && array_key_exists('displayIn', $data)) { - return [ - 'parent' => $data['displayIn'], - 'section' => $section, - 'data' => $data - ]; - } - - if (array_key_exists('children', $data)) { - foreach ($data['children'] as $childSection => $childData) { - return $this->getDisplayInSection($childSection, $childData); - } - } - - return null; + $sectionData = $result->getData(); + $sectionInitialStructure = isset($sectionData['children']) ? $sectionData['children'] : []; + $sectionChangedStructure = $this->paymentSectionModifier->modify($sectionInitialStructure); + $sectionData['children'] = $sectionChangedStructure; + $result->setData($sectionData, $this->scopeDefiner->getScope()); } } diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/PaymentSectionModifierTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/PaymentSectionModifierTest.php new file mode 100644 index 0000000000000000000000000000000000000000..fdea2375d8497b76e573d5b8fe610e8adc14efcd --- /dev/null +++ b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/PaymentSectionModifierTest.php @@ -0,0 +1,182 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Paypal\Test\Unit\Model\Config\Structure; + +use Magento\Paypal\Model\Config\Structure\PaymentSectionModifier; + +class PaymentSectionModifierTest extends \PHPUnit_Framework_TestCase +{ + private static $specialGroups = [ + 'account', + 'recommended_solutions', + 'other_paypal_payment_solutions', + 'other_payment_methods', + ]; + + /** + * @param string $case + * @param array $structure + * @dataProvider caseProvider + */ + public function testSpecialGroupsPresent($case, $structure) + { + $modifier = new PaymentSectionModifier(); + $modifiedStructure = $modifier->modify($structure); + $presentSpecialGroups = array_intersect( + self::$specialGroups, + array_keys($modifiedStructure) + ); + + $this->assertEquals( + self::$specialGroups, + $presentSpecialGroups, + sprintf('All special groups must be present in %s case', $case) + ); + } + + /** + * @param string $case + * @param array $structure + * @dataProvider caseProvider + */ + public function testOnlySpecialGroupsPresent($case, $structure) + { + $modifier = new PaymentSectionModifier(); + $modifiedStructure = $modifier->modify($structure); + $presentNotSpecialGroups = array_diff( + array_keys($modifiedStructure), + self::$specialGroups + ); + + $this->assertEquals( + [], + $presentNotSpecialGroups, + sprintf('Only special groups should be present at top level in "%s" case', $case) + ); + } + + /** + * @param string $case + * @param array $structure + * @dataProvider caseProvider + */ + public function testGroupsNotRemovedAfterModification($case, $structure) + { + $modifier = new PaymentSectionModifier(); + $modifiedStructure = $modifier->modify($structure); + + $removedGroups = array_diff( + $this->fetchAllAvailableGroups($structure), + $this->fetchAllAvailableGroups($modifiedStructure) + ); + + $this->assertEquals( + [], + $removedGroups, + sprintf('Groups should not be removed after modification in "%s" case', $case) + ); + } + + public function testMovedToTargetSpecialGroup() + { + $structure = [ + 'some_payment_method1' => [ + 'id' => 'some_payment_method1', + 'displayIn' => 'recommended_solutions', + ], + 'some_group' => [ + 'id' => 'some_group', + 'children' => [ + 'some_payment_method2' => [ + 'id' => 'some_payment_method2', + 'displayIn' => 'recommended_solutions' + ], + 'some_payment_method3' => [ + 'id' => 'some_payment_method3', + 'displayIn' => 'other_payment_methods' + ], + 'some_payment_method4' => [ + 'id' => 'some_payment_method4', + 'displayIn' => 'recommended_solutions' + ], + 'some_payment_method5' => [ + 'id' => 'some_payment_method5', + ], + ] + ], + ]; + + $modifier = new PaymentSectionModifier(); + $modifiedStructure = $modifier->modify($structure); + + $this->assertEquals( + [ + 'account' => [], + 'recommended_solutions' => [ + 'children' => [ + 'some_payment_method1' => [ + 'id' => 'some_payment_method1', + ], + 'some_payment_method2' => [ + 'id' => 'some_payment_method2', + ], + 'some_payment_method4' => [ + 'id' => 'some_payment_method4', + ], + ], + ], + 'other_paypal_payment_solutions' => [], + 'other_payment_methods' => [ + 'children' => [ + 'some_payment_method3' => [ + 'id' => 'some_payment_method3', + ], + 'some_group' => [ + 'id' => 'some_group', + 'children' => [ + 'some_payment_method5' => [ + 'id' => 'some_payment_method5', + ], + ], + ], + ], + ], + ], + $modifiedStructure, + 'Some group is not moved correctly' + ); + } + + /** + * This helper method walks recursively through configuration structure and + * collect available configuration groups + * + * @param array $structure + * @return array Sorted list of group identifiers + */ + private function fetchAllAvailableGroups($structure) + { + $availableGroups = []; + foreach ($structure as $group => $data) { + $availableGroups[] = $group; + if (isset($data['children'])) { + $availableGroups = array_merge( + $availableGroups, + $this->fetchAllAvailableGroups($data['children']) + ); + } + } + $availableGroups = array_values(array_unique($availableGroups)); + sort($availableGroups); + return $availableGroups; + } + + public function caseProvider() + { + return include __DIR__ . '/_files/payment_section_structure_variations.php'; + } +} diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/_files/payment_section_structure_variations.php b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/_files/payment_section_structure_variations.php new file mode 100644 index 0000000000000000000000000000000000000000..3dc323a1b2207c0253b6c885ae4e406f57bfc87f --- /dev/null +++ b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/_files/payment_section_structure_variations.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +return [ + [ + 'empty structure', + [] + ], + [ + 'structure with special groups at the begin of the list', + [ + 'account' => [ + 'id' => 'account', + ], + 'recommended_solutions' => [ + 'id' => 'recommended_solutions', + ], + 'other_paypal_payment_solutions' => [ + 'id' => 'other_paypal_payment_solutions', + ], + 'other_payment_methods' => [ + 'id' => 'other_payment_methods', + ], + 'some_payment_method' => [ + 'id' => 'some_payment_method', + ], + ] + ], + [ + 'structure with special groups at the end of the list', + [ + 'some_payment_method' => [ + 'id' => 'some_payment_method', + ], + 'account' => [ + 'id' => 'account', + ], + 'recommended_solutions' => [ + 'id' => 'recommended_solutions', + ], + 'other_paypal_payment_solutions' => [ + 'id' => 'other_paypal_payment_solutions', + ], + 'other_payment_methods' => [ + 'id' => 'other_payment_methods', + ], + ] + ], + [ + 'structure with special groups in the middle of the list', + [ + 'some_payment_methodq' => [ + 'id' => 'some_payment_methodq', + ], + 'account' => [ + 'id' => 'account', + ], + 'recommended_solutions' => [ + 'id' => 'recommended_solutions', + ], + 'other_paypal_payment_solutions' => [ + 'id' => 'other_paypal_payment_solutions', + ], + 'other_payment_methods' => [ + 'id' => 'other_payment_methods', + ], + 'some_payment_method2' => [ + 'id' => 'some_payment_method2', + ], + ] + ], + [ + 'structure with all assigned groups', + [ + 'some_payment_method1' => [ + 'id' => 'some_payment_method1', + 'displayIn' => 'other_paypal_payment_solutions', + ], + 'some_payment_method2' => [ + 'id' => 'some_payment_method2', + 'displayIn' => 'recommended_solutions', + ], + ] + ], + [ + 'structure with not assigned groups', + [ + 'some_payment_method1' => [ + 'id' => 'some_payment_method1', + 'displayIn' => 'other_paypal_payment_solutions', + ], + 'some_payment_method2' => [ + 'id' => 'some_payment_method2', + ], + ] + ], + [ + 'special groups has predefined children', + [ + 'recommended_solutions' => [ + 'id' => 'recommended_solutions', + 'children' => [ + 'some_payment_method1' => [ + 'id' => 'some_payment_method1', + ], + ] + ], + 'some_payment_method2' => [ + 'id' => 'some_payment_method2', + 'displayIn' => 'recommended_solutions', + ], + ] + ], + [ + 'structure with displayIn that do not reference to special groups', + [ + 'some_payment_method1' => [ + 'id' => 'some_payment_method1', + ], + 'some_payment_method2' => [ + 'id' => 'some_payment_method2', + 'displayIn' => 'some_payment_method1', + ], + ] + ], +]; diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Config/StructurePluginTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Config/StructurePluginTest.php index 26dd4af0220aaeebfb10b397a9fa7c494fa36936..2e4d146ac6159b5bf995dca335ea3abe4a06549e 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Config/StructurePluginTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Config/StructurePluginTest.php @@ -61,7 +61,10 @@ class StructurePluginTest extends \PHPUnit_Framework_TestCase $this->objectManagerHelper = new ObjectManagerHelper($this); $this->plugin = $this->objectManagerHelper->getObject( ConfigStructurePlugin::class, - ['scopeDefiner' => $this->configScopeDefinerMock, 'helper' => $this->backendHelperMock] + [ + 'scopeDefiner' => $this->configScopeDefinerMock, + 'backendHelper' => $this->backendHelperMock + ] ); } diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js b/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js index bddfdf9d39aa5e1537d868a6f3f1b8459445f9fc..81aec707dbe234d3dd72188c655195c829d84a65 100644 --- a/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js +++ b/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js @@ -317,7 +317,7 @@ define(['jquery', 'jquery/ui'], function ($) { if (this._loop) { additionalParams += '&loop=1'; } - src = 'http://player.vimeo.com/video/' + + src = window.location.protocol + '//player.vimeo.com/video/' + this._code + '?api=1&player_id=vimeo' + this._code + timestamp + diff --git a/app/code/Magento/Sales/Setup/InstallSchema.php b/app/code/Magento/Sales/Setup/InstallSchema.php index 64240feef0f5f4036bed21ce22e14d6f6db57d10..6b0b27739ca15d966ab4cd8259af6b213a939cca 100644 --- a/app/code/Magento/Sales/Setup/InstallSchema.php +++ b/app/code/Magento/Sales/Setup/InstallSchema.php @@ -2091,7 +2091,7 @@ class InstallSchema implements InstallSchemaInterface )->addColumn( 'cc_number_enc', \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, - 32, + 128, [], 'Cc Number Enc' )->addColumn( diff --git a/app/code/Magento/Sales/Setup/UpgradeSchema.php b/app/code/Magento/Sales/Setup/UpgradeSchema.php index 60f93fc765079760b2dd4ca434990337aaee748d..700da396b930d8312dfaaa6015268fa9531ae82b 100644 --- a/app/code/Magento/Sales/Setup/UpgradeSchema.php +++ b/app/code/Magento/Sales/Setup/UpgradeSchema.php @@ -84,6 +84,17 @@ class UpgradeSchema implements UpgradeSchemaInterface ); } } + if (version_compare($context->getVersion(), '2.0.5', '<')) { + $connection = $installer->getConnection(self::$connectionName); + $connection->modifyColumn( + $installer->getTable('sales_order_payment', self::$connectionName), + 'cc_number_enc', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 'length' => 128 + ] + ); + } } /** diff --git a/app/code/Magento/Shipping/Model/Shipping.php b/app/code/Magento/Shipping/Model/Shipping.php index 3df54577107a9b4dc512f33df197f83abccdc495..d67ab2ddf8d49d0dd07d933ae213378819c5d4f4 100644 --- a/app/code/Magento/Shipping/Model/Shipping.php +++ b/app/code/Magento/Shipping/Model/Shipping.php @@ -5,8 +5,10 @@ */ namespace Magento\Shipping\Model; -use Magento\Sales\Model\Order\Shipment; +use Magento\Framework\App\ObjectManager; use Magento\Quote\Model\Quote\Address\RateCollectorInterface; +use Magento\Quote\Model\Quote\Address\RateRequestFactory; +use Magento\Sales\Model\Order\Shipment; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -81,6 +83,11 @@ class Shipping implements RateCollectorInterface */ protected $stockRegistry; + /** + * @var RateRequestFactory + */ + private $rateRequestFactory; + /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Shipping\Model\Config $shippingConfig @@ -91,6 +98,9 @@ class Shipping implements RateCollectorInterface * @param \Magento\Directory\Model\RegionFactory $regionFactory * @param \Magento\Framework\Math\Division $mathDivision * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry + * @param RateRequestFactory $rateRequestFactory + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, @@ -101,7 +111,8 @@ class Shipping implements RateCollectorInterface \Magento\Shipping\Model\Shipment\RequestFactory $shipmentRequestFactory, \Magento\Directory\Model\RegionFactory $regionFactory, \Magento\Framework\Math\Division $mathDivision, - \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry + \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, + RateRequestFactory $rateRequestFactory = null ) { $this->_scopeConfig = $scopeConfig; $this->_shippingConfig = $shippingConfig; @@ -112,6 +123,7 @@ class Shipping implements RateCollectorInterface $this->_regionFactory = $regionFactory; $this->mathDivision = $mathDivision; $this->stockRegistry = $stockRegistry; + $this->rateRequestFactory = $rateRequestFactory ?: ObjectManager::getInstance()->get(RateRequestFactory::class); } /** @@ -463,7 +475,7 @@ class Shipping implements RateCollectorInterface public function collectRatesByAddress(\Magento\Framework\DataObject $address, $limitCarrier = null) { /** @var $request \Magento\Quote\Model\Quote\Address\RateRequest */ - $request = $this->_shipmentRequestFactory->create(); + $request = $this->rateRequestFactory->create(); $request->setAllItems($address->getAllItems()); $request->setDestCountryId($address->getCountryId()); $request->setDestRegionId($address->getRegionId()); @@ -473,10 +485,13 @@ class Shipping implements RateCollectorInterface $request->setPackageWeight($address->getWeight()); $request->setFreeMethodWeight($address->getFreeMethodWeight()); $request->setPackageQty($address->getItemQty()); - $request->setStoreId($this->_storeManager->getStore()->getId()); - $request->setWebsiteId($this->_storeManager->getStore()->getWebsiteId()); - $request->setBaseCurrency($this->_storeManager->getStore()->getBaseCurrency()); - $request->setPackageCurrency($this->_storeManager->getStore()->getCurrentCurrency()); + + /** @var \Magento\Store\Api\Data\StoreInterface $store */ + $store = $this->_storeManager->getStore(); + $request->setStoreId($store->getId()); + $request->setWebsiteId($store->getWebsiteId()); + $request->setBaseCurrency($store->getBaseCurrency()); + $request->setPackageCurrency($store->getCurrentCurrency()); $request->setLimitCarrier($limitCarrier); $request->setBaseSubtotalInclTax($address->getBaseSubtotalInclTax()); diff --git a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml index a5f96e8d27cf07714562e242c96ef29008acbd8c..d9c7402a3e1588fc1c6c24f0fcc1b3afc5af9f3b 100644 --- a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml +++ b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml @@ -10,9 +10,11 @@ $stores = $block->getStoresSortedBySortOrder(); ?> -<fieldset class="fieldset"> - <legend class="legend"><span><?php echo $block->escapeHtml( __('Manage Swatch (Values of Your Attribute)')); ?></span></legend> - <div id="swatch-visual-options-panel"> +<fieldset class="admin__fieldset fieldset"> + <legend class="legend"> + <span><?php echo $block->escapeHtml( __('Manage Swatch (Values of Your Attribute)')); ?></span> + </legend><br /> + <div class="admin__control-table-wrapper" id="swatch-visual-options-panel"> <table class="data-table clearfix" cellspacing="0"> <thead> <tr id="swatch-visual-options-table"> @@ -116,5 +118,4 @@ $stores = $block->getStoresSortedBySortOrder(); } } </script> - </fieldset> diff --git a/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js b/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js index 950e87b2bfb58762bf63ab6d2200a63a7c8e5049..642c9b607274516ed8494dc09fd77181289e1256 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js @@ -20,6 +20,7 @@ define([ method: 'GET', dataType: 'json' }, + dataScope: '', data: {} }, @@ -29,8 +30,16 @@ define([ * @returns {DataStorage} Chainable. */ initConfig: function () { + var scope; + this._super(); + scope = this.dataScope; + + if (typeof scope === 'string') { + this.dataScope = scope ? [scope] : []; + } + this._requests = []; return this; @@ -77,10 +86,12 @@ define([ * @returns {jQueryPromise} */ getData: function (params, options) { - var cachedRequest = this.getRequest(params); + var cachedRequest; - if (params && params.filters && params.filters['store_id']) { - cachedRequest = false; + if (this.hasScopeChanged(params)) { + this.clearRequests(); + } else { + cachedRequest = this.getRequest(params); } options = options || {}; @@ -90,6 +101,30 @@ define([ this.requestData(params); }, + /** + * Tells whether one of the parameters defined in the "dataScope" has + * changed since the last request. + * + * @param {Object} params - Request parameters. + * @returns {Boolean} + */ + hasScopeChanged: function (params) { + var lastRequest = _.last(this._requests), + keys, + diff; + + if (!lastRequest) { + return false; + } + + diff = utils.compare(lastRequest.params, params); + + keys = _.pluck(diff.changes, 'path'); + keys = keys.concat(Object.keys(diff.containers)); + + return _.intersection(this.dataScope, keys).length > 0; + }, + /** * Extends records of current data object * with the provided records collection. diff --git a/app/code/Magento/Vault/Model/PaymentTokenRepository.php b/app/code/Magento/Vault/Model/PaymentTokenRepository.php index f32e7dbcb45dfe9c6b392504d5c11ffde8f0a460..f904fed1b8ad90168eca6992ca1e03e10b330b67 100644 --- a/app/code/Magento/Vault/Model/PaymentTokenRepository.php +++ b/app/code/Magento/Vault/Model/PaymentTokenRepository.php @@ -129,6 +129,7 @@ class PaymentTokenRepository implements PaymentTokenRepositoryInterface } $tokenModel->setIsActive(false); + $tokenModel->setIsVisible(false); $tokenModel->save(); return true; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less index 43f04e52a8e6040f3d554d8c73946cdaa069a5d8..3ae7e9bf31178f155a118ece3e28c5927e209bb9 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less @@ -131,6 +131,7 @@ &._required { span { &:after { + .lib-css(margin, @form-field-label-asterisk__margin); color: @validation__color; content: '*'; } diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index 3a8cc65a597b27b9558f6d507308cc9b716a8137..d8c894788dd8252724f344f342fc69a867b9f313 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -744,6 +744,7 @@ // -------------------------------------- fieldset { + min-width: 0; padding: 20px; } @@ -5049,6 +5050,10 @@ } } } + + .admin__control-table-wrapper { + clear: both; + } } .catalog-product-set-index { diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php index 3da02007eb5082fa8d056486670a8ec077554645..73500ff5ae24cfa395ffb2c10461a53737217ce2 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php @@ -180,8 +180,12 @@ class BasePriceStorageTest extends WebapiAbstract ] ], 2 => [ - 'message' => 'Requested store is not found.', - 'parameters' => [] + 'message' => + 'Requested store is not found. Row ID: SKU = not_existing_sku, Store ID: 9999.', + 'parameters' => [ + 'not_existing_sku', + '9999' + ] ] ]; diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php index ff6a4f5283ff2a5f23d48da8f813df24342cc76f..f04097a17a0762c4f47ae1b1ebded3e512028e75 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php @@ -174,15 +174,19 @@ class CostStorageTest extends WebapiAbstract ] ], 1 => [ - 'message' => 'Invalid attribute %fieldName = %fieldValue.', + 'message' => 'Invalid attribute Cost = -9999. Row ID: SKU = not_existing_sku, Store ID: 9999.', 'parameters' => [ - 'Cost', '-9999', + 'not_existing_sku', + '9999' ] ], 2 => [ - 'message' => 'Requested store is not found.', - 'parameters' => [] + 'message' => 'Requested store is not found. Row ID: SKU = not_existing_sku, Store ID: 9999.', + 'parameters' => [ + 'not_existing_sku', + '9999' + ] ] ]; diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/LiselectstoreElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/LiselectstoreElement.php index 65985c017d08195438e94d07b032d035c6712788..1ef99365d95eb681d06b9295bd85649e2886fb6d 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/LiselectstoreElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/LiselectstoreElement.php @@ -74,7 +74,7 @@ class LiselectstoreElement extends SimpleElement } $optionSelector = './/' . implode($this->optionMaskFollowing, $optionSelector) . '/a'; - $option = $this->driver->find($optionSelector, Locator::SELECTOR_XPATH); + $option = $this->context->find($optionSelector, Locator::SELECTOR_XPATH); if (!$option->isVisible()) { throw new \Exception('[' . implode('/', $value) . '] option is not visible in store switcher.'); } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Cache.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Cache.php index 7b26568132bbe4da15ec4796a4676d4efd8f4435..272e3158d3c27021d4a8bddd1130edfa8ba9a832 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Cache.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Cache.php @@ -29,13 +29,16 @@ class Cache extends Cli const PARAM_CACHE_ENABLE = 'cache:enable'; /** - * Flush cache. + * Flush Cache. + * If no parameters are set, all cache types are flushed. * + * @param array $cacheTypes * @return void */ - public function flush() + public function flush(array $cacheTypes = []) { - parent::execute(Cache::PARAM_CACHE_FLUSH); + $options = empty($cacheTypes) ? '' : ' ' . implode(' ', $cacheTypes); + parent::execute(Cache::PARAM_CACHE_FLUSH . $options); } /** diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/StaticContent.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/StaticContent.php new file mode 100644 index 0000000000000000000000000000000000000000..225b99b0283f6d8d03bace79d7124847169a9539 --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/StaticContent.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Mtf\Util\Command\Cli; + +use Magento\Mtf\Util\Command\Cli; + +/** + * Merchant Developer deploys static view files during test executions so that Storefront UI updates are applied. + */ +class StaticContent extends Cli +{ + /** + * Parameter for deploy static view files. + */ + const PARAM_SETUP_STATIC_CONTENT_DEPLOY = 'setup:static-content:deploy'; + + /** + * Deploy static view files. + * + * @return void + */ + public function deploy() + { + parent::execute(StaticContent::PARAM_SETUP_STATIC_CONTENT_DEPLOY); + } +} diff --git a/dev/tests/functional/phpunit.xml.dist b/dev/tests/functional/phpunit.xml.dist index bcd8adec52eee81173b52bcaa3efca23efaa6a6e..afc68476ee4123c72a4f52e9f3cc8cf84c67aa0b 100644 --- a/dev/tests/functional/phpunit.xml.dist +++ b/dev/tests/functional/phpunit.xml.dist @@ -39,6 +39,7 @@ <env name="basedir" value="var/log" /> <env name="credentials_file_path" value="./credentials.xml.dist" /> <env name="mage_mode" value="developer" /> + <env name="magento_timezone" value="America/Los_Angeles" /> </php> </phpunit> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php new file mode 100644 index 0000000000000000000000000000000000000000..e7937da1e4ec8ffa4e37999e54829837ad569474 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpUsedOnFrontend.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Backend\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Customer\Test\Fixture\Customer; +use Magento\Customer\Test\TestStep\LoginCustomerOnFrontendStep as LogInCustomerOnStorefront; +use Magento\Customer\Test\TestStep\LogoutCustomerOnFrontendStep as LogOutCustomerOnStorefront; + +/** + * Assert that http is used all over the Storefront. + */ +class AssertHttpUsedOnFrontend extends AbstractConstraint +{ + /** + * Unsecured protocol format. + * + * @var string + */ + private $unsecuredProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTP; + + /** + * Browser interface. + * + * @var BrowserInterface + */ + protected $browser; + + /** + * Customer account. + * + * @var Customer + */ + protected $customer; + + /** + * Validations execution. + * + * @param BrowserInterface $browser + * @param Customer $customer + * @return void + */ + public function processAssert(BrowserInterface $browser, Customer $customer) + { + $this->browser = $browser; + $this->customer = $customer; + $this->customer->persist(); + + // Log in to Customer Account on Storefront to assert that http is used indeed. + $this->objectManager->create(LogInCustomerOnStorefront::class, ['customer' => $this->customer])->run(); + $this->assertUsedProtocol($this->unsecuredProtocol); + + // Log out from Customer Account on Storefront to assert that JS is deployed validly as a part of statics. + $this->objectManager->create(LogOutCustomerOnStorefront::class)->run(); + $this->assertUsedProtocol($this->unsecuredProtocol); + } + + /** + * Assert that specified protocol is used on current page. + * + * @param string $expectedProtocol + * @return void + */ + protected function assertUsedProtocol($expectedProtocol) + { + if (substr($expectedProtocol, -3) !== "://") { + $expectedProtocol .= '://'; + } + + \PHPUnit_Framework_Assert::assertStringStartsWith( + $expectedProtocol, + $this->browser->getUrl(), + "$expectedProtocol is not used." + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Unsecured URLs are used for Storefront pages.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php new file mode 100644 index 0000000000000000000000000000000000000000..34019f5849be6ee997a362fc154e1af6abd96ca6 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Constraint/AssertHttpsUsedOnBackend.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Backend\Test\Constraint; + +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Backend\Test\Page\Adminhtml\Dashboard; + +/** + * Assert that https protocol is used all over the Admin panel. + */ +class AssertHttpsUsedOnBackend extends AbstractConstraint +{ + /** + * Secured protocol format. + * + * @var string + */ + private $securedProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTPS; + + /** + * Unsecured protocol format. + * + * @var string + */ + private $unsecuredProtocol = \Magento\Framework\HTTP\PhpEnvironment\Request::SCHEME_HTTP; + + /** + * Browser interface. + * + * @var BrowserInterface + */ + protected $browser; + + /** + * Validations execution. + * + * @param BrowserInterface $browser + * @param Dashboard $adminDashboardPage + * @param string $navMenuPath + * @return void + */ + public function processAssert(BrowserInterface $browser, Dashboard $adminDashboardPage, $navMenuPath) + { + $this->browser = $browser; + + // Open specified Admin page using Navigation Menu to assert that JS is deployed validly as a part of statics. + $adminDashboardPage->open()->getMenuBlock()->navigate($navMenuPath); + $this->assertUsedProtocol($this->securedProtocol); + $this->assertDirectHttpUnavailable(); + } + + /** + * Assert that specified protocol is used on current page. + * + * @param string $expectedProtocol + * @return void + */ + protected function assertUsedProtocol($expectedProtocol) + { + if (substr($expectedProtocol, -3) !== "://") { + $expectedProtocol .= '://'; + } + + \PHPUnit_Framework_Assert::assertStringStartsWith( + $expectedProtocol, + $this->browser->getUrl(), + "$expectedProtocol is not used." + ); + } + + /** + * Assert that Merchant is redirected to https if trying to access the page directly via http. + * + * @return void + */ + protected function assertDirectHttpUnavailable() + { + $fakeUrl = str_replace($this->securedProtocol, $this->unsecuredProtocol, $this->browser->getUrl()); + $this->browser->open($fakeUrl); + \PHPUnit_Framework_Assert::assertStringStartsWith( + $this->securedProtocol, + $this->browser->getUrl(), + 'Merchant is not redirected to https if tries to access the Admin panel page directly via http.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Secured URLs are used for Admin panel pages.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Fixture/Source/Date.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Fixture/Source/Date.php index 9bc78a1416ef8536e51da1e69b499add05a4567f..2d625e2a7213153a2d3f98a9829645187b261691 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Fixture/Source/Date.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Fixture/Source/Date.php @@ -13,9 +13,17 @@ use Magento\Mtf\Fixture\DataSource; * * Data keys: * - pattern (Format a local time/date with delta, e.g. 'm/d/Y -3 days' = current day - 3 days) + * - apply_timezone (true if it is needed to apply timezone) */ class Date extends DataSource { + /** + * Indicates whether timezone setting is applied or not. + * + * @var bool + */ + private $isTimezoneApplied; + /** * @constructor * @param array $params @@ -35,7 +43,16 @@ class Date extends DataSource if (!$timestamp) { throw new \Exception('Invalid date format for "' . $this->params['attribute_code'] . '" field'); } - $date = date(str_replace($delta, '', $data['pattern']), $timestamp); + if (isset($data['apply_timezone']) && $data['apply_timezone'] === true) { + $date = new \DateTime(); + $date->setTimestamp($timestamp); + $date->setTimezone(new \DateTimeZone($_ENV['magento_timezone'])); + $date = $date->format(str_replace($delta, '', $data['pattern'])); + $this->isTimezoneApplied = true; + } else { + $date = date(str_replace($delta, '', $data['pattern']), $timestamp); + $this->isTimezoneApplied = false; + } if (!$date) { $date = date('m/d/Y'); } @@ -44,4 +61,14 @@ class Date extends DataSource $this->data = $data; } } + + /** + * Verifies if timezone setting has been already applied. + * + * @return bool + */ + public function isTimezoneApplied() + { + return $this->isTimezoneApplied; + } } 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 eb65b5daa9e9d756bcae958d9ef17553d32acbe7..25e6442e5105bf6e5be2bac04323ca81ab4665f6 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 @@ -5,7 +5,8 @@ * 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"> +<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="store_information_US"> <field name="general/store_information/name" xsi:type="array"> @@ -165,12 +166,14 @@ <item name="scope_id" xsi:type="number">0</item> <item name="label" xsi:type="string">Yes</item> <item name="value" xsi:type="number">1</item> + <item name="inherit" xsi:type="number">1</item> </field> <field name="web/secure/use_in_adminhtml" xsi:type="array"> <item name="scope" xsi:type="string">default</item> <item name="scope_id" xsi:type="number">0</item> <item name="label" xsi:type="string">Yes</item> <item name="value" xsi:type="number">1</item> + <item name="inherit" xsi:type="number">1</item> </field> </dataset> @@ -220,6 +223,21 @@ </field> </dataset> + <dataset name="disable_https_frontend_admin"> + <field name="web/secure/use_in_frontend" xsi:type="array"> + <item name="scope" xsi:type="string">default</item> + <item name="scope_id" xsi:type="number">0</item> + <item name="label" xsi:type="string">No</item> + <item name="value" xsi:type="number">0</item> + </field> + <field name="web/secure/use_in_adminhtml" xsi:type="array"> + <item name="scope" xsi:type="string">default</item> + <item name="scope_id" xsi:type="number">0</item> + <item name="label" xsi:type="string">No</item> + <item name="value" xsi:type="number">0</item> + </field> + </dataset> + <dataset name="custom_allowed_country"> <field name="general/country/allow" xsi:type="array"> <item name="scope" xsi:type="string">default</item> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.php b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d6508cac810757323f50ce6683aeb5a24df0025d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.php @@ -0,0 +1,157 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Backend\Test\TestCase; + +use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Backend\Test\Page\Adminhtml\SystemConfigEdit; +use Magento\Mtf\Util\Command\Cli\Cache; +use Magento\Mtf\Util\Command\Cli\StaticContent; + +/** + * Verify that Merchant can configure secure URLs for Storefront and/or Admin panel in order to improve Store security. + * + * Preconditions: + * 1. SSL on server is configured. + * 2. Secure URLs are disabled for Storefront & Admin (out-of-the-box Magento state). + * + * Steps: + * 1. Log in to Admin panel. + * 2. Go to "Stores > Configuration" page. + * 3. Select needed scope. + * 4. Go to "General > Web > Base URLs (Secure)" section. + * 5. Specify Base URL with Secure protocol in the same format as a Secure Base URL. + * (i) Make sure that Secure Base URL ends with a "/". + * 6. Enable Secure URLs for Storefront if there is a need. + * 7. Enable Secure URLs for Admin if there is a need. + * 8. Save the Config & refresh invalidated caches (Configuration, Page Cache). + * 9. Deploy static view files. + * + * 10. If Secure URLs for Storefront were enabled: + * 1. Assert that https is used all over the Storefront. + * 2. Assert that static content is deployed validly (ex: JS functionality works on Storefront). + * 3. Assert that Customer is redirected to https if trying to access the page directly via http. + * 11. If secure URLs for Storefront were disabled: + * 1. Assert that http is used all over the Storefront. + * 2. Assert that static content is deployed validly (ex: JS functionality works on Storefront). + * + * 12. If secure URLs for Admin were enabled: + * 1. Assert that https is used all over the Admin panel. + * 2. Assert that static content is deployed validly (ex: JS functionality works in Admin panel). + * 3. Assert that Merchant is redirected to https if trying to access the page directly via http. + * 13. If secure URLs for Admin were disabled: + * 1. Assert that http is used all over the Admin panel. + * 2. Assert that static content is deployed validly (ex: JS functionality works in Admin panel). + * 3. Assert that Merchant is redirected to http if trying to access the page directly via https. + * + * Postconditions: + * 1. Turn the Secure URLs usage off (with further cache refreshing & static content deploying). + * + * @ZephyrId MAGETWO-35408 + */ +class ConfigureSecureUrlsTest extends Injectable +{ + /* tags */ + const MVP = 'no'; + const SEVERITY = 'S1'; + /* end tags */ + + /** + * Fixture factory. + * + * @var FixtureFactory + */ + protected $fixtureFactory; + + /** + * "Configuration" page in Admin panel. + * + * @var SystemConfigEdit + */ + protected $configurationAdminPage; + + /** + * Cache CLI. + * + * @var Cache + */ + protected $cache; + + /** + * Static content CLI. + * + * @var StaticContent + */ + protected $staticContent; + + /** + * Prepare data for further test execution. + * + * @param FixtureFactory $fixtureFactory + * @param SystemConfigEdit $configurationAdminPage + * @param Cache $cache + * @param StaticContent $staticContent + * @return void + */ + public function __inject( + FixtureFactory $fixtureFactory, + SystemConfigEdit $configurationAdminPage, + Cache $cache, + StaticContent $staticContent + ) { + $this->fixtureFactory = $fixtureFactory; + $this->configurationAdminPage = $configurationAdminPage; + $this->cache = $cache; + $this->staticContent = $staticContent; + } + + /** + * Test execution. + * + * @param string $configData + * @return void + */ + public function test($configData) + { + $data = [ + 'web/secure/base_url' => [ + 'scope' => 'default', + 'scope_id' => 0, + 'value' => str_replace(['http', 'index.php/'], ['https', ''], $_ENV['app_frontend_url']) + ] + ]; + $config = $this->fixtureFactory->createByCode('configData', ['dataset' => $configData, 'data' => $data]); + $config->persist(); + + // Workaround until MTA-3879 is delivered. + $this->configurationAdminPage->open(); + $this->configurationAdminPage->getForm() + ->getGroup('web', 'secure') + ->setValue('web', 'secure', 'use_in_adminhtml', 'Yes'); + $this->configurationAdminPage->getPageActions()->save(); + $_ENV['app_backend_url'] = str_replace('http', 'https', $_ENV['app_backend_url']); + + $this->cache->flush(['config', 'full_page']); + $this->staticContent->deploy(); + } + + /** + * Revert all applied high-level changes. + * + * @return void + */ + public function tearDown() + { + $this->configurationAdminPage->open(); + $this->configurationAdminPage->getForm() + ->getGroup('web', 'secure') + ->setValue('web', 'secure', 'use_in_adminhtml', 'No'); + $this->configurationAdminPage->getPageActions()->save(); + $this->cache->flush(['config', 'full_page']); + $this->staticContent->deploy(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..d63e0639ea030ad9b5ada22a89d9dc100c2372d4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ConfigureSecureUrlsTest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Backend\Test\TestCase\ConfigureSecureUrlsTest" summary="Configure secure URLs" ticketId="MAGETWO-35408"> + <variation name="ConfigureSecureUrlsHttpForStorefrontHttpsForAdmin" summary="http for Storefront, https for Admin" ticketId="MAGETWO-35408"> + <data name="configData" xsi:type="string">disable_https_frontend_admin</data> + <data name="navMenuPath" xsi:type="string">Marketing>Catalog Price Rule</data> + <constraint name="Magento\Backend\Test\Constraint\AssertHttpUsedOnFrontend" /> + <constraint name="Magento\Backend\Test\Constraint\AssertHttpsUsedOnBackend" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/etc/di.xml index ecba481f0e9480c63fa604cb64ff8bceb517b924..722279d5ec65914ece2ea22887a817ae41abfea2 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/etc/di.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/etc/di.xml @@ -11,6 +11,16 @@ <argument name="severity" xsi:type="string">high</argument> </arguments> </type> + <type name="Magento\Backend\Test\Constraint\AssertHttpsUsedOnBackend"> + <arguments> + <argument name="severity" xsi:type="string">high</argument> + </arguments> + </type> + <type name="Magento\Backend\Test\Constraint\AssertHttpsUsedOnFrontend"> + <arguments> + <argument name="severity" xsi:type="string">middle</argument> + </arguments> + </type> <type name="Magento\Backend\Test\Constraint\AssertStoreCanBeLocalized"> <arguments> <argument name="severity" xsi:type="string">high</argument> 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 2f0b248b800b3ce9feb2c42129a13975821dfefe..ebab7cc83b283e2a8bd5285c5361cdba7f453385 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 @@ -57,6 +57,10 @@ <selector>[name="use_default[name]"]</selector> <input>checkbox</input> </use_default_name> + <use_default_price> + <selector>[name="use_default[price]"]</selector> + <input>checkbox</input> + </use_default_price> </fields> </product-details> <advanced-pricing> 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 bb2c8bc6ead7cdca55ab42daf72e3e88f0f79310..8f73299d5f6dcfc234465faddec418a67964d092 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 @@ -48,6 +48,7 @@ <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="use_default_price" group="product-details" /> <field name="old_id" is_required="0" /> <field name="options_container" is_required="0" /> <field name="page_layout" is_required="0" /> 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 79b00f58cd742cb3bf19175e5916209b21810d0d..e9791de338bd36e9c5ec904423888913a54f6211 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 @@ -1799,5 +1799,35 @@ <item name="dataset" xsi:type="string">simple_order_default</item> </field> </dataset> + + <dataset name="product_with_additional_website"> + <field name="sku" xsi:type="string">simple_product_with_category_%isolation%</field> + <field name="name" xsi:type="string">Simple product with category %isolation%</field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="qty" xsi:type="string">777</item> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">1</field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">10</item> + <item name="dataset" xsi:type="string" /> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + <item name="1" xsi:type="array"> + <item name="dataset" xsi:type="string">custom_store</item> + </item> + </field> + <field name="url_key" xsi:type="string">simple-product-%isolation%</field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/ConfigData.xml index 308bff869265fb64eb4a667c30397e84dd174864..28ea191568cb6c8bc4b12cb3eb84b9aeb5092907 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/ConfigData.xml @@ -45,5 +45,21 @@ <item name="inherit" xsi:type="number">1</item> </field> </dataset> + <dataset name="price_scope_website"> + <field name="catalog/price/scope" xsi:type="array"> + <item name="scope" xsi:type="string">default</item> + <item name="scope_id" xsi:type="number">0</item> + <item name="label" xsi:type="string">Website</item> + <item name="value" xsi:type="number">1</item> + </field> + </dataset> + <dataset name="price_scope_website_rollback"> + <field name="catalog/price/scope" xsi:type="array"> + <item name="scope" xsi:type="string">default</item> + <item name="scope_id" xsi:type="number">0</item> + <item name="label" xsi:type="string">Global</item> + <item name="value" xsi:type="number">0</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCancelSuccessMessageInShoppingCart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCancelSuccessMessageInShoppingCart.php index 054c0ce20cd813c22b73377884e2b51d2ae83e94..f6e325a0c47900ee280b2272b0c2c84edd96ebf9 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCancelSuccessMessageInShoppingCart.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Constraint/AssertCancelSuccessMessageInShoppingCart.php @@ -17,7 +17,7 @@ class AssertCancelSuccessMessageInShoppingCart extends AbstractConstraint /** * Cancel success message text. */ - const SUCCESS_MESSAGE = 'Payment was canceled.'; + const SUCCESS_MESSAGE = 'Your purchase process has been cancelled.'; /** * Assert that success message about canceled order is present and correct. diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php index 33ad076c0fce299f0b461ed19dfb13dc29150cc7..a74c86e8e093d5b7e92706321fc2f2dd08315196 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php @@ -505,4 +505,42 @@ class CustomerRepositoryTest extends \PHPUnit_Framework_TestCase 'default_shipping customer attribute did not updated' ); } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDbIsolation enabled + */ + public function testUpdateDefaultShippingAndDefaultBillingTest() + { + $customerId = 1; + $customerData = [ + "id" => 1, + "website_id" => 1, + "email" => "roni_cost@example.com", + "firstname" => "1111", + "lastname" => "Boss", + "middlename" => null, + "gender" => 0 + ]; + + $customerEntity = $this->customerFactory->create(['data' => $customerData]); + + $customer = $this->customerRepository->getById($customerId); + $oldDefaultBilling = $customer->getDefaultBilling(); + $oldDefaultShipping = $customer->getDefaultShipping(); + + $savedCustomer = $this->customerRepository->save($customerEntity); + + $this->assertEquals( + $savedCustomer->getDefaultBilling(), + $oldDefaultBilling, + 'Default billing shoud not be overridden' + ); + + $this->assertEquals( + $savedCustomer->getDefaultShipping(), + $oldDefaultShipping, + 'Default shipping shoud not be overridden' + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/ResourceModel/Key/ChangeTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/ResourceModel/Key/ChangeTest.php index 52376ad81245ced6b87694ebbf8d4dd3c70e3a6e..76de7a2ab57d80f751f6c301f12a48dd5393a645 100644 --- a/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/ResourceModel/Key/ChangeTest.php +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/ResourceModel/Key/ChangeTest.php @@ -79,6 +79,7 @@ class ChangeTest extends \PHPUnit_Framework_TestCase ) ); $this->assertNotContains($testValue, $values1); + $this->assertRegExp('/([0-9]+:)([0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+)/', current($values1)); // Verify that the credit card number has been encrypted $values2 = $connection->fetchPairs( @@ -88,6 +89,7 @@ class ChangeTest extends \PHPUnit_Framework_TestCase ) ); $this->assertNotContains('1111111111', $values2); + $this->assertRegExp('/([0-9]+:)([0-9]+:)([a-zA-Z0-9]+:)([a-zA-Z0-9]+)/', current($values1)); /** clean up */ $select = $connection->select()->from($configModel->getMainTable())->where('path=?', $testPath); diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9aadc478f00c4b03f534ef12e71ae1538e81c9c8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Shipping\Model; + +use Magento\Framework\DataObject; +use Magento\Framework\ObjectManagerInterface; +use Magento\Quote\Model\Quote\Address\RateResult\Method; +use Magento\Shipping\Model\Rate\Result; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Contains list of tests for Shipping model + */ +class ShippingTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Shipping + */ + private $model; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->model = $this->objectManager->get(Shipping::class); + } + + /** + * Checks shipping rates processing by address. + * @covers \Magento\Shipping\Model\Shipping::collectRatesByAddress + * @return Result + */ + public function testCollectRatesByAddress() + { + $address = $this->objectManager->create(DataObject::class, [ + 'data' => [ + 'region_id' => 'CA', + 'postcode' => '11111', + 'lastname' => 'John', + 'firstname' => 'Doe', + 'street' => 'Some street', + 'city' => 'Los Angeles', + 'email' => 'john.doe@example.com', + 'telephone' => '11111111', + 'country_id' => 'US', + 'item_qty' => 1 + ] + ]); + /** @var Shipping $result */ + $result = $this->model->collectRatesByAddress($address, 'flatrate'); + static::assertInstanceOf(Shipping::class, $result); + + return $result->getResult(); + } + + /** + * Checks shipping rate details for processed address. + * @covers \Magento\Shipping\Model\Shipping::collectRatesByAddress + * @param Result $result + * @depends testCollectRatesByAddress + * @magentoConfigFixture carriers/flatrate/active 1 + * @magentoConfigFixture carriers/flatrate/price 5.00 + */ + public function testCollectRates(Result $result) + { + $rates = $result->getAllRates(); + static::assertNotEmpty($rates); + + /** @var Method $rate */ + $rate = array_pop($rates); + + static::assertInstanceOf(Method::class, $rate); + static::assertEquals('flatrate', $rate->getData('carrier')); + static::assertEquals(5, $rate->getData('price')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Vault/Model/PaymentTokenRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Vault/Model/PaymentTokenRepositoryTest.php index c22ac69db338392544ad2cada1b908c0bc42aa77..255b660de37edde2ac98cb9ddd1d0f939b8d08f3 100644 --- a/dev/tests/integration/testsuite/Magento/Vault/Model/PaymentTokenRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Vault/Model/PaymentTokenRepositoryTest.php @@ -8,39 +8,48 @@ namespace Magento\Vault\Model; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; /** - * Class PaymentTokenRepositoryTest - * @package Magento\Vault\Model + * PaymentTokenRepositoryTest contains tests for Vault token repository + * * @magentoDbIsolation enabled */ class PaymentTokenRepositoryTest extends \PHPUnit_Framework_TestCase { - /** @var PaymentTokenRepository */ + /** + * @var PaymentTokenRepository + */ private $repository; - /** @var SortOrderBuilder */ + /** + * @var SortOrderBuilder + */ private $sortOrderBuilder; - /** @var FilterBuilder */ + /** + * @var FilterBuilder + */ private $filterBuilder; - /** @var SearchCriteriaBuilder */ + /** + * @var SearchCriteriaBuilder + */ private $searchCriteriaBuilder; + /** + * @var ObjectManager + */ + private $objectManager; + public function setUp() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->repository = $objectManager->create(PaymentTokenRepository::class); - $this->searchCriteriaBuilder = $objectManager->create( - \Magento\Framework\Api\SearchCriteriaBuilder::class - ); - $this->filterBuilder = $objectManager->get( - \Magento\Framework\Api\FilterBuilder::class - ); - $this->sortOrderBuilder = $objectManager->get( - \Magento\Framework\Api\SortOrderBuilder::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->repository = $this->objectManager->create(PaymentTokenRepository::class); + $this->searchCriteriaBuilder = $this->objectManager->create(SearchCriteriaBuilder::class); + $this->filterBuilder = $this->objectManager->get(FilterBuilder::class); + $this->sortOrderBuilder = $this->objectManager->get(SortOrderBuilder::class); } /** @@ -77,4 +86,26 @@ class PaymentTokenRepositoryTest extends \PHPUnit_Framework_TestCase $this->assertEquals('second', array_shift($items)->getPaymentMethodCode()); $this->assertEquals('first', array_shift($items)->getPaymentMethodCode()); } + + /** + * @covers \Magento\Vault\Model\PaymentTokenRepository::delete + * @magentoDataFixture Magento/Vault/_files/token.php + */ + public function testDelete() + { + /** @var PaymentTokenManagement $tokenManagement */ + $tokenManagement = $this->objectManager->get(PaymentTokenManagement::class); + + $token = $tokenManagement->getByPublicHash('public_hash', 0); + + /** @var PaymentTokenRepository $tokenRepository */ + $tokenRepository = $this->objectManager->get(PaymentTokenRepository::class); + $tokenRepository->delete($token); + + $deletedToken = $tokenRepository->getById($token->getEntityId()); + + static::assertEquals('public_hash', $deletedToken->getPublicHash()); + static::assertFalse($deletedToken->getIsActive()); + static::assertFalse($deletedToken->getIsVisible()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Vault/_files/token.php b/dev/tests/integration/testsuite/Magento/Vault/_files/token.php new file mode 100644 index 0000000000000000000000000000000000000000..29e8415c4207213f81e4a70914b53844bd724490 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Vault/_files/token.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © 2013-2017 Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Vault\Model\PaymentToken; +use Magento\Vault\Model\PaymentTokenRepository; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var PaymentToken $token */ +$token = $objectManager->create(PaymentToken::class); + +$token->setGatewayToken('gateway_token') + ->setPublicHash('public_hash') + ->setPaymentMethodCode('vault_payment') + ->setType('card') + ->setExpiresAt(strtotime('+1 year')) + ->setIsVisible(true) + ->setIsActive(true); + +/** @var PaymentTokenRepository $tokenRepository */ +$tokenRepository = $objectManager->create(PaymentTokenRepository::class); +$tokenRepository->save($token); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js new file mode 100644 index 0000000000000000000000000000000000000000..febbde30f8e9c72b6af3be18a5b4d217ad9e8d83 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js @@ -0,0 +1,76 @@ +/** + * Copyright © 2017 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/*eslint max-nested-callbacks: 0*/ +/*jscs:disable requireCamelCaseOrUpperCaseIdentifiers*/ +define([ + 'mageUtils', + 'Magento_Ui/js/grid/data-storage' +], function (utils, DataStorage) { + 'use strict'; + + describe('Magento_Ui/js/grid/data-storage', function () { + describe('costructor', function () { + it('converts dataScope property to array', function () { + var model = new DataStorage({ + dataScope: 'magento' + }); + + expect(model.dataScope).toEqual(['magento']); + }); + }); + + describe('hasScopeChanged', function () { + it('is function', function () { + var model = new DataStorage({ + dataScope: '' + }); + + expect(model.hasScopeChanged).toBeDefined(); + expect(typeof model.hasScopeChanged).toEqual('function'); + }); + + it('returns false if no requests have been made', function () { + var model = new DataStorage({ + dataScope: '' + }); + + expect(model.hasScopeChanged()).toBeFalsy(); + }); + + it('tells whether parameters defined in the dataScope property have changed', function () { + var params, newParams, model; + + params = { + namespace: 'magento', + search: '', + filters: { + store_id: 0 + }, + sorting: {}, + paging: {} + }; + + newParams = utils.extend({}, params, { + search: 'magento', + filters: { + store_id: 1 + } + }); + + model = new DataStorage({ + dataScope: 'filters.store_id' + }); + + model.cacheRequest({ + totalRecords: 0 + }, params); + + expect(model.hasScopeChanged(params)).toBeFalsy(); + expect(model.hasScopeChanged(newParams)).toBeTruthy(); + }); + }); + }); +}); diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php index 004ebd4d4f4e7b4ba6ea0fdae1e604677f6c491c..279f4476b5f9ddc18dd16cca59e41e394b7f4b43 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php @@ -106,7 +106,7 @@ class Write extends Read implements WriteInterface $targetDirectory->create($this->driver->getParentDirectory($newPath)); } $absolutePath = $this->driver->getAbsolutePath($this->path, $path); - $absoluteNewPath = $targetDirectory->driver->getAbsolutePath($this->path, $newPath); + $absoluteNewPath = $targetDirectory->getAbsolutePath($newPath); return $this->driver->rename($absolutePath, $absoluteNewPath, $targetDirectory->driver); } diff --git a/lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/WriteTest.php b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/WriteTest.php index 46cbfdb2077ad314f274d1ba21b8288cdb8d84f5..7ab182f40906d8d14c905153bf083ad7b031ed62 100644 --- a/lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/WriteTest.php +++ b/lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/WriteTest.php @@ -7,6 +7,9 @@ */ namespace Magento\Framework\Filesystem\Test\Unit\Directory; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\DriverInterface; + class WriteTest extends \PHPUnit_Framework_TestCase { /** @@ -68,7 +71,7 @@ class WriteTest extends \PHPUnit_Framework_TestCase public function testGetDriver() { $this->assertInstanceOf( - \Magento\Framework\Filesystem\DriverInterface::class, + DriverInterface::class, $this->write->getDriver(), 'getDriver method expected to return instance of Magento\Framework\Filesystem\DriverInterface' ); @@ -90,8 +93,7 @@ class WriteTest extends \PHPUnit_Framework_TestCase public function testCreateSymlinkTargetDirectoryExists() { - $targetDir = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\WriteInterface::class) - ->getMock(); + $targetDir = $this->getMockBuilder(WriteInterface::class)->getMock(); $targetDir->driver = $this->driver; $sourcePath = 'source/path/file'; $destinationDirectory = 'destination/path'; @@ -159,4 +161,66 @@ class WriteTest extends \PHPUnit_Framework_TestCase { return $this->path . $path; } + + /** + * @param string $sourcePath + * @param string $targetPath + * @param WriteInterface $targetDir + * @dataProvider getFilePathsDataProvider + */ + public function testRenameFile($sourcePath, $targetPath, $targetDir) + { + if ($targetDir !== null) { + $targetDir->driver = $this->getMockBuilder(DriverInterface::class)->getMockForAbstractClass(); + $targetDirPath = 'TARGET_PATH/'; + $targetDir->expects($this->once()) + ->method('getAbsolutePath') + ->with($targetPath) + ->willReturn($targetDirPath . $targetPath); + $targetDir->expects($this->once()) + ->method('isExists') + ->with(dirname($targetPath)) + ->willReturn(false); + $targetDir->expects($this->once()) + ->method('create') + ->with(dirname($targetPath)); + } + + $this->driver->expects($this->any()) + ->method('getAbsolutePath') + ->willReturnMap([ + [$this->path, $sourcePath, null, $this->getAbsolutePath($sourcePath)], + [$this->path, $targetPath, null, $this->getAbsolutePath($targetPath)], + ]); + $this->driver->expects($this->any()) + ->method('isFile') + ->willReturnMap([ + [$this->getAbsolutePath($sourcePath), true], + [$this->getAbsolutePath($targetPath), true], + ]); + $this->driver->expects($this->any()) + ->method('getParentDirectory') + ->with($targetPath) + ->willReturn(dirname($targetPath)); + $this->write->renameFile($sourcePath, $targetPath, $targetDir); + } + + /** + * @return array + */ + public function getFilePathsDataProvider() + { + return [ + [ + 'path/to/source.file', + 'path/to/target.file', + null, + ], + [ + 'path/to/source.file', + 'path/to/target.file', + $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(), + ], + ]; + } } diff --git a/lib/internal/Magento/Framework/View/Asset/File/FallbackContext.php b/lib/internal/Magento/Framework/View/Asset/File/FallbackContext.php index 424794c4f30648c04a8a9adf4ca24c0dd01f43bb..c493d4eacb5fa464470d852c6873965564042bd7 100644 --- a/lib/internal/Magento/Framework/View/Asset/File/FallbackContext.php +++ b/lib/internal/Magento/Framework/View/Asset/File/FallbackContext.php @@ -13,11 +13,6 @@ use Magento\Framework\App\Filesystem\DirectoryList; */ class FallbackContext extends Context { - /** - * Secure path - */ - const SECURE_PATH = 'secure'; - /** * @var string */ @@ -33,24 +28,17 @@ class FallbackContext extends Context */ private $locale; - /** - * @var bool - */ - private $isSecure; - /** * @param string $baseUrl * @param string $areaType * @param string $themePath * @param string $localeCode - * @param bool $isSecure */ - public function __construct($baseUrl, $areaType, $themePath, $localeCode, $isSecure = false) + public function __construct($baseUrl, $areaType, $themePath, $localeCode) { $this->area = $areaType; $this->theme = $themePath; $this->locale = $localeCode; - $this->isSecure = $isSecure; parent::__construct($baseUrl, DirectoryList::STATIC_VIEW, $this->generatePath()); } @@ -103,6 +91,6 @@ class FallbackContext extends Context */ public function getConfigPath() { - return $this->getPath() . ($this->isSecure ? '/' . self::SECURE_PATH : ''); + return $this->getPath(); } } diff --git a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php index 1e20e27ee538aeab2aa35bab346c0facd32a99ff..de9248b8fe10accfb78f9267c22b71389e800f38 100644 --- a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php +++ b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php @@ -50,8 +50,11 @@ class Direct implements \Magento\Framework\View\Asset\MergeStrategyInterface public function merge(array $assetsToMerge, Asset\LocalInterface $resultAsset) { $mergedContent = $this->composeMergedContent($assetsToMerge, $resultAsset); - $dir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); - $dir->writeFile($resultAsset->getPath(), $mergedContent); + $filePath = $resultAsset->getPath(); + $staticDir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); + $tmpDir = $this->filesystem->getDirectoryWrite(DirectoryList::TMP); + $tmpDir->writeFile($filePath, $mergedContent); + $tmpDir->renameFile($filePath, $filePath, $staticDir); } /** diff --git a/lib/internal/Magento/Framework/View/Asset/Repository.php b/lib/internal/Magento/Framework/View/Asset/Repository.php index f0ab3d0ae36c1084af9746e270c03651a1bd53e7..28309b7d5836bb811d29407f258d0a91a89f7808 100644 --- a/lib/internal/Magento/Framework/View/Asset/Repository.php +++ b/lib/internal/Magento/Framework/View/Asset/Repository.php @@ -268,8 +268,7 @@ class Repository 'baseUrl' => $url, 'areaType' => $area, 'themePath' => $themePath, - 'localeCode' => $locale, - 'isSecure' => $isSecure + 'localeCode' => $locale ] ); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/File/FallbackContextTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/File/FallbackContextTest.php index a338df7ce296b960a5a92ec0c8c7cc2a5c532947..a6d5d2c4c29a5d4bae8b2e97cd04999295bd1fc0 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/File/FallbackContextTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/File/FallbackContextTest.php @@ -31,7 +31,6 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase * @param string $areaType * @param string $themePath * @param string $localeCode - * @param bool $isSecure * @param string $expectedResult * @dataProvider getConfigPathDataProvider */ @@ -40,7 +39,6 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase $areaType, $themePath, $localeCode, - $isSecure, $expectedResult ) { $this->fallbackContext = $this->objectManager->getObject( @@ -49,8 +47,7 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase 'baseUrl' => $baseUrl, 'areaType' => $areaType, 'themePath' => $themePath, - 'localeCode' => $localeCode, - 'isSecure' => $isSecure + 'localeCode' => $localeCode ] ); $this->assertEquals($expectedResult, $this->fallbackContext->getConfigPath()); @@ -64,7 +61,6 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase 'areaType' => 'frontend', 'themePath' => 'Magento/blank', 'localeCode' => 'en_US', - 'isSecure' => false, 'expectedResult' => 'frontend/Magento/blank/en_US' ], 'https' => [ @@ -72,8 +68,7 @@ class FallbackContextTest extends \PHPUnit_Framework_TestCase 'areaType' => 'frontend', 'themePath' => 'Magento/blank', 'localeCode' => 'en_US', - 'isSecure' => true, - 'expectedResult' => 'frontend/Magento/blank/en_US/secure' + 'expectedResult' => 'frontend/Magento/blank/en_US' ] ]; } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php index 17ba6714246d9afa44151cc2b1a72cf3a7e18542..33ad9608109e93dcd34ea9df21aa4f84d23586b5 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\View\Test\Unit\Asset\MergeStrategy; +use Magento\Framework\Filesystem\Directory\WriteInterface; use \Magento\Framework\View\Asset\MergeStrategy\Direct; use Magento\Framework\App\Filesystem\DirectoryList; @@ -22,9 +23,14 @@ class DirectTest extends \PHPUnit_Framework_TestCase protected $cssUrlResolver; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Filesystem\Directory\WriteInterface + * @var \PHPUnit_Framework_MockObject_MockObject|WriteInterface */ - protected $writeDir; + protected $staticDir; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|WriteInterface + */ + protected $tmpDir; /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\View\Asset\LocalInterface @@ -35,11 +41,14 @@ class DirectTest extends \PHPUnit_Framework_TestCase { $this->cssUrlResolver = $this->getMock(\Magento\Framework\View\Url\CssResolver::class); $filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false); - $this->writeDir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\WriteInterface::class); + $this->staticDir = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); + $this->tmpDir = $this->getMockBuilder(WriteInterface::class)->getMockForAbstractClass(); $filesystem->expects($this->any()) ->method('getDirectoryWrite') - ->with(DirectoryList::STATIC_VIEW) - ->will($this->returnValue($this->writeDir)); + ->willReturnMap([ + [DirectoryList::STATIC_VIEW, \Magento\Framework\Filesystem\DriverPool::FILE, $this->staticDir], + [DirectoryList::TMP, \Magento\Framework\Filesystem\DriverPool::FILE, $this->tmpDir], + ]); $this->resultAsset = $this->getMock(\Magento\Framework\View\Asset\File::class, [], [], '', false); $this->object = new Direct($filesystem, $this->cssUrlResolver); } @@ -47,7 +56,9 @@ class DirectTest extends \PHPUnit_Framework_TestCase public function testMergeNoAssets() { $this->resultAsset->expects($this->once())->method('getPath')->will($this->returnValue('foo/result')); - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', ''); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', ''); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge([], $this->resultAsset); } @@ -55,7 +66,9 @@ class DirectTest extends \PHPUnit_Framework_TestCase { $this->resultAsset->expects($this->once())->method('getPath')->will($this->returnValue('foo/result')); $assets = $this->prepareAssetsToMerge([' one', 'two']); // note leading space intentionally - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', 'onetwo'); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', 'onetwo'); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge($assets, $this->resultAsset); } @@ -73,7 +86,9 @@ class DirectTest extends \PHPUnit_Framework_TestCase ->method('aggregateImportDirectives') ->with('12') ->will($this->returnValue('1020')); - $this->writeDir->expects($this->once())->method('writeFile')->with('foo/result', '1020'); + $this->staticDir->expects($this->never())->method('writeFile'); + $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', '1020'); + $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir); $this->object->merge($assets, $this->resultAsset); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php index 7e212fc7980a6ceefd72fb1ff0be97934f3de81d..3f818aaa4e7cab96d1772e06678b658edb8f5f68 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php @@ -185,8 +185,7 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase 'baseUrl' => '', 'areaType' => '', 'themePath' => 'Default', - 'localeCode' => '', - 'isSecure' => '', + 'localeCode' => '' ] ) ->willReturn($fallbackContextMock); @@ -251,8 +250,7 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase 'baseUrl' => '', 'areaType' => 'area', 'themePath' => '', - 'localeCode' => 'locale', - 'isSecure' => '', + 'localeCode' => 'locale' ] ) ->willReturn($fallbackContextMock); diff --git a/nginx.conf.sample b/nginx.conf.sample index ec524374900f64b4ed0307ae8e17501e814a3f62..9f94f72e6c876930eb82db865907594ee174705e 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -108,7 +108,7 @@ location /static/ { expires +1y; if (!-f $request_filename) { - rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; + rewrite ^/static/?(.*)$ /static.php?resource=$1 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { @@ -117,11 +117,11 @@ location /static/ { expires off; if (!-f $request_filename) { - rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; + rewrite ^/static/?(.*)$ /static.php?resource=$1 last; } } if (!-f $request_filename) { - rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; + rewrite ^/static/?(.*)$ /static.php?resource=$1 last; } add_header X-Frame-Options "SAMEORIGIN"; }