diff --git a/app/code/Magento/Braintree/Controller/Paypal/Review.php b/app/code/Magento/Braintree/Controller/Paypal/Review.php index 4576e3b033df8..15acb1859ec87 100644 --- a/app/code/Magento/Braintree/Controller/Paypal/Review.php +++ b/app/code/Magento/Braintree/Controller/Paypal/Review.php @@ -11,6 +11,7 @@ use Magento\Braintree\Gateway\Config\PayPal\Config; use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater; use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Model\Method\Logger; /** * Class Review @@ -22,6 +23,11 @@ class Review extends AbstractAction */ private $quoteUpdater; + /** + * @var Logger + */ + private $logger; + /** * @var string */ @@ -34,15 +40,18 @@ class Review extends AbstractAction * @param Config $config * @param Session $checkoutSession * @param QuoteUpdater $quoteUpdater + * @param Logger $logger */ public function __construct( Context $context, Config $config, Session $checkoutSession, - QuoteUpdater $quoteUpdater + QuoteUpdater $quoteUpdater, + Logger $logger ) { parent::__construct($context, $config, $checkoutSession); $this->quoteUpdater = $quoteUpdater; + $this->logger = $logger; } /** @@ -54,6 +63,7 @@ public function execute() $this->getRequest()->getPostValue('result', '{}'), true ); + $this->logger->debug($requestData); $quote = $this->checkoutSession->getQuote(); try { diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php index aa23fa767d1ed..ae2b1b1423640 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php @@ -123,8 +123,8 @@ private function updateShippingAddress(Quote $quote, array $details) { $shippingAddress = $quote->getShippingAddress(); - $shippingAddress->setLastname($details['lastName']); - $shippingAddress->setFirstname($details['firstName']); + $shippingAddress->setLastname($this->getShippingRecipientLastName($details)); + $shippingAddress->setFirstname($this->getShippingRecipientFirstName($details)); $shippingAddress->setEmail($details['email']); $shippingAddress->setCollectShippingRates(true); @@ -188,4 +188,30 @@ private function updateAddressData(Address $address, array $addressData) $address->setSameAsBilling(false); $address->setCustomerAddressId(null); } + + /** + * Returns shipping recipient first name. + * + * @param array $details + * @return string + */ + private function getShippingRecipientFirstName(array $details) + { + return isset($details['shippingAddress']['recipientName']) + ? explode(' ', $details['shippingAddress']['recipientName'], 2)[0] + : $details['firstName']; + } + + /** + * Returns shipping recipient last name. + * + * @param array $details + * @return string + */ + private function getShippingRecipientLastName(array $details) + { + return isset($details['shippingAddress']['recipientName']) + ? explode(' ', $details['shippingAddress']['recipientName'], 2)[1] + : $details['lastName']; + } } diff --git a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php index cb911a8396b36..cc79b5b008e6e 100644 --- a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Braintree\Test\Unit\Controller\Paypal; +use Magento\Payment\Model\Method\Logger; use Magento\Quote\Model\Quote; use Magento\Framework\View\Layout; use Magento\Checkout\Model\Session; @@ -63,6 +64,14 @@ class ReviewTest extends \PHPUnit\Framework\TestCase * @var Review */ private $review; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + + /** + * @var Logger|\PHPUnit_Framework_MockObject_MockObject + */ + private $loggerMock; protected function setUp() { @@ -87,6 +96,9 @@ protected function setUp() ->getMock(); $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) ->getMockForAbstractClass(); + $this->loggerMock = $this->getMockBuilder(Logger::class) + ->disableOriginalConstructor() + ->getMock(); $contextMock->expects(self::once()) ->method('getRequest') @@ -102,7 +114,8 @@ protected function setUp() $contextMock, $this->configMock, $this->checkoutSessionMock, - $this->quoteUpdaterMock + $this->quoteUpdaterMock, + $this->loggerMock ); } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php index 7475d81a56142..b67f7d09941ca 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php @@ -165,7 +165,7 @@ private function getDetails(): array 'region' => 'IL', 'postalCode' => '60618', 'countryCodeAlpha2' => 'US', - 'recipientName' => 'John Doe', + 'recipientName' => 'Jane Smith', ], 'billingAddress' => [ 'streetAddress' => '123 Billing Street', @@ -186,9 +186,9 @@ private function getDetails(): array private function updateShippingAddressStep(array $details) { $this->shippingAddress->method('setLastname') - ->with($details['lastName']); + ->with('Smith'); $this->shippingAddress->method('setFirstname') - ->with($details['firstName']); + ->with('Jane'); $this->shippingAddress->method('setEmail') ->with($details['email']); $this->shippingAddress->method('setCollectShippingRates') diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php index cc3dda6e2d7b1..e741070547163 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php @@ -155,38 +155,4 @@ public function modifyMetaLockedDataProvider() { return [[true], [false]]; } - - public function testModifyMetaWithCaching() - { - $this->arrayManagerMock->expects($this->exactly(2)) - ->method('findPath') - ->willReturn(true); - $cacheManager = $this->getMockBuilder(CacheInterface::class) - ->getMockForAbstractClass(); - $cacheManager->expects($this->once()) - ->method('load') - ->with(Categories::CATEGORY_TREE_ID . '_'); - $cacheManager->expects($this->once()) - ->method('save'); - - $modifier = $this->createModel(); - $cacheContextProperty = new \ReflectionProperty( - Categories::class, - 'cacheManager' - ); - $cacheContextProperty->setAccessible(true); - $cacheContextProperty->setValue($modifier, $cacheManager); - - $groupCode = 'test_group_code'; - $meta = [ - $groupCode => [ - 'children' => [ - 'category_ids' => [ - 'sortOrder' => 10, - ], - ], - ], - ]; - $modifier->modifyMeta($meta); - } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index 2dad7e8495b11..a3baf7b14a229 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Model\Locator\LocatorInterface; @@ -11,6 +13,7 @@ use Magento\Framework\App\CacheInterface; use Magento\Framework\DB\Helper as DbHelper; use Magento\Catalog\Model\Category as CategoryModel; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\UrlInterface; use Magento\Framework\Stdlib\ArrayManager; @@ -202,6 +205,7 @@ protected function createNewCategoryModal(array $meta) * * @param array $meta * @return array + * @throws LocalizedException * @since 101.0.0 */ protected function customizeCategoriesField(array $meta) @@ -306,20 +310,64 @@ protected function customizeCategoriesField(array $meta) * * @param string|null $filter * @return array + * @throws LocalizedException * @since 101.0.0 */ protected function getCategoriesTree($filter = null) { - $categoryTree = $this->getCacheManager()->load(self::CATEGORY_TREE_ID . '_' . $filter); - if ($categoryTree) { - return $this->serializer->unserialize($categoryTree); + $storeId = (int) $this->locator->getStore()->getId(); + + $cachedCategoriesTree = $this->getCacheManager() + ->load($this->getCategoriesTreeCacheId($storeId, (string) $filter)); + if (!empty($cachedCategoriesTree)) { + return $this->serializer->unserialize($cachedCategoriesTree); } - $storeId = $this->locator->getStore()->getId(); + $categoriesTree = $this->retrieveCategoriesTree( + $storeId, + $this->retrieveShownCategoriesIds($storeId, (string) $filter) + ); + + $this->getCacheManager()->save( + $this->serializer->serialize($categoriesTree), + $this->getCategoriesTreeCacheId($storeId, (string) $filter), + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Framework\App\Cache\Type\Block::CACHE_TAG + ] + ); + + return $categoriesTree; + } + + /** + * Get cache id for categories tree. + * + * @param int $storeId + * @param string $filter + * @return string + */ + private function getCategoriesTreeCacheId(int $storeId, string $filter = '') : string + { + return self::CATEGORY_TREE_ID + . '_' . (string) $storeId + . '_' . $filter; + } + + /** + * Retrieve filtered list of categories id. + * + * @param int $storeId + * @param string $filter + * @return array + * @throws LocalizedException + */ + private function retrieveShownCategoriesIds(int $storeId, string $filter = '') : array + { /* @var $matchingNamesCollection \Magento\Catalog\Model\ResourceModel\Category\Collection */ $matchingNamesCollection = $this->categoryCollectionFactory->create(); - if ($filter !== null) { + if (!empty($filter)) { $matchingNamesCollection->addAttributeToFilter( 'name', ['like' => $this->dbHelper->addLikeEscape($filter, ['position' => 'any'])] @@ -339,6 +387,19 @@ protected function getCategoriesTree($filter = null) } } + return $shownCategoriesIds; + } + + /** + * Retrieve tree of categories with attributes. + * + * @param int $storeId + * @param array $shownCategoriesIds + * @return array|null + * @throws LocalizedException + */ + private function retrieveCategoriesTree(int $storeId, array $shownCategoriesIds) + { /* @var $collection \Magento\Catalog\Model\ResourceModel\Category\Collection */ $collection = $this->categoryCollectionFactory->create(); @@ -365,15 +426,6 @@ protected function getCategoriesTree($filter = null) $categoryById[$category->getParentId()]['optgroup'][] = &$categoryById[$category->getId()]; } - $this->getCacheManager()->save( - $this->serializer->serialize($categoryById[CategoryModel::TREE_ROOT_ID]['optgroup']), - self::CATEGORY_TREE_ID . '_' . $filter, - [ - \Magento\Catalog\Model\Category::CACHE_TAG, - \Magento\Framework\App\Cache\Type\Block::CACHE_TAG - ] - ); - return $categoryById[CategoryModel::TREE_ROOT_ID]['optgroup']; } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 9cb6d4bd2390d..d37b755e8c57a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2838,7 +2838,8 @@ protected function getProductUrlSuffix($storeId = null) protected function getUrlKey($rowData) { if (!empty($rowData[self::URL_KEY])) { - return $this->productUrl->formatUrlKey($rowData[self::URL_KEY]); + $urlKey = (string) $rowData[self::URL_KEY]; + return trim(strtolower($urlKey)); } if (!empty($rowData[self::COL_NAME])) { diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php index 64f032f2d16e9..3e858e96500c5 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php @@ -3,9 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogUrlRewrite\Model; -use Magento\Store\Model\Store; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; class ProductUrlPathGenerator { @@ -19,36 +26,36 @@ class ProductUrlPathGenerator protected $productUrlSuffix = []; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $scopeConfig; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator + * @var CategoryUrlPathGenerator */ protected $categoryUrlPathGenerator; /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface + * @var ProductRepositoryInterface */ protected $productRepository; /** - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param StoreManagerInterface $storeManager + * @param ScopeConfigInterface $scopeConfig * @param CategoryUrlPathGenerator $categoryUrlPathGenerator - * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param ProductRepositoryInterface $productRepository */ public function __construct( - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator, - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + StoreManagerInterface $storeManager, + ScopeConfigInterface $scopeConfig, + CategoryUrlPathGenerator $categoryUrlPathGenerator, + ProductRepositoryInterface $productRepository ) { $this->storeManager = $storeManager; $this->scopeConfig = $scopeConfig; @@ -59,8 +66,8 @@ public function __construct( /** * Retrieve Product Url path (with category if exists) * - * @param \Magento\Catalog\Model\Product $product - * @param \Magento\Catalog\Model\Category $category + * @param Product $product + * @param Category $category * * @return string */ @@ -80,10 +87,10 @@ public function getUrlPath($product, $category = null) /** * Prepare URL Key with stored product data (fallback for "Use Default Value" logic) * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string */ - protected function prepareProductDefaultUrlKey(\Magento\Catalog\Model\Product $product) + protected function prepareProductDefaultUrlKey(Product $product) { $storedProduct = $this->productRepository->getById($product->getId()); $storedUrlKey = $storedProduct->getUrlKey(); @@ -93,9 +100,9 @@ protected function prepareProductDefaultUrlKey(\Magento\Catalog\Model\Product $p /** * Retrieve Product Url path with suffix * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @param int $storeId - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @return string */ public function getUrlPathWithSuffix($product, $storeId, $category = null) @@ -106,8 +113,8 @@ public function getUrlPathWithSuffix($product, $storeId, $category = null) /** * Get canonical product url path * - * @param \Magento\Catalog\Model\Product $product - * @param \Magento\Catalog\Model\Category|null $category + * @param Product $product + * @param Category|null $category * @return string */ public function getCanonicalUrlPath($product, $category = null) @@ -119,7 +126,7 @@ public function getCanonicalUrlPath($product, $category = null) /** * Generate product url key based on url_key entered by merchant or product name * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string|null */ public function getUrlKey($product) @@ -131,13 +138,15 @@ public function getUrlKey($product) /** * Prepare url key for product * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string */ - protected function prepareProductUrlKey(\Magento\Catalog\Model\Product $product) + protected function prepareProductUrlKey(Product $product) { - $urlKey = $product->getUrlKey(); - return $product->formatUrlKey($urlKey === '' || $urlKey === null ? $product->getName() : $urlKey); + $urlKey = (string)$product->getUrlKey(); + $urlKey = trim(strtolower($urlKey)); + + return $urlKey ?: $product->formatUrlKey($product->getName()); } /** @@ -155,7 +164,7 @@ protected function getProductUrlSuffix($storeId = null) if (!isset($this->productUrlSuffix[$storeId])) { $this->productUrlSuffix[$storeId] = $this->scopeConfig->getValue( self::XML_PATH_PRODUCT_URL_SUFFIX, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php index 956fe1b88e0ad..14f30eb7607d3 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogUrlRewrite\Test\Unit\Model; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; @@ -72,10 +73,11 @@ protected function setUp() public function getUrlPathDataProvider() { return [ - 'path based on url key' => ['url-key', null, 'url-key'], - 'path based on product name 1' => ['', 'product-name', 'product-name'], - 'path based on product name 2' => [null, 'product-name', 'product-name'], - 'path based on product name 3' => [false, 'product-name', 'product-name'] + 'path based on url key uppercase' => ['Url Key', null, 0, 'url key'], + 'path based on url key' => ['url-key', null, 0, 'url-key'], + 'path based on product name 1' => ['', 'product-name', 1, 'product-name'], + 'path based on product name 2' => [null, 'product-name', 1, 'product-name'], + 'path based on product name 3' => [false, 'product-name', 1, 'product-name'] ]; } @@ -83,15 +85,17 @@ public function getUrlPathDataProvider() * @dataProvider getUrlPathDataProvider * @param string|null|bool $urlKey * @param string|null|bool $productName + * @param int $formatterCalled * @param string $result */ - public function testGetUrlPath($urlKey, $productName, $result) + public function testGetUrlPath($urlKey, $productName, $formatterCalled, $result) { $this->product->expects($this->once())->method('getData')->with('url_path') ->will($this->returnValue(null)); $this->product->expects($this->any())->method('getUrlKey')->will($this->returnValue($urlKey)); $this->product->expects($this->any())->method('getName')->will($this->returnValue($productName)); - $this->product->expects($this->once())->method('formatUrlKey')->will($this->returnArgument(0)); + $this->product->expects($this->exactly($formatterCalled)) + ->method('formatUrlKey')->will($this->returnArgument(0)); $this->assertEquals($result, $this->productUrlPathGenerator->getUrlPath($this->product, null)); } diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index ce976d3f62c74..9c9f04185477e 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -375,45 +375,17 @@ protected function getAttributesMeta(Type $entityType) return $meta; } - /** - * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... - * - * @param Attribute $customerAttribute - * @return bool - */ - private function canShowAttributeInForm(AbstractAttribute $customerAttribute) - { - $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; - - if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { - return is_array($customerAttribute->getUsedInForms()) && - ( - (in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || - (in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) - ); - } else { - return is_array($customerAttribute->getUsedInForms()) && - in_array('customer_address_edit', $customerAttribute->getUsedInForms()); - } - } - /** * Detect can we show attribute on specific form or not * * @param Attribute $customerAttribute * @return bool */ - private function canShowAttribute(AbstractAttribute $customerAttribute) + private function canShowAttribute(AbstractAttribute $customerAttribute): bool { - $userDefined = (bool) $customerAttribute->getIsUserDefined(); - if (!$userDefined) { - return $customerAttribute->getIsVisible(); - } - - $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); - - return ($this->allowToShowHiddenAttributes && $canShowOnForm) || - (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + return $this->allowToShowHiddenAttributes && (bool) $customerAttribute->getIsUserDefined() + ? true + : (bool) $customerAttribute->getIsVisible(); } /** diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php index 50c21379054bf..f6a3ecb810aa5 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php @@ -1295,16 +1295,15 @@ public function testGetDataWithVisibleAttributesWithAccountEdit() $meta = $dataProvider->getMeta(); $this->assertNotEmpty($meta); - $this->assertEquals($this->getExpectationForVisibleAttributes(false), $meta); + $this->assertEquals($this->getExpectationForVisibleAttributes(), $meta); } /** * Retrieve all customer variations of attributes with all variations of visibility * - * @param bool $isRegistration * @return array */ - private function getCustomerAttributeExpectations($isRegistration) + private function getCustomerAttributeExpectations() { return [ self::ATTRIBUTE_CODE . "_1" => [ @@ -1314,7 +1313,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', 'options' => 'test-options', - 'visible' => !$isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1351,7 +1350,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'config' => [ 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', - 'visible' => $isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1374,7 +1373,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'config' => [ 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', - 'visible' => $isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1397,14 +1396,13 @@ private function getCustomerAttributeExpectations($isRegistration) /** * Retrieve all variations of attributes with all variations of visibility * - * @param bool $isRegistration * @return array */ - private function getExpectationForVisibleAttributes($isRegistration = true) + private function getExpectationForVisibleAttributes() { return [ 'customer' => [ - 'children' => $this->getCustomerAttributeExpectations($isRegistration), + 'children' => $this->getCustomerAttributeExpectations(), ], 'address' => [ 'children' => [ diff --git a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php index 64305cfce9b08..4aed7818b5095 100644 --- a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php +++ b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php @@ -92,9 +92,15 @@ public function execute(\Magento\Framework\Event\Observer $observer) if ($purchasedLink->getId()) { return $this; } + $storeId = $orderItem->getOrder()->getStoreId(); + $orderStatusToEnableItem = $this->_scopeConfig->getValue( + \Magento\Downloadable\Model\Link\Purchased\Item::XML_PATH_ORDER_ITEM_STATUS, + ScopeInterface::SCOPE_STORE, + $storeId + ); if (!$product) { $product = $this->_createProductModel()->setStoreId( - $orderItem->getOrder()->getStoreId() + $storeId )->load( $orderItem->getProductId() ); @@ -150,6 +156,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) )->setNumberOfDownloadsBought( $numberOfDownloads )->setStatus( + \Magento\Sales\Model\Order\Item::STATUS_PENDING == $orderStatusToEnableItem ? + \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_AVAILABLE : \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_PENDING )->setCreatedAt( $orderItem->getCreatedAt() diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php index 06a8a5b680bf4..259f00ec5a9c5 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php @@ -3,9 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Model\Payflow\Service\Response; use Magento\Framework\DataObject; +use Magento\Framework\Intl\DateTimeFactory; use Magento\Payment\Model\Method\Logger; use Magento\Paypal\Model\Payflow\Service\Response\Handler\HandlerInterface; use Magento\Framework\Session\Generic; @@ -18,6 +21,8 @@ /** * Class Transaction + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Transaction { @@ -51,6 +56,11 @@ class Transaction */ private $logger; + /** + * @var DateTimeFactory + */ + private $dateTimeFactory; + /** * @param Generic $sessionTransparent * @param CartRepositoryInterface $quoteRepository @@ -58,6 +68,7 @@ class Transaction * @param PaymentMethodManagementInterface $paymentManagement * @param HandlerInterface $errorHandler * @param Logger $logger + * @param DateTimeFactory $dateTimeFactory */ public function __construct( Generic $sessionTransparent, @@ -65,7 +76,8 @@ public function __construct( Transparent $transparent, PaymentMethodManagementInterface $paymentManagement, HandlerInterface $errorHandler, - Logger $logger + Logger $logger, + DateTimeFactory $dateTimeFactory ) { $this->sessionTransparent = $sessionTransparent; $this->quoteRepository = $quoteRepository; @@ -73,6 +85,7 @@ public function __construct( $this->paymentManagement = $paymentManagement; $this->errorHandler = $errorHandler; $this->logger = $logger; + $this->dateTimeFactory = $dateTimeFactory; } /** @@ -114,8 +127,45 @@ public function savePaymentInQuote($response) $payment->setData(OrderPaymentInterface::CC_TYPE, $response->getData(OrderPaymentInterface::CC_TYPE)); $payment->setAdditionalInformation(Payflowpro::PNREF, $response->getData(Payflowpro::PNREF)); + $expDate = $response->getData('expdate'); + $expMonth = $this->getCcExpMonth($expDate); + $payment->setCcExpMonth($expMonth); + $expYear = $this->getCcExpYear($expDate); + $payment->setCcExpYear($expYear); + $this->errorHandler->handle($payment, $response); $this->paymentManagement->set($quote->getId(), $payment); } + + /** + * Extracts expiration month from PayPal response expiration date. + * + * @param string $expDate format {MMYY} + * @return int + */ + private function getCcExpMonth(string $expDate): int + { + return (int)substr($expDate, 0, 2); + } + + /** + * Extracts expiration year from PayPal response expiration date. + * + * @param string $expDate format {MMYY} + * @return int + */ + private function getCcExpYear(string $expDate): int + { + $last2YearDigits = (int)substr($expDate, 2, 2); + $currentDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); + $first2YearDigits = (int)substr($currentDate->format('Y'), 0, 2); + + // case when credit card expires at next century + if ((int)$currentDate->format('y') > $last2YearDigits) { + $first2YearDigits++; + } + + return 100 * $first2YearDigits + $last2YearDigits; + } } diff --git a/app/code/Magento/Rule/Model/ResourceModel/Rule/Collection/AbstractCollection.php b/app/code/Magento/Rule/Model/ResourceModel/Rule/Collection/AbstractCollection.php index b3d761b378d94..e0468f17e587a 100644 --- a/app/code/Magento/Rule/Model/ResourceModel/Rule/Collection/AbstractCollection.php +++ b/app/code/Magento/Rule/Model/ResourceModel/Rule/Collection/AbstractCollection.php @@ -83,11 +83,21 @@ public function addWebsiteFilter($websiteId) if ($website instanceof \Magento\Store\Model\Website) { $websiteIds[$index] = $website->getId(); } + $websiteIds[$index] = (int) $websiteIds[$index]; } + + $websiteSelect = $this->getConnection()->select(); + $websiteSelect->from( + $this->getTable($entityInfo['associations_table']), + [$entityInfo['rule_id_field']] + )->distinct( + true + )->where( + $this->getConnection()->quoteInto($entityInfo['entity_id_field'] . ' IN (?)', $websiteIds) + ); $this->getSelect()->join( - ['website' => $this->getTable($entityInfo['associations_table'])], - $this->getConnection()->quoteInto('website.' . $entityInfo['entity_id_field'] . ' IN (?)', $websiteIds) - . ' AND main_table.' . $entityInfo['rule_id_field'] . ' = website.' . $entityInfo['rule_id_field'], + ['website' => $websiteSelect], + 'main_table.' . $entityInfo['rule_id_field'] . ' = website.' . $entityInfo['rule_id_field'], [] ); } diff --git a/app/code/Magento/Rule/Test/Unit/Model/ResourceModel/Rule/Collection/AbstractCollectionTest.php b/app/code/Magento/Rule/Test/Unit/Model/ResourceModel/Rule/Collection/AbstractCollectionTest.php deleted file mode 100644 index c4e7a591212c5..0000000000000 --- a/app/code/Magento/Rule/Test/Unit/Model/ResourceModel/Rule/Collection/AbstractCollectionTest.php +++ /dev/null @@ -1,200 +0,0 @@ -_entityFactoryMock = $this->createMock(\Magento\Framework\Data\Collection\EntityFactoryInterface::class); - $this->_loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->_fetchStrategyMock = $this->createMock( - \Magento\Framework\Data\Collection\Db\FetchStrategyInterface::class - ); - $this->_managerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $this->_db = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, - [], - '', - false, - false, - true, - ['__sleep', '__wakeup', 'getTable'] - ); - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->abstractCollection = $this->getMockForAbstractClass( - \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection::class, - [ - 'entityFactory' => $this->_entityFactoryMock, - 'logger' => $this->_loggerMock, - 'fetchStrategy' => $this->_fetchStrategyMock, - 'eventManager' => $this->_managerMock, - null, - $this->_db - ], - '', - false, - false, - true, - ['__sleep', '__wakeup', '_getAssociatedEntityInfo', 'getConnection', 'getSelect', 'getTable'] - ); - } - - /** - * @return array - */ - public function addWebsitesToResultDataProvider() - { - return [ - [null, true], - [true, true], - [false, false] - ]; - } - - /** - * @dataProvider addWebsitesToResultDataProvider - */ - public function testAddWebsitesToResult($flag, $expectedResult) - { - $this->abstractCollection->addWebsitesToResult($flag); - $this->assertEquals($expectedResult, $this->abstractCollection->getFlag('add_websites_to_result')); - } - - protected function _prepareAddFilterStubs() - { - $entityInfo = []; - $entityInfo['entity_id_field'] = 'entity_id'; - $entityInfo['rule_id_field'] = 'rule_id'; - $entityInfo['associations_table'] = 'assoc_table'; - - $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); - $select = $this->createMock(\Magento\Framework\DB\Select::class); - $collectionSelect = $this->createMock(\Magento\Framework\DB\Select::class); - - $connection->expects($this->any()) - ->method('select') - ->will($this->returnValue($select)); - - $select->expects($this->any()) - ->method('from') - ->will($this->returnSelf()); - - $select->expects($this->any()) - ->method('where') - ->will($this->returnSelf()); - - $this->abstractCollection->expects($this->any()) - ->method('getConnection') - ->will($this->returnValue($connection)); - - $this->_db->expects($this->any()) - ->method('getTable') - ->will($this->returnArgument(0)); - - $this->abstractCollection->expects($this->any()) - ->method('getSelect') - ->will($this->returnValue($collectionSelect)); - - $this->abstractCollection->expects($this->any()) - ->method('_getAssociatedEntityInfo') - ->will($this->returnValue($entityInfo)); - } - - public function testAddWebsiteFilter() - { - $this->_prepareAddFilterStubs(); - $website = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getId', '__sleep', '__wakeup']); - - $website->expects($this->any()) - ->method('getId') - ->will($this->returnValue(1)); - - $this->assertInstanceOf( - \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection::class, - $this->abstractCollection->addWebsiteFilter($website) - ); - } - - public function testAddWebsiteFilterArray() - { - $this->selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->connectionMock->expects($this->atLeastOnce()) - ->method('quoteInto') - ->with($this->equalTo('website. IN (?)'), $this->equalTo(['2', '3'])) - ->willReturn(true); - - $this->abstractCollection->expects($this->atLeastOnce())->method('getSelect')->willReturn($this->selectMock); - $this->abstractCollection->expects($this->atLeastOnce())->method('getConnection') - ->willReturn($this->connectionMock); - - $this->assertInstanceOf( - \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection::class, - $this->abstractCollection->addWebsiteFilter(['2', '3']) - ); - } - - public function testAddFieldToFilter() - { - $this->_prepareAddFilterStubs(); - $result = $this->abstractCollection->addFieldToFilter('website_ids', []); - $this->assertNotNull($result); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php new file mode 100644 index 0000000000000..23fd8d7fe324e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php @@ -0,0 +1,63 @@ + 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1 +]; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +/** @var ProductInterface $product */ +$product = $objectManager->create(ProductInterface::class); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Чудовий продукт без Url Key') + ->setSku('ukrainian-without-url-key') + ->setPrice(10) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData($stockDataConfig); +try { + $productRepository->save($product); +} catch (\Exception $e) { + // problems during save +}; + +/** @var ProductInterface $product */ +$product = $objectManager->create(ProductInterface::class); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Надзвичайний продукт з Url Key') + ->setSku('ukrainian-with-url-key') + ->setPrice(10) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData($stockDataConfig) + ->setUrlKey('надзвичайний продукт на кожен день'); +try { + $productRepository->save($product); +} catch (\Exception $e) { + // problems during save +}; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php new file mode 100644 index 0000000000000..d4592430c0e94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php @@ -0,0 +1,37 @@ +getInstance()->reinitialize(); + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->get(ProductRepositoryInterface::class); + +$productSkus = [ + 'ukrainian-with-url-key', + 'ukrainian-without-url-key', +]; +try { + foreach ($productSkus as $sku) { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } +} catch (NoSuchEntityException $e) { + // nothing to delete +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 6e361dfb05de0..cb96910ec86e1 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -28,11 +28,13 @@ use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; use Magento\Store\Model\Store; use Psr\Log\LoggerInterface; +use Magento\Framework\Exception\NoSuchEntityException; /** * Class ProductTest * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @magentoAppArea adminhtml * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_catalog_product_reindex_schedule.php * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -71,6 +73,11 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase */ private $logger; + /** + * @var array + */ + private $importedProducts; + /** * @inheritdoc */ @@ -84,10 +91,27 @@ protected function setUp() \Magento\CatalogImportExport\Model\Import\Product::class, ['logger' => $this->logger] ); + $this->importedProducts = []; parent::setUp(); } + protected function tearDown() + { + /* We rollback here the products created during the Import because they were + created during test execution and we do not have the rollback for them */ + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + foreach ($this->importedProducts as $productSku) { + try { + $product = $productRepository->get($productSku, false, null, true); + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + // nothing to delete + } + } + } + /** * Options for assertion * @@ -271,6 +295,8 @@ public function testStockState() * @param string $importFile * @param string $sku * @param int $expectedOptionsQty + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException * @magentoAppIsolation enabled */ public function testSaveCustomOptions($importFile, $sku, $expectedOptionsQty) @@ -1244,6 +1270,8 @@ public function testProductPositionInCategory() * @magentoAppIsolation enabled * @magentoDataFixture Magento/Catalog/_files/category_product.php * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testNewProductPositionInCategory() { @@ -1369,6 +1397,7 @@ protected function loadCategoryByName($categoryName) * @dataProvider validateUrlKeysDataProvider * @param $importFile string * @param $expectedErrors array + * @throws \Magento\Framework\Exception\LocalizedException */ public function testValidateUrlKeys($importFile, $expectedErrors) { @@ -1597,12 +1626,13 @@ public function testImportWithoutUrlKeys() * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function testImportWithUrlKeysWithSpaces() { $products = [ - 'simple1' => 'url-key-with-spaces1', - 'simple2' => 'url-key-with-spaces2', + 'simple1' => 'url key with spaces1', + 'simple2' => 'url key with spaces2', ]; $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); @@ -1629,6 +1659,52 @@ public function testImportWithUrlKeysWithSpaces() } } + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_non_latin_url_key.php + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testImportWithNonLatinUrlKeys() + { + $productsCreatedByFixture = [ + 'ukrainian-with-url-key' => 'nove-im-ja-pislja-importu-scho-stane-url-key', + 'ukrainian-without-url-key' => 'новий url key після імпорту', + ]; + $productsImportedByCsv = [ + 'imported-ukrainian-with-url-key' => 'імпортований продукт', + 'imported-ukrainian-without-url-key' => 'importovanij-produkt-bez-url-key', + ]; + $productSkuMap = array_merge($productsCreatedByFixture, $productsImportedByCsv); + $this->importedProducts = array_keys($productsImportedByCsv); + + $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + \Magento\ImportExport\Model\Import\Source\Csv::class, + [ + 'file' => __DIR__ . '/_files/products_to_import_with_non_latin_url_keys.csv', + 'directory' => $directory, + ] + ); + + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE, 'entity' => 'catalog_product'] + ) + ->setSource($source) + ->validateData(); + + $this->assertEquals($errors->getErrorsCount(), 0); + $this->_model->importData(); + + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + foreach ($productSkuMap as $productSku => $productUrlKey) { + $this->assertEquals($productUrlKey, $productRepository->get($productSku)->getUrlKey()); + } + } + /** * Make sure the absence of a url_key column in the csv file won't erase the url key of the existing products. * To reach the goal we need to not send the name column, as the url key is generated from it. @@ -1844,6 +1920,7 @@ public function testProductWithWrappedAdditionalAttributes() * * @param string $fileName * @param int $expectedErrors + * @throws \Magento\Framework\Exception\LocalizedException */ private function importDataForMediaTest(string $fileName, int $expectedErrors = 0) { @@ -2266,6 +2343,7 @@ public function testImportWithBackordersDisabled() * Import file by providing import filename in parameters * * @param string $fileName + * @throws \Magento\Framework\Exception\LocalizedException */ private function importFile(string $fileName) { @@ -2297,6 +2375,7 @@ private function importFile(string $fileName) * Import file with non-existing images and skip-errors strategy. * * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function testImportWithSkipErrorsAndNonExistingImage() { @@ -2382,6 +2461,7 @@ public function testImportProductWithUpdateUrlKey() * @magentoDataFixture mediaImportImageFixture * @magentoAppIsolation enabled * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function testSaveProductOnImportNonExistingImage() { @@ -2414,6 +2494,8 @@ public function testSaveProductOnImportNonExistingImage() * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testImportProductWithContinueOnError() { diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv new file mode 100644 index 0000000000000..8b324a5330779 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv @@ -0,0 +1,5 @@ +sku,product_type,store_view_code,name,price,attribute_set_code,url_key +imported-ukrainian-with-url-key,simple,,"Імпортований продукт з Url Key",50,Default,"імпортований продукт" +imported-ukrainian-without-url-key,simple,,"Імпортований продукт без Url Key",55,Default, +ukrainian-without-url-key,simple,,"Чудовий продукт без Url Key",55,Default,"новий url key після імпорту" +ukrainian-with-url-key,simple,,"Нове ім'я після імпорту що стане url key",55,Default, \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php new file mode 100644 index 0000000000000..e2bb1d7b8f7c6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php @@ -0,0 +1,135 @@ + $paypalExpDate, + 'AMT' => '0.00', + 'RESPMSG' => 'Verified', + 'CVV2MATCH' => 'Y', + 'PNREF' => 'A10AAD866C87', + 'SECURETOKEN' => '3HYEHfG06skydAdBXbpIl8QJZ', + 'AVSDATA' => 'YNY', + 'RESULT' => '0', + 'IAVS' => 'N', + 'AVSADDR' => 'Y', + 'SECURETOKENID' => 'yqanLisRZbI0HAG8q3SbbKbhiwjNZAGf', + ]; + + $quote = $this->getQuote($reservedOrderId); + $this->getRequest()->setPostValue($postData); + + /** @var Session $checkoutSession */ + $checkoutSession = $this->_objectManager->get(GenericSession::class); + $checkoutSession->setQuoteId($quote->getId()); + $this->setCurrentDateTime($currentDateTime); + + $this->dispatch('paypal/transparent/response'); + + /** @var PaymentMethodManagementInterface $paymentManagment */ + $paymentManagment = $this->_objectManager->get(PaymentMethodManagementInterface::class); + $payment = $paymentManagment->get($quote->getId()); + + $this->assertEquals($expectedCcMonth, $payment->getCcExpMonth()); + $this->assertEquals($expectedCcYear, $payment->getCcExpYear()); + } + + /** + * @return array + */ + public function paymentCcExpirationDateDataProvider(): array + { + return [ + 'Expiration year in current century' => [ + 'currentDateTime' => '2019-07-05 00:00:00', + 'paypalExpDate' => '0321', + 'expectedCcMonth' => 3, + 'expectedCcYear' => 2021 + ], + 'Expiration year in next century' => [ + 'currentDateTime' => '2099-01-01 00:00:00', + 'paypalExpDate' => '1002', + 'expectedCcMonth' => 10, + 'expectedCcYear' => 2102 + ] + ]; + } + + /** + * Sets current date and time. + * + * @param string $date + */ + private function setCurrentDateTime(string $dateTime) + { + $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC')); + $dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $dateTimeFactory->method('create') + ->willReturn($dateTime); + + $this->_objectManager->addSharedInstance($dateTimeFactory, DateTimeFactory::class); + } + + /** + * Gets quote by reserved order ID. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote(string $reservedOrderId): CartInterface + { + $searchCriteria = $this->_objectManager->get(SearchCriteriaBuilder::class) + ->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php index f619193d4921f..9ea97b6e939cc 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php @@ -5,12 +5,39 @@ */ namespace Magento\SalesRule\Model\ResourceModel\Rule; +use Magento\Config\Model\Config\Backend\Admin\Custom as AdminBackendConfig; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ -class CollectionTest extends \PHPUnit\Framework\TestCase +class CollectionTest extends TestCase { + /** + * @var Collection + */ + private $collection; + + /** + * @var string + */ + private $defaultTimezone; + + /** + * @inheritDoc + */ + protected function setUp() + { + $scopeConfig = Bootstrap::getObjectManager()->get(ScopeConfigInterface::class); + $this->defaultTimezone = $scopeConfig->getValue(AdminBackendConfig::XML_PATH_GENERAL_LOCALE_TIMEZONE); + + $this->collection = Bootstrap::getObjectManager()->create(Collection::class); + } + /** * @magentoDataFixture Magento/SalesRule/_files/rules.php * @magentoDataFixture Magento/SalesRule/_files/coupons.php @@ -21,12 +48,8 @@ class CollectionTest extends \PHPUnit\Framework\TestCase */ public function testSetValidationFilter($couponCode, $expectedItems) { - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $items = array_values($collection->setValidationFilter(1, 0, $couponCode)->getItems()); - - $ids = []; + /** @var \Magento\SalesRule\Model\Rule[] $items */ + $items = array_values($this->collection->setValidationFilter(1, 0, $couponCode)->getItems()); $this->assertEquals( count($expectedItems), @@ -34,6 +57,7 @@ public function testSetValidationFilter($couponCode, $expectedItems) 'Invalid number of items in the result collection' ); + $ids = []; foreach ($items as $key => $item) { $this->assertEquals($expectedItems[$key], $item->getName()); $this->assertFalse( @@ -71,7 +95,7 @@ public function setValidationFilterDataProvider() */ public function testSetValidationFilterWithGroup() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\SalesRule\Model\Rule $rule */ $rule = $objectManager->get(\Magento\Framework\Registry::class) @@ -82,13 +106,8 @@ public function testSetValidationFilterWithGroup() $quote->load('test_order_item_with_items', 'reserved_order_id'); //gather only the existing rules that obey the validation filter - /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */ - $ruleCollection = $objectManager->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $appliedRulesArray = array_keys( - $ruleCollection->setValidationFilter( + $this->collection->setValidationFilter( $quote->getStore()->getWebsiteId(), 0, '', @@ -108,7 +127,7 @@ public function testSetValidationFilterWithGroup() */ public function testSetValidationFilterAnyCategory() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\SalesRule\Model\Rule $rule */ $rule = $objectManager->get(\Magento\Framework\Registry::class) @@ -119,13 +138,8 @@ public function testSetValidationFilterAnyCategory() $quote->load('test_order_item_with_items', 'reserved_order_id'); //gather only the existing rules that obey the validation filter - /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */ - $ruleCollection = $objectManager->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $appliedRulesArray = array_keys( - $ruleCollection->setValidationFilter( + $this->collection->setValidationFilter( $quote->getStore()->getWebsiteId(), 0, '', @@ -146,20 +160,15 @@ public function testSetValidationFilterAnyCategory() */ public function testSetValidationFilterOther() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\Quote\Model\Quote $quote */ $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); $quote->load('test_order_item_with_items', 'reserved_order_id'); //gather only the existing rules that obey the validation filter - /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */ - $ruleCollection = $objectManager->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $appliedRulesArray = array_keys( - $ruleCollection->setValidationFilter( + $this->collection->setValidationFilter( $quote->getStore()->getWebsiteId(), 0, '', @@ -181,11 +190,8 @@ public function testSetValidationFilterOther() public function testMultiRulesWithTimezone() { $this->setSpecificTimezone('Europe/Kiev'); - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $collection->addWebsiteGroupDateFilter(1, 0); - $items = array_values($collection->getItems()); + $this->collection->addWebsiteGroupDateFilter(1, 0); + $items = array_values($this->collection->getItems()); $this->assertNotEmpty($items); } @@ -200,11 +206,8 @@ public function testMultiRulesWithTimezone() public function testMultiRulesWithDifferentTimezone() { $this->setSpecificTimezone('Australia/Sydney'); - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $collection->addWebsiteGroupDateFilter(1, 0); - $items = array_values($collection->getItems()); + $this->collection->addWebsiteGroupDateFilter(1, 0); + $items = array_values($this->collection->getItems()); $this->assertNotEmpty($items); } @@ -224,7 +227,7 @@ protected function setSpecificTimezone($timezone) ] ] ]; - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Config\Model\Config\Factory::class) + Bootstrap::getObjectManager()->get(\Magento\Config\Model\Config\Factory::class) ->create() ->addData($localeData) ->save(); @@ -239,11 +242,9 @@ protected function setSpecificTimezone($timezone) */ public function testAddAttributeInConditionFilterPositive() { - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $collection->addAttributeInConditionFilter('attribute_for_sales_rule_1'); - $item = $collection->getFirstItem(); + $this->collection->addAttributeInConditionFilter('attribute_for_sales_rule_1'); + /** @var \Magento\SalesRule\Model\Rule $item */ + $item = $this->collection->getFirstItem(); $this->assertEquals('50% Off on some attribute', $item->getName()); } @@ -256,16 +257,57 @@ public function testAddAttributeInConditionFilterPositive() */ public function testAddAttributeInConditionFilterNegative() { - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $collection->addAttributeInConditionFilter('attribute_for_sales_rule_2'); - $this->assertEquals(0, $collection->count()); + $this->collection->addAttributeInConditionFilter('attribute_for_sales_rule_2'); + $this->assertEquals(0, $this->collection->count()); + } + + /** + * @magentoAppIsolation disabled + * @magentoDataFixture Magento/SalesRule/_files/multi_websites_rules.php + * @dataProvider addWebsiteFilterDataProvider + * @param string[] $websiteCodes + * @param int $count + */ + public function testAddWebsiteFilter(array $websiteCodes, int $count) + { + $websiteRepository = Bootstrap::getObjectManager()->get(WebsiteRepositoryInterface::class); + $websiteIds = []; + foreach ($websiteCodes as $websiteCode) { + $websiteIds[] = (int) $websiteRepository->get($websiteCode)->getId(); + } + + $this->collection->addWebsiteFilter($websiteIds); + $this->assertEquals($count, $this->collection->getSize()); + $this->assertCount($count, $this->collection->getItems()); } - public function tearDown() + /** + * @return array + */ + public function addWebsiteFilterDataProvider(): array + { + return [ + [ + ['base'], + 4, + ], + [ + ['test'], + 2, + ], + [ + ['base', 'test'], + 5, + ], + ]; + } + + /** + * @inheritDoc + */ + protected function tearDown() { // restore default timezone - $this->setSpecificTimezone('America/Los_Angeles'); + $this->setSpecificTimezone($this->defaultTimezone); } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php new file mode 100644 index 0000000000000..a43df3d67c077 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php @@ -0,0 +1,16 @@ +setWebsiteIds($website->getId()) + ->save(); + +/** @var \Magento\SalesRule\Model\Rule $rule3 */ +$rule3->setWebsiteIds(implode(',', [1, $website->getId()])) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php new file mode 100644 index 0000000000000..9e0e01b9fc51c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php @@ -0,0 +1,8 @@ +create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule1 */ +$rule1 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule1->setName( '#1' )->setIsActive( 1 @@ -27,9 +27,9 @@ )->setSortOrder(1) ->save(); -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule2 */ +$rule2 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule2->setName( '#2' )->setIsActive( 1 @@ -50,9 +50,9 @@ )->setSortOrder(2) ->save(); -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule3 */ +$rule3 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule3->setName( '#3' )->setIsActive( 1 @@ -73,9 +73,9 @@ )->setSortOrder(3) ->save(); -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule4 */ +$rule4 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule4->setName( '#4' )->setIsActive( 1 @@ -96,9 +96,9 @@ )->setSortOrder(4) ->save(); -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule5 */ +$rule5 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule5->setName( '#5' )->setIsActive( 1 diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml index 4790ce24fd87f..62406b6cbd30a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml @@ -69,5 +69,8 @@ dev/build/publication/sanity/ce.xml + + dev/composer.lock + diff --git a/lib/internal/Magento/Framework/App/DocRootLocator.php b/lib/internal/Magento/Framework/App/DocRootLocator.php index 6fb35c42f1330..d73baf8e4e742 100644 --- a/lib/internal/Magento/Framework/App/DocRootLocator.php +++ b/lib/internal/Magento/Framework/App/DocRootLocator.php @@ -3,10 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\App; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\ReadFactory; /** @@ -20,18 +22,26 @@ class DocRootLocator private $request; /** + * @deprecated * @var ReadFactory */ private $readFactory; + /** + * @var Filesystem + */ + private $filesystem; + /** * @param RequestInterface $request * @param ReadFactory $readFactory + * @param Filesystem|null $filesystem */ - public function __construct(RequestInterface $request, ReadFactory $readFactory) + public function __construct(RequestInterface $request, ReadFactory $readFactory, Filesystem $filesystem = null) { $this->request = $request; $this->readFactory = $readFactory; + $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class); } /** @@ -42,7 +52,8 @@ public function __construct(RequestInterface $request, ReadFactory $readFactory) public function isPub() { $rootBasePath = $this->request->getServer('DOCUMENT_ROOT'); - $readDirectory = $this->readFactory->create(DirectoryList::ROOT); - return (substr($rootBasePath, -strlen('/pub')) === '/pub') && !$readDirectory->isExist($rootBasePath . 'setup'); + $readDirectory = $this->filesystem->getDirectoryRead(DirectoryList::ROOT); + + return (substr($rootBasePath, -\strlen('/pub')) === '/pub') && ! $readDirectory->isExist('setup'); } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php index 23afbbc73d2b9..ef4152ba2e49e 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php @@ -8,6 +8,9 @@ use Magento\Framework\App\DocRootLocator; +/** + * Test for Magento\Framework\App\DocRootLocator class. + */ class DocRootLocatorTest extends \PHPUnit\Framework\TestCase { /** @@ -21,11 +24,15 @@ public function testIsPub($path, $isExist, $result) { $request = $this->createMock(\Magento\Framework\App\Request\Http::class); $request->expects($this->once())->method('getServer')->willReturn($path); + + $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class); + $reader = $this->createMock(\Magento\Framework\Filesystem\Directory\Read::class); + $filesystem = $this->createMock(\Magento\Framework\Filesystem::class); + $filesystem->expects($this->once())->method('getDirectoryRead')->willReturn($reader); $reader->expects($this->any())->method('isExist')->willReturn($isExist); - $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class); - $readFactory->expects($this->once())->method('create')->willReturn($reader); - $model = new DocRootLocator($request, $readFactory); + + $model = new DocRootLocator($request, $readFactory, $filesystem); $this->assertSame($result, $model->isPub()); }