From fa2c3b5f5cb37f04c12e12b8bf1673ac0c13a5bb Mon Sep 17 00:00:00 2001 From: Artem Klimov Date: Sat, 30 Jun 2018 15:14:03 +0300 Subject: [PATCH 001/310] Alphabetize Schema Fields#11 --- lib/internal/Magento/Framework/GraphQl/Config.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/internal/Magento/Framework/GraphQl/Config.php b/lib/internal/Magento/Framework/GraphQl/Config.php index 8d40d7fa6bb49..e6dc128e4dd43 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config.php +++ b/lib/internal/Magento/Framework/GraphQl/Config.php @@ -53,6 +53,11 @@ public function getConfigElement(string $configElementName) : ConfigElementInter sprintf('Config element "%s" is not declared in GraphQL schema', $configElementName) ); } + + if (!empty($data['fields']) && is_array($data['fields'])) { + ksort($data['fields']); + } + return $this->configElementFactory->createFromConfigData($data); } From 6a68bd880131a22bacdbf18b25f274c5c8cc236b Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Wed, 25 Jul 2018 13:08:12 +0200 Subject: [PATCH 002/310] Query complexity limiter introduced --- app/code/Magento/GraphQl/etc/di.xml | 6 +++ .../GraphQl/Query/QueryComplexityLimiter.php | 53 +++++++++++++++++++ .../GraphQl/Query/QueryProcessor.php | 23 ++++---- 3 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index 105fa8bf166b2..4aed25ac7bf4a 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -97,4 +97,10 @@ + + + 50 + 150 + + diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php new file mode 100644 index 0000000000000..be768d9f8defd --- /dev/null +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php @@ -0,0 +1,53 @@ +queryDepth = $queryDepth; + $this->queryComplexity = $queryComplexity; + } + + public function execute(bool $disableIntrospection = false): void + { + DocumentValidator::addRule(new QueryDepth($this->queryDepth)); + DocumentValidator::addRule(new QueryComplexity($this->queryComplexity)); + + if ($disableIntrospection) { + DocumentValidator::addRule(new DisableIntrospection()); + } + } +} diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php index 94a0e5a1c1a6e..0a23e4d00e8c7 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php @@ -7,9 +7,6 @@ namespace Magento\Framework\GraphQl\Query; -use GraphQL\Validator\DocumentValidator; -use GraphQL\Validator\Rules\DisableIntrospection; -use GraphQL\Validator\Rules\QueryDepth; use Magento\Framework\GraphQl\Exception\ExceptionFormatter; use Magento\Framework\GraphQl\Schema; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; @@ -24,12 +21,21 @@ class QueryProcessor */ private $exceptionFormatter; + /** + * @var QueryComplexityLimiter + */ + protected $queryComplexityLimiter; + /** * @param ExceptionFormatter $exceptionFormatter + * @param QueryComplexityLimiter $queryComplexityChecker */ - public function __construct(ExceptionFormatter $exceptionFormatter) - { + public function __construct( + ExceptionFormatter $exceptionFormatter, + QueryComplexityLimiter $queryComplexityChecker + ) { $this->exceptionFormatter = $exceptionFormatter; + $this->queryComplexityLimiter = $queryComplexityChecker; } /** @@ -49,10 +55,9 @@ public function process( array $variableValues = null, string $operationName = null ) : array { - if (!$this->exceptionFormatter->shouldShowDetail()) { - DocumentValidator::addRule(new QueryDepth(10)); - DocumentValidator::addRule(new DisableIntrospection()); - } + $disableIntrospection = $this->exceptionFormatter->shouldShowDetail(); + $this->queryComplexityLimiter->execute($disableIntrospection); + $rootValue = null; return \GraphQL\GraphQL::executeQuery( $schema, From 660e393a1087060a838dc299ea80606ddb08df50 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Thu, 26 Jul 2018 11:11:01 +0200 Subject: [PATCH 003/310] Added some missing PHPDoc --- .../Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php index be768d9f8defd..993390f5cfd22 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php @@ -41,6 +41,9 @@ public function __construct( $this->queryComplexity = $queryComplexity; } + /** + * @param bool $disableIntrospection + */ public function execute(bool $disableIntrospection = false): void { DocumentValidator::addRule(new QueryDepth($this->queryDepth)); From 351ae3784d0a148c40c89e581f9b6f0f61675a9a Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Thu, 26 Jul 2018 12:04:33 +0200 Subject: [PATCH 004/310] Added API-functional test for query limiter --- .../Framework/QueryComplexityLimiterTest.php | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php new file mode 100644 index 0000000000000..fe278eaef1914 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php @@ -0,0 +1,332 @@ +graphQlQuery($query); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testQueryDepthIsLimited() + { + $query + = <<graphQlQuery($query); + } +} From 9aedd10c130d3455c352d3a3bddad856a75dd82d Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Thu, 26 Jul 2018 12:06:12 +0200 Subject: [PATCH 005/310] Constructor argument renamed --- .../Magento/Framework/GraphQl/Query/QueryProcessor.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php index 0a23e4d00e8c7..e1c9c6d6dbdfe 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php @@ -28,14 +28,14 @@ class QueryProcessor /** * @param ExceptionFormatter $exceptionFormatter - * @param QueryComplexityLimiter $queryComplexityChecker + * @param QueryComplexityLimiter $queryComplexityLimiter */ public function __construct( ExceptionFormatter $exceptionFormatter, - QueryComplexityLimiter $queryComplexityChecker + QueryComplexityLimiter $queryComplexityLimiter ) { $this->exceptionFormatter = $exceptionFormatter; - $this->queryComplexityLimiter = $queryComplexityChecker; + $this->queryComplexityLimiter = $queryComplexityLimiter; } /** From 88fb39883b20bad5500e8a7e1fd492ed33bfecf9 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 30 Jul 2018 14:34:55 +0200 Subject: [PATCH 006/310] Fixed introspection disabling expression --- lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php index e1c9c6d6dbdfe..e3a2ea3fa753e 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php @@ -55,7 +55,7 @@ public function process( array $variableValues = null, string $operationName = null ) : array { - $disableIntrospection = $this->exceptionFormatter->shouldShowDetail(); + $disableIntrospection = !$this->exceptionFormatter->shouldShowDetail(); $this->queryComplexityLimiter->execute($disableIntrospection); $rootValue = null; From 99b1d1d5397cc3fb97551699647a33717a6ed3f6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 30 Jul 2018 14:53:14 +0200 Subject: [PATCH 007/310] Reduced limiting values --- app/code/Magento/GraphQl/etc/di.xml | 4 +- .../Framework/QueryComplexityLimiterTest.php | 173 +----------------- .../GraphQl/Query/QueryComplexityLimiter.php | 4 +- 3 files changed, 7 insertions(+), 174 deletions(-) diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index 4aed25ac7bf4a..529280b7cbb6e 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -99,8 +99,8 @@ - 50 - 150 + 10 + 50 diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php index fe278eaef1914..c5f645a94472a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php @@ -86,109 +86,6 @@ public function testQueryComplexityIsLimited() id types } - categories { - id - position - level - url_key - url_path - product_count - breadcrumbs { - category_id - category_name - category_url_key - } - products { - items { - name - special_from_date - special_to_date - new_to_date - new_from_date - tier_price - manufacturer - thumbnail - sku - image - canonical_url - updated_at - created_at - categories { - id - position - level - url_key - url_path - product_count - breadcrumbs { - category_id - category_name - category_url_key - } - products { - items { - name - special_from_date - special_to_date - new_to_date - new_from_date - tier_price - manufacturer - sku - image - canonical_url - updated_at - created_at - categories { - id - position - level - url_key - url_path - product_count - breadcrumbs { - category_id - category_name - category_url_key - } - products { - items { - name - special_from_date - special_to_date - new_to_date - new_from_date - tier_price - manufacturer - sku - image - thumbnail - canonical_url - updated_at - created_at - categories { - id - position - level - url_key - url_path - product_count - default_sort_by - breadcrumbs { - category_id - category_name - category_url_key - } - } - } - } - } - } - } - } - } - } - } } } } @@ -201,7 +98,7 @@ public function testQueryComplexityIsLimited() } QUERY; - self::expectExceptionMessageRegExp('/Max query complexity should be 150 but got 151/'); + self::expectExceptionMessageRegExp('/Max query complexity should be 50 but got 62/'); $this->graphQlQuery($query); } @@ -238,71 +135,7 @@ public function testQueryDepthIsLimited() categories { products { items { - categories { - products { - items { - categories { - products { - items { - categories { - products { - items { - name, - categories { - products { - items { - categories { - products { - items { - categories{ - products { - items { - categories { - products { - items { - categories { - products { - items { - categories { - products { - items { - name, - categories { - products { - items { - categories { - name - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } + name } } } @@ -326,7 +159,7 @@ public function testQueryDepthIsLimited() } } QUERY; - self::expectExceptionMessageRegExp('/Max query depth should be 50 but got 51/'); + self::expectExceptionMessageRegExp('/Max query depth should be 10 but got 20/'); $this->graphQlQuery($query); } } diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php index 993390f5cfd22..d2d9477e67b1e 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php @@ -34,8 +34,8 @@ class QueryComplexityLimiter * @param int $queryComplexity */ public function __construct( - int $queryDepth = 50, - int $queryComplexity = 150 + int $queryDepth = 10, + int $queryComplexity = 50 ) { $this->queryDepth = $queryDepth; $this->queryComplexity = $queryComplexity; From 1ff16642855a313b3b8e4a7a9a6e836c09769c69 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Mon, 3 Sep 2018 18:33:15 +0300 Subject: [PATCH 008/310] MAGETWO-94424: [2.3] Wrong product and shipping prices are applying on admin orders --- app/code/Magento/Store/Model/Store.php | 20 ++-- .../Store/Test/Unit/Model/StoreTest.php | 98 ++++++++++++++++++- 2 files changed, 103 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index af25957257421..32c9a78448428 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -902,23 +902,17 @@ public function setCurrentCurrencyCode($code) */ public function getCurrentCurrencyCode() { + $availableCurrencyCodes = \array_values($this->getAvailableCurrencyCodes(true)); // try to get currently set code among allowed - $code = $this->_httpContext->getValue(Context::CONTEXT_CURRENCY); - $code = $code === null ? $this->_getSession()->getCurrencyCode() : $code; - if (empty($code)) { + $code = $this->_httpContext->getValue(Context::CONTEXT_CURRENCY) ?? $this->_getSession()->getCurrencyCode(); + if (empty($code) || !\in_array($code, $availableCurrencyCodes)) { $code = $this->getDefaultCurrencyCode(); - } - if (in_array($code, $this->getAvailableCurrencyCodes(true))) { - return $code; + if (!\in_array($code, $availableCurrencyCodes) && !empty($availableCurrencyCodes)) { + $code = $availableCurrencyCodes[0]; + } } - // take first one of allowed codes - $codes = array_values($this->getAvailableCurrencyCodes(true)); - if (empty($codes)) { - // return default code, if no codes specified at all - return $this->getDefaultCurrencyCode(); - } - return array_shift($codes); + return $code; } /** diff --git a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php index f98cf5d892e07..f4a5010e51b88 100644 --- a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php @@ -3,11 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Store\Test\Unit\Model; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; @@ -38,6 +38,16 @@ class StoreTest extends \PHPUnit\Framework\TestCase */ protected $filesystemMock; + /** + * @var ReinitableConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + /** + * @var SessionManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $sessionMock; + /** * @var \Magento\Framework\Url\ModifierInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -61,12 +71,22 @@ protected function setUp() 'isSecure', 'getServer', ]); + $this->filesystemMock = $this->getMockBuilder(\Magento\Framework\Filesystem::class) ->disableOriginalConstructor() ->getMock(); + $this->configMock = $this->getMockBuilder(ReinitableConfigInterface::class) + ->getMock(); + $this->sessionMock = $this->getMockBuilder(SessionManagerInterface::class) + ->setMethods(['getCurrencyCode']) + ->getMockForAbstractClass(); $this->store = $this->objectManagerHelper->getObject( \Magento\Store\Model\Store::class, - ['filesystem' => $this->filesystemMock] + [ + 'filesystem' => $this->filesystemMock, + 'config' => $this->configMock, + 'session' => $this->sessionMock, + ] ); $this->urlModifierMock = $this->createMock(\Magento\Framework\Url\ModifierInterface::class); @@ -694,6 +714,80 @@ public function testGetScopeTypeName() $this->assertEquals('Store View', $this->store->getScopeTypeName()); } + /** + * @param array $availableCodes + * @param string $currencyCode + * @param string $defaultCode + * @param string $expectedCode + * @return void + * @dataProvider currencyCodeDataProvider + */ + public function testGetCurrentCurrencyCode( + array $availableCodes, + string $currencyCode, + string $defaultCode, + string $expectedCode + ): void { + $this->store->setData('available_currency_codes', $availableCodes); + $this->sessionMock->method('getCurrencyCode') + ->willReturn($currencyCode); + $this->configMock->method('getValue') + ->with(\Magento\Directory\Model\Currency::XML_PATH_CURRENCY_DEFAULT) + ->willReturn($defaultCode); + + $code = $this->store->getCurrentCurrencyCode(); + $this->assertEquals($expectedCode, $code); + } + + /** + * @return array + */ + public function currencyCodeDataProvider(): array + { + return [ + [ + [ + 'USD', + ], + 'USD', + 'USD', + 'USD', + ], + [ + [ + 'USD', + 'EUR', + ], + 'EUR', + 'USD', + 'EUR', + ], + [ + [ + 'EUR', + 'USD', + ], + 'GBP', + 'USD', + 'USD', + ], + [ + [ + 'USD', + ], + 'GBP', + 'EUR', + 'USD', + ], + [ + [], + 'GBP', + 'EUR', + 'EUR', + ], + ]; + } + /** * @param \Magento\Store\Model\Store $model */ From 2a7cd23d460e8ff278730cafbf3a2a1f33ae2461 Mon Sep 17 00:00:00 2001 From: Oleksandr_Hodzevych Date: Mon, 3 Sep 2018 18:47:32 +0300 Subject: [PATCH 009/310] MAGETWO-91609: Problems with operator more/less in the "catalog Products List" widget - Fixed widget operator less or more --- .../Rule/Model/Condition/Sql/Builder.php | 4 + .../Unit/Model/Condition/Sql/BuilderTest.php | 119 ++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index 2894de0f19b87..1afe995f837ea 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -32,9 +32,13 @@ class Builder '==' => ':field = ?', '!=' => ':field <> ?', '>=' => ':field >= ?', + '>e;' => ':field >= ?', '>' => ':field > ?', + '>' => ':field > ?', '<=' => ':field <= ?', + '<e;' => ':field <= ?', '<' => ':field < ?', + '<' => ':field < ?', '{}' => ':field IN (?)', '!{}' => ':field NOT IN (?)', '()' => ':field IN (?)', diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php index daf7b1462c722..b3ddaa51c2605 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php @@ -78,4 +78,123 @@ public function testAttachConditionToCollection() $this->_builder->attachConditionToCollection($collection, $combine); } + + /** + * Test for attach condition to collection with operator in html format + * + * @covers \Magento\Rule\Model\Condition\Sql\Builder::attachConditionToCollection() + * @return void; + */ + public function testAttachConditionAsHtmlToCollection() + { + $abstractCondition = $this->getMockForAbstractClass( + \Magento\Rule\Model\Condition\AbstractCondition::class, + [], + '', + false, + false, + true, + ['getOperatorForValidate', 'getMappedSqlField', 'getAttribute', 'getBindArgumentValue'] + ); + + $abstractCondition->expects($this->once()) + ->method('getMappedSqlField') + ->will($this->returnValue('argument')); + + $abstractCondition->expects($this->once()) + ->method('getOperatorForValidate') + ->will($this->returnValue('>')); + + $abstractCondition->expects($this->at(1)) + ->method('getAttribute') + ->will($this->returnValue('attribute')); + + $abstractCondition->expects($this->at(2)) + ->method('getAttribute') + ->will($this->returnValue('attribute')); + + $abstractCondition->expects($this->once()) + ->method('getBindArgumentValue') + ->will($this->returnValue(10)); + + $conditions = [ + $abstractCondition + ]; + + $collection = $this->createPartialMock( + \Magento\Eav\Model\Entity\Collection\AbstractCollection::class, + [ + 'getResource', + 'getSelect', + 'getStoreId', + 'getDefaultStoreId', + ] + ); + + $combine = $this->createPartialMock(\Magento\Rule\Model\Condition\Combine::class, + [ + 'getConditions', + 'getValue', + 'getAggregator' + ] + ); + $resource = $this->createPartialMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class, ['getConnection']); + $select = $this->createPartialMock(\Magento\Framework\DB\Select::class, ['where']); + $select->expects($this->never()) + ->method('where'); + + $connection = $this->getMockForAbstractClass( + \Magento\Framework\DB\Adapter\AdapterInterface::class, + ['quoteInto'], + '', + false + ); + + $connection->expects($this->once()) + ->method('quoteInto') + ->with(' > ?', 10) + ->will($this->returnValue(' > 10')); + + $collection->expects($this->once()) + ->method('getResource') + ->will($this->returnValue($resource)); + + $collection->expects($this->once()) + ->method('getStoreId') + ->willReturn(1); + + $collection->expects($this->once()) + ->method('getDefaultStoreId') + ->willReturn(1); + + $resource->expects($this->once()) + ->method('getConnection') + ->will($this->returnValue($connection)); + + $combine->expects($this->once()) + ->method('getValue') + ->willReturn('attribute'); + + $combine->expects($this->once()) + ->method('getAggregator') + ->willReturn(' AND '); + + $combine->expects($this->at(0)) + ->method('getConditions') + ->will($this->returnValue($conditions)); + + $combine->expects($this->at(1)) + ->method('getConditions') + ->will($this->returnValue($conditions)); + + $combine->expects($this->at(2)) + ->method('getConditions') + ->will($this->returnValue($conditions)); + + $combine->expects($this->at(3)) + ->method('getConditions') + ->will($this->returnValue($conditions)); + + $this->_builder->attachConditionToCollection($collection, $combine); + } } From 5725c92582a43d15dc58b415e90cb2a1e31ea648 Mon Sep 17 00:00:00 2001 From: vprohorov Date: Wed, 5 Sep 2018 05:00:39 +0300 Subject: [PATCH 010/310] MAGETWO-62728: My Wishlist - quantity input box issue - Adding maxlength to input validation --- .../Wishlist/view/frontend/templates/item/column/cart.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml index 9ea0d1a823235..e124edc6a43e1 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml @@ -21,7 +21,7 @@ $product = $item->getProduct();
-
From 335c173deba047e558f44f69f5538f1a5be2b54a Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Wed, 5 Sep 2018 15:13:10 +0200 Subject: [PATCH 011/310] Adjusted complexity limiter for developer mode --- .../Framework/GraphQl/Query/QueryComplexityLimiter.php | 10 ++++++---- .../Magento/Framework/GraphQl/Query/QueryProcessor.php | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php index d2d9477e67b1e..a144ef8967f17 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php @@ -16,6 +16,8 @@ * Sets limits for query complexity. A single GraphQL query can potentially * generate thousands of database operations so, the very complex queries * should be filtered and rejected. + * + * https://github.com/webonyx/graphql-php/blob/master/docs/security.md#query-complexity-analysis */ class QueryComplexityLimiter { @@ -42,15 +44,15 @@ public function __construct( } /** - * @param bool $disableIntrospection + * @param bool $developerMode */ - public function execute(bool $disableIntrospection = false): void + public function execute(bool $developerMode = false): void { - DocumentValidator::addRule(new QueryDepth($this->queryDepth)); DocumentValidator::addRule(new QueryComplexity($this->queryComplexity)); - if ($disableIntrospection) { + if (!$developerMode) { DocumentValidator::addRule(new DisableIntrospection()); + DocumentValidator::addRule(new QueryDepth($this->queryDepth)); } } } diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php index e3a2ea3fa753e..57ff56b665a1e 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php @@ -24,7 +24,7 @@ class QueryProcessor /** * @var QueryComplexityLimiter */ - protected $queryComplexityLimiter; + private $queryComplexityLimiter; /** * @param ExceptionFormatter $exceptionFormatter @@ -55,8 +55,8 @@ public function process( array $variableValues = null, string $operationName = null ) : array { - $disableIntrospection = !$this->exceptionFormatter->shouldShowDetail(); - $this->queryComplexityLimiter->execute($disableIntrospection); + $developerMode = !$this->exceptionFormatter->shouldShowDetail(); + $this->queryComplexityLimiter->execute($developerMode); $rootValue = null; return \GraphQL\GraphQL::executeQuery( From 9d244de1504677845aa7ccbf4a61218f851b790b Mon Sep 17 00:00:00 2001 From: Yuliya Labudova Date: Thu, 6 Sep 2018 10:39:17 +0300 Subject: [PATCH 012/310] MAGETWO-91650: Translation not working for product alerts - Translate email depending on the store on which the notification button was clicked --- app/code/Magento/ProductAlert/Model/Email.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/ProductAlert/Model/Email.php b/app/code/Magento/ProductAlert/Model/Email.php index 7bc4aba351546..d425a0c6b052c 100644 --- a/app/code/Magento/ProductAlert/Model/Email.php +++ b/app/code/Magento/ProductAlert/Model/Email.php @@ -307,11 +307,7 @@ public function send() return false; } - if ($this->_customer->getStoreId() > 0) { - $store = $this->_storeManager->getStore($this->_customer->getStoreId()); - } else { - $store = $this->_website->getDefaultStore(); - } + $store = $this->_website->getDefaultStore(); $storeId = $store->getId(); if ($this->_type == 'price' && !$this->_scopeConfig->getValue( From cd649d162681f3698cb0f989cb624f79ae9f4dfc Mon Sep 17 00:00:00 2001 From: Oleksandr_Hodzevych Date: Fri, 7 Sep 2018 15:10:50 +0300 Subject: [PATCH 013/310] MAGETWO-91609: Problems with operator more/less in the "catalog Products List" widget - Fixed widget operator less or more --- app/code/Magento/Rule/Model/Condition/Sql/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index 1afe995f837ea..01c671f0b0678 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -32,11 +32,11 @@ class Builder '==' => ':field = ?', '!=' => ':field <> ?', '>=' => ':field >= ?', - '>e;' => ':field >= ?', + '>=' => ':field >= ?', '>' => ':field > ?', '>' => ':field > ?', '<=' => ':field <= ?', - '<e;' => ':field <= ?', + '<=' => ':field <= ?', '<' => ':field < ?', '<' => ':field < ?', '{}' => ':field IN (?)', From 502e415c948db0eaa72ddd1b3dbe57879e0d97f6 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich Date: Fri, 7 Sep 2018 17:37:49 +0300 Subject: [PATCH 014/310] MAGETWO-91649: #13513: Magento ignore store-level url_key of child category in URL rewrite process for global scope - Fixed issue with incorrect url path generation for children categories; --- .../CategoryUrlPathAutogeneratorObserver.php | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php index eb54f0427c11a..5c7a8b16a666e 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php @@ -8,6 +8,7 @@ use Magento\Catalog\Model\Category; use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; use Magento\CatalogUrlRewrite\Service\V1\StoreViewService; +use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Framework\Event\Observer; use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; use Magento\Framework\Event\ObserverInterface; @@ -30,19 +31,27 @@ class CategoryUrlPathAutogeneratorObserver implements ObserverInterface */ protected $storeViewService; + /** + * @var CategoryRepositoryInterface + */ + private $categoryRepository; + /** * @param CategoryUrlPathGenerator $categoryUrlPathGenerator * @param ChildrenCategoriesProvider $childrenCategoriesProvider * @param \Magento\CatalogUrlRewrite\Service\V1\StoreViewService $storeViewService + * @param CategoryRepositoryInterface $categoryRepository */ public function __construct( CategoryUrlPathGenerator $categoryUrlPathGenerator, ChildrenCategoriesProvider $childrenCategoriesProvider, - StoreViewService $storeViewService + StoreViewService $storeViewService, + CategoryRepositoryInterface $categoryRepository ) { $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; $this->childrenCategoriesProvider = $childrenCategoriesProvider; $this->storeViewService = $storeViewService; + $this->categoryRepository = $categoryRepository; } /** @@ -77,22 +86,22 @@ public function execute(\Magento\Framework\Event\Observer $observer) */ protected function updateUrlPathForChildren(Category $category) { - $children = $this->childrenCategoriesProvider->getChildren($category, true); - if ($this->isGlobalScope($category->getStoreId())) { - foreach ($children as $child) { + $childrenIds = $this->childrenCategoriesProvider->getChildrenIds($category, true); + foreach ($childrenIds as $childId) { foreach ($category->getStoreIds() as $storeId) { if ($this->storeViewService->doesEntityHaveOverriddenUrlPathForStore( $storeId, - $child->getId(), + $childId, Category::ENTITY )) { - $child->setStoreId($storeId); + $child = $this->categoryRepository->get($childId, $storeId); $this->updateUrlPathForCategory($child); } } } } else { + $children = $this->childrenCategoriesProvider->getChildren($category, true); foreach ($children as $child) { $child->setStoreId($category->getStoreId()); $this->updateUrlPathForCategory($child); From 5746734cc6345d6b265b3efc8d9c839778eb48e9 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich Date: Tue, 11 Sep 2018 13:53:04 +0300 Subject: [PATCH 015/310] MAGETWO-91658: Wrong Checkout Totals Sort Order in cart - Fixed issue with incorrect totals sorting on cart page; --- .../view/frontend/layout/checkout_cart_index.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/SalesRule/view/frontend/layout/checkout_cart_index.xml b/app/code/Magento/SalesRule/view/frontend/layout/checkout_cart_index.xml index a5d0be503baad..9e86671500c50 100644 --- a/app/code/Magento/SalesRule/view/frontend/layout/checkout_cart_index.xml +++ b/app/code/Magento/SalesRule/view/frontend/layout/checkout_cart_index.xml @@ -13,14 +13,10 @@ - - - - Magento_SalesRule/js/view/cart/totals/discount - - Discount - - + + Magento_SalesRule/js/view/cart/totals/discount + + Discount From b1227fc73a76c8dfe7c0af3241b4eeb19e7eb18b Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 17 Sep 2018 15:18:36 +0200 Subject: [PATCH 016/310] Changed query depth limiter behavior --- app/code/Magento/GraphQl/etc/di.xml | 2 +- .../Framework/QueryComplexityLimiterTest.php | 6 +++++- .../GraphQl/Query/QueryComplexityLimiter.php | 14 ++++---------- .../Framework/GraphQl/Query/QueryProcessor.php | 6 ++++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index 529280b7cbb6e..b1d24ce6c4a55 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -99,7 +99,7 @@ - 10 + 15 50 diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php index c5f645a94472a..4616c450786ed 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php @@ -9,6 +9,10 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; +/** + * Tests query complexity limiter and depth limiter. + * Actual for production mode only + */ class QueryComplexityLimiterTest extends GraphQlAbstract { /** @@ -159,7 +163,7 @@ public function testQueryDepthIsLimited() } } QUERY; - self::expectExceptionMessageRegExp('/Max query depth should be 10 but got 20/'); + self::expectExceptionMessageRegExp('/Max query depth should be 15 but got 20/'); $this->graphQlQuery($query); } } diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php index a144ef8967f17..15e0edb2400ec 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php @@ -36,23 +36,17 @@ class QueryComplexityLimiter * @param int $queryComplexity */ public function __construct( - int $queryDepth = 10, + int $queryDepth = 15, int $queryComplexity = 50 ) { $this->queryDepth = $queryDepth; $this->queryComplexity = $queryComplexity; } - /** - * @param bool $developerMode - */ - public function execute(bool $developerMode = false): void + public function execute(): void { DocumentValidator::addRule(new QueryComplexity($this->queryComplexity)); - - if (!$developerMode) { - DocumentValidator::addRule(new DisableIntrospection()); - DocumentValidator::addRule(new QueryDepth($this->queryDepth)); - } + DocumentValidator::addRule(new DisableIntrospection()); + DocumentValidator::addRule(new QueryDepth($this->queryDepth)); } } diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php index 57ff56b665a1e..98e6c840ba682 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php @@ -55,8 +55,10 @@ public function process( array $variableValues = null, string $operationName = null ) : array { - $developerMode = !$this->exceptionFormatter->shouldShowDetail(); - $this->queryComplexityLimiter->execute($developerMode); + var_dump($this->exceptionFormatter->shouldShowDetail()); + if (!$this->exceptionFormatter->shouldShowDetail()) { + $this->queryComplexityLimiter->execute(); + } $rootValue = null; return \GraphQL\GraphQL::executeQuery( From b91f5c81dc994d070aca3e7bb453f08cdc8eb756 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda Date: Mon, 17 Sep 2018 20:29:49 +0300 Subject: [PATCH 017/310] GraphQL-133: Complexity limiter prototype --- .../Framework/GraphQl/Query/QueryComplexityLimiter.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php index 15e0edb2400ec..b302f4272527a 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php @@ -13,6 +13,8 @@ use GraphQL\Validator\Rules\QueryComplexity; /** + * QueryComplexityLimiter + * * Sets limits for query complexity. A single GraphQL query can potentially * generate thousands of database operations so, the very complex queries * should be filtered and rejected. @@ -43,6 +45,11 @@ public function __construct( $this->queryComplexity = $queryComplexity; } + /** + * Sets limits for query complexity + * + * @return void + */ public function execute(): void { DocumentValidator::addRule(new QueryComplexity($this->queryComplexity)); From 341d68cc42e6a06cfdccf2d1fcb4475f69bb9318 Mon Sep 17 00:00:00 2001 From: RomanKis Date: Tue, 5 Dec 2017 18:01:25 +0200 Subject: [PATCH 018/310] MAGETWO-94308: User can place order with amount less than "minimum order amount" via Checkout with multiple address flow. - Upport fix from 2.2-develop --- .../Model/Checkout/Type/Multishipping.php | 53 +++++++++++++++++-- .../Model/Checkout/Type/MultishippingTest.php | 42 ++++++++++++++- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index 9412b13895599..8d740b4ea6b92 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -897,13 +897,21 @@ public function reset() */ public function validateMinimumAmount() { - return !($this->_scopeConfig->isSetFlag( + $minimumOrderActive = $this->_scopeConfig->isSetFlag( 'sales/minimum_order/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) && $this->_scopeConfig->isSetFlag( + ); + + if ($this->_scopeConfig->isSetFlag( 'sales/minimum_order/multi_address', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) && !$this->getQuote()->validateMinimumAmount()); + \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ) { + $result = !($minimumOrderActive && !$this->getQuote()->validateMinimumAmount()); + } else { + $result = !($minimumOrderActive && !$this->validateMinimumAmountForAddressItems()); + } + + return $result; } /** @@ -1115,6 +1123,43 @@ private function getShippingAssignmentProcessor() return $this->shippingAssignmentProcessor; } + /** + * Validate minimum amount for "Checkout with Multiple Addresses" when + * "Validate Each Address Separately in Multi-address Checkout" is No. + * + * @return bool + */ + private function validateMinimumAmountForAddressItems() + { + $result = true; + $storeId = $this->getQuote()->getStoreId(); + + $minAmount = $this->_scopeConfig->getValue( + 'sales/minimum_order/amount', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeId + ); + $taxInclude = $this->_scopeConfig->getValue( + 'sales/minimum_order/tax_including', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeId + ); + + $addresses = $this->getQuote()->getAllAddresses(); + + $baseTotal = 0; + foreach ($addresses as $address) { + $taxes = $taxInclude ? $address->getBaseTaxAmount() : 0; + $baseTotal += $address->getBaseSubtotalWithDiscount() + $taxes; + } + + if ($baseTotal < $minAmount) { + $result = false; + } + + return $result; + } + /** * Remove successfully placed items from quote. * 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 94c11bef1d311..da206cf266219 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 @@ -167,13 +167,18 @@ class MultishippingTest extends \PHPUnit\Framework\TestCase */ private $sessionMock; + /** + * @var PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + protected function setUp() { $this->checkoutSessionMock = $this->createSimpleMock(Session::class); $this->customerSessionMock = $this->createSimpleMock(CustomerSession::class); $this->orderFactoryMock = $this->createSimpleMock(OrderFactory::class); $eventManagerMock = $this->createSimpleMock(ManagerInterface::class); - $scopeConfigMock = $this->createSimpleMock(ScopeConfigInterface::class); + $this->scopeConfigMock = $this->createSimpleMock(ScopeConfigInterface::class); $this->sessionMock = $this->createSimpleMock(Generic::class); $addressFactoryMock = $this->createSimpleMock(AddressFactory::class); $this->toOrderMock = $this->createSimpleMock(ToOrder::class); @@ -224,7 +229,7 @@ protected function setUp() $this->orderFactoryMock, $this->addressRepositoryMock, $eventManagerMock, - $scopeConfigMock, + $this->scopeConfigMock, $this->sessionMock, $addressFactoryMock, $this->toOrderMock, @@ -807,4 +812,37 @@ private function createSimpleMock($className) ->disableOriginalConstructor() ->getMock(); } + + public function testValidateMinimumAmountMultiAddressTrue() + { + $this->scopeConfigMock->expects($this->exactly(2))->method('isSetFlag')->withConsecutive( + ['sales/minimum_order/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], + ['sales/minimum_order/multi_address', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] + )->willReturnOnConsecutiveCalls(true, true); + + $this->checkoutSessionMock->expects($this->atLeastOnce())->method('getQuote')->willReturn($this->quoteMock); + $this->quoteMock->expects($this->once())->method('validateMinimumAmount')->willReturn(false); + $this->assertFalse($this->model->validateMinimumAmount()); + } + + public function testValidateMinimumAmountMultiAddressFalse() + { + $addressMock = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + $this->scopeConfigMock->expects($this->exactly(2))->method('isSetFlag')->withConsecutive( + ['sales/minimum_order/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], + ['sales/minimum_order/multi_address', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] + )->willReturnOnConsecutiveCalls(true, false); + + $this->scopeConfigMock->expects($this->exactly(2))->method('getValue')->withConsecutive( + ['sales/minimum_order/amount', \Magento\Store\Model\ScopeInterface::SCOPE_STORE], + ['sales/minimum_order/tax_including', \Magento\Store\Model\ScopeInterface::SCOPE_STORE] + )->willReturnOnConsecutiveCalls(100, false); + + $this->checkoutSessionMock->expects($this->atLeastOnce())->method('getQuote')->willReturn($this->quoteMock); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn(1); + $this->quoteMock->expects($this->once())->method('getAllAddresses')->willReturn([$addressMock]); + $addressMock->expects($this->once())->method('getBaseSubtotalWithDiscount')->willReturn(101); + + $this->assertTrue($this->model->validateMinimumAmount()); + } } From e323c7b3661191c9eeb52776ab1b4c0c6fb5d8ad Mon Sep 17 00:00:00 2001 From: Tommy Wiebell Date: Mon, 17 Sep 2018 14:50:10 -0500 Subject: [PATCH 019/310] MAGETWO-94308: User can place order with amount less than "minimum order amount" via Checkout with multiple address flow. - Fix issue with upport that doesn't trigger totals to re-calculate before validation - Remove redundant form markup from addresses template - Update unit test to account for changes --- .../Model/Checkout/Type/Multishipping.php | 3 ++- .../Unit/Model/Checkout/Type/MultishippingTest.php | 8 ++++++++ .../view/frontend/templates/checkout/addresses.phtml | 11 ----------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index 8d740b4ea6b92..f55af40388fdf 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -874,7 +874,7 @@ private function logExceptions(array $exceptionList) */ public function save() { - $this->getQuote()->collectTotals(); + $this->getQuote()->setTotalsCollectedFlag(false)->collectTotals(); $this->quoteRepository->save($this->getQuote()); return $this; } @@ -1145,6 +1145,7 @@ private function validateMinimumAmountForAddressItems() $storeId ); + $this->getQuote()->collectTotals(); $addresses = $this->getQuote()->getAllAddresses(); $baseTotal = 0; 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 da206cf266219..67ff233f717eb 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 @@ -282,6 +282,10 @@ public function testSetShippingItemsInformation() ->willReturn(null); $this->quoteMock->expects($this->atLeastOnce())->method('getAllItems')->willReturn([]); + $this->quoteMock->expects($this->once()) + ->method('__call') + ->with('setTotalsCollectedFlag', [false]) + ->willReturnSelf(); $this->filterBuilderMock->expects($this->atLeastOnce())->method('setField')->willReturnSelf(); $this->filterBuilderMock->expects($this->atLeastOnce())->method('setValue')->willReturnSelf(); @@ -421,6 +425,10 @@ public function testSetShippingMethods() $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]); + $this->quoteMock->expects($this->once()) + ->method('__call') + ->with('setTotalsCollectedFlag', [false]) + ->willReturnSelf(); $this->mockShippingAssignment(); diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml index 0f96f7cdb708a..a29013cc71722 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml @@ -14,17 +14,6 @@ * @var $block \Magento\Multishipping\Block\Checkout\Addresses */ ?> -
getData('save_rewrites_history')); - /** @var CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator */ - $categoryUrlRewriteGenerator = $this->categoryUrlRewriteGeneratorFactory->create(); - $mergeDataProvider->merge( - $categoryUrlRewriteGenerator->generate($childCategory, false, $rootCategoryId) - ); + if ($childrenIds = $this->childrenCategoriesProvider->getChildrenIds($category, true)) { + foreach ($childrenIds as $childId) { + /** @var Category $childCategory */ + $childCategory = $this->categoryRepository->get($childId, $storeId); + $childCategory->setData('save_rewrites_history', $category->getData('save_rewrites_history')); + /** @var CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator */ + $categoryUrlRewriteGenerator = $this->categoryUrlRewriteGeneratorFactory->create(); + $mergeDataProvider->merge( + $categoryUrlRewriteGenerator->generate($childCategory, false, $rootCategoryId) + ); + } } return $mergeDataProvider->getData(); diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/ChildrenUrlRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/ChildrenUrlRewriteGeneratorTest.php index 3f641256b1259..f8422c7c05fa6 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/ChildrenUrlRewriteGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/ChildrenUrlRewriteGeneratorTest.php @@ -31,6 +31,9 @@ class ChildrenUrlRewriteGeneratorTest extends \PHPUnit\Framework\TestCase /** @var \PHPUnit_Framework_MockObject_MockObject */ private $serializerMock; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $categoryRepository; + protected function setUp() { $this->serializerMock = $this->getMockBuilder(Json::class) @@ -47,6 +50,9 @@ protected function setUp() $this->categoryUrlRewriteGenerator = $this->getMockBuilder( \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator::class )->disableOriginalConstructor()->getMock(); + $this->categoryRepository = $this->getMockBuilder( + \Magento\Catalog\Model\CategoryRepository::class + )->disableOriginalConstructor()->getMock(); $mergeDataProviderFactory = $this->createPartialMock( \Magento\UrlRewrite\Model\MergeDataProviderFactory::class, ['create'] @@ -59,14 +65,15 @@ protected function setUp() [ 'childrenCategoriesProvider' => $this->childrenCategoriesProvider, 'categoryUrlRewriteGeneratorFactory' => $this->categoryUrlRewriteGeneratorFactory, - 'mergeDataProviderFactory' => $mergeDataProviderFactory + 'mergeDataProviderFactory' => $mergeDataProviderFactory, + 'categoryRepository' => $this->categoryRepository ] ); } public function testNoChildrenCategories() { - $this->childrenCategoriesProvider->expects($this->once())->method('getChildren')->with($this->category, true) + $this->childrenCategoriesProvider->expects($this->once())->method('getChildrenIds')->with($this->category, true) ->will($this->returnValue([])); $this->assertEquals([], $this->childrenUrlRewriteGenerator->generate('store_id', $this->category)); @@ -76,14 +83,16 @@ public function testGenerate() { $storeId = 'store_id'; $saveRewritesHistory = 'flag'; + $childId = 2; $childCategory = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) ->disableOriginalConstructor()->getMock(); - $childCategory->expects($this->once())->method('setStoreId')->with($storeId); $childCategory->expects($this->once())->method('setData') ->with('save_rewrites_history', $saveRewritesHistory); - $this->childrenCategoriesProvider->expects($this->once())->method('getChildren')->with($this->category, true) - ->will($this->returnValue([$childCategory])); + $this->childrenCategoriesProvider->expects($this->once())->method('getChildrenIds')->with($this->category, true) + ->will($this->returnValue([$childId])); + $this->categoryRepository->expects($this->once())->method('get') + ->with($childId, $storeId)->willReturn($childCategory); $this->category->expects($this->any())->method('getData')->with('save_rewrites_history') ->will($this->returnValue($saveRewritesHistory)); $this->categoryUrlRewriteGeneratorFactory->expects($this->once())->method('create') From 1992a368332102563331372d2267f633e3752940 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Tue, 18 Sep 2018 17:32:06 +0200 Subject: [PATCH 022/310] Travis api-functional testsuite check --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index cc730ca5a2cd4..e909bb1980752 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ env: - TEST_SUITE=integration INTEGRATION_INDEX=2 - TEST_SUITE=integration INTEGRATION_INDEX=3 - TEST_SUITE=functional + - TEST_SUITE=api-functional matrix: exclude: - php: 7.1 From 96092708fc5156ec967b3aa5d1704b86fcd65611 Mon Sep 17 00:00:00 2001 From: Tommy Wiebell Date: Tue, 18 Sep 2018 15:41:25 -0500 Subject: [PATCH 023/310] MAGETWO-94308: User can place order with amount less than "minimum order amount" via Checkout with multiple address flow. - Extract config value to variable to resolve code style error --- .../Multishipping/Model/Checkout/Type/Multishipping.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index a30acdc3943ee..d1df064f57140 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -904,10 +904,12 @@ public function validateMinimumAmount() \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); - if ($this->_scopeConfig->isSetFlag( + $minimumOrderMultiFlag = $this->_scopeConfig->isSetFlag( 'sales/minimum_order/multi_address', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE) - ) { + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + if ($minimumOrderMultiFlag) { $result = !($minimumOrderActive && !$this->getQuote()->validateMinimumAmount()); } else { $result = !($minimumOrderActive && !$this->validateMinimumAmountForAddressItems()); From b48171bfa9c5fdb5ce1101dff67b4e3e0e5189ff Mon Sep 17 00:00:00 2001 From: Aleksey Tsoy Date: Wed, 19 Sep 2018 13:48:06 +0600 Subject: [PATCH 024/310] MAGETWO-71022: After return is complete in Admin, 'remaining quantity' in customer account shows incorrect value - Added automated test --- .../Mftf/ActionGroup/AdminInvoiceActionGroup.xml | 12 ++++++++++++ .../Mftf/ActionGroup/AdminShipmentActionGroup.xml | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml index 15aff7c751a11..905cb08a401e1 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml @@ -38,4 +38,16 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml index 85430aeaa4168..3d70a742b13eb 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml @@ -35,4 +35,16 @@ + + + + + + + + + + + + \ No newline at end of file From 78327c1ab246fc20ed84ee54ed67e2d97a1c5131 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich Date: Wed, 19 Sep 2018 14:06:15 +0300 Subject: [PATCH 025/310] MAGETWO-91657: Advanced pricing the bulk discounts by percentage returns error when set - Added validation for discount value field in Advanced Pricing section of product. --- .../Ui/DataProvider/Product/Form/Modifier/TierPrice.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php index 0eddca3322205..5f8d7fcf9049a 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php @@ -45,7 +45,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc * @since 101.1.0 */ public function modifyData(array $data) @@ -54,8 +54,11 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * Add tier price info to meta array. {@inheritdoc} + * * @since 101.1.0 + * @param array $meta + * @return array */ public function modifyMeta(array $meta) { @@ -150,7 +153,9 @@ private function getUpdatedTierPriceStructure(array $priceMeta) 'dataType' => Price::NAME, 'addbefore' => '%', 'validation' => [ + 'required-entry' => true, 'validate-number' => true, + 'validate-greater-than-zero' => true, 'less-than-equals-to' => 100 ], 'visible' => $firstOption From 9302a4f3f22217c6a71002ce009ba3d8ae4897b4 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Wed, 19 Sep 2018 15:45:28 +0200 Subject: [PATCH 026/310] Increased limiter values for passing the tests --- app/code/Magento/GraphQl/etc/di.xml | 4 +- .../Framework/QueryComplexityLimiterTest.php | 133 +++++++++++++++++- .../GraphQl/Query/QueryComplexityLimiter.php | 4 +- 3 files changed, 134 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index b1d24ce6c4a55..622888f697b84 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -99,8 +99,8 @@ - 15 - 50 + 20 + 160 diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php index 4616c450786ed..3304d9e6198f4 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php @@ -90,6 +90,127 @@ public function testQueryComplexityIsLimited() id types } + categories { + id + position + level + url_key + url_path + product_count + breadcrumbs { + category_id + category_name + category_url_key + } + products { + items { + name + special_from_date + special_to_date + new_to_date + new_from_date + tier_price + manufacturer + thumbnail + sku + image + canonical_url + updated_at + created_at + categories { + id + position + level + url_key + url_path + product_count + breadcrumbs { + category_id + category_name + category_url_key + } + products { + items { + name + special_from_date + special_to_date + new_to_date + new_from_date + tier_price + manufacturer + sku + image + canonical_url + updated_at + created_at + categories { + id + position + level + url_key + url_path + product_count + breadcrumbs { + category_id + category_name + category_url_key + } + products { + items { + name + special_from_date + special_to_date + new_to_date + new_from_date + tier_price + manufacturer + sku + image + thumbnail + canonical_url + updated_at + created_at + categories { + id + position + position + position + position + position + position + position + position + position + position + position + position + position + position + position + position + position + position + position + level + url_key + url_path + product_count + default_sort_by + breadcrumbs { + category_id + category_name + category_url_key + } + } + } + } + } + } + } + } + } + } + } } } } @@ -102,7 +223,7 @@ public function testQueryComplexityIsLimited() } QUERY; - self::expectExceptionMessageRegExp('/Max query complexity should be 50 but got 62/'); + self::expectExceptionMessageRegExp('/Max query complexity should be 160 but got 169/'); $this->graphQlQuery($query); } @@ -139,7 +260,13 @@ public function testQueryDepthIsLimited() categories { products { items { - name + categories { + products { + items { + name + } + } + } } } } @@ -163,7 +290,7 @@ public function testQueryDepthIsLimited() } } QUERY; - self::expectExceptionMessageRegExp('/Max query depth should be 15 but got 20/'); + self::expectExceptionMessageRegExp('/Max query depth should be 20 but got 23/'); $this->graphQlQuery($query); } } diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php index b302f4272527a..3936da21fc56a 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php @@ -38,8 +38,8 @@ class QueryComplexityLimiter * @param int $queryComplexity */ public function __construct( - int $queryDepth = 15, - int $queryComplexity = 50 + int $queryDepth = 20, + int $queryComplexity = 160 ) { $this->queryDepth = $queryDepth; $this->queryComplexity = $queryComplexity; From a43f6603f2114ddaaebc5e76616d0c315e86203b Mon Sep 17 00:00:00 2001 From: vprohorov Date: Wed, 19 Sep 2018 02:50:20 +0300 Subject: [PATCH 027/310] MAGETWO-91537: Search synonyms results missing for words including hyphen and numbers - Removing hyphen from SPECIAL_CHARACTERS constant --- .../Search/Adapter/Mysql/Query/Builder/Match.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php index 8e3758817adf0..28e321d4c5d47 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php @@ -15,11 +15,16 @@ use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; /** + * Class for building select where condition. + * * @api */ class Match implements QueryInterface { - const SPECIAL_CHARACTERS = '-+~/\\<>\'":*$#@()!,.?`=%&^'; + /** + * @var string + */ + const SPECIAL_CHARACTERS = '+~/\\<>\'":*$#@()!,.?`=%&^'; const MINIMAL_CHARACTER_LENGTH = 3; @@ -69,7 +74,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function build( ScoreBuilder $scoreBuilder, @@ -113,6 +118,8 @@ public function build( } /** + * Prepare query value for build function. + * * @param string $queryValue * @param string $conditionType * @return string From 6e20bdd2d7e4bc3b177980620eac2161281f10ae Mon Sep 17 00:00:00 2001 From: Aleksey Tsoy Date: Thu, 20 Sep 2018 16:55:49 +0600 Subject: [PATCH 028/310] MAGETWO-91658: Wrong Checkout Totals Sort Order in cart - Added automated test --- .../Mftf/ActionGroup/CheckoutActionGroup.xml | 8 ++ .../Test/Mftf/Data/CheckoutConfigData.xml | 25 ++++++ .../Mftf/Metadata/checkout_config-meta.xml | 86 +++++++++++++++++++ .../Section/CheckoutCartSummarySection.xml | 1 + .../CheckoutTotalsSortOrderInCartTest.xml | 53 ++++++++++++ .../Test/Mftf/Data/SalesRuleData.xml | 29 +++++++ 6 files changed, 202 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Data/CheckoutConfigData.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Metadata/checkout_config-meta.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index 41be15d0ca410..01a7d8ead9132 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -86,6 +86,14 @@ + + + + + + + + diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/CheckoutConfigData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/CheckoutConfigData.xml new file mode 100644 index 0000000000000..bf2ae28009011 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Data/CheckoutConfigData.xml @@ -0,0 +1,25 @@ + + + + + + ShippingTotalsSortOrder + + + + 27 + + + + DefaultTotalFlagZero + + + 0 + + \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Metadata/checkout_config-meta.xml b/app/code/Magento/Checkout/Test/Mftf/Metadata/checkout_config-meta.xml new file mode 100644 index 0000000000000..55572ee73ac46 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Metadata/checkout_config-meta.xml @@ -0,0 +1,86 @@ + + + + + + + + + integer + + + integer + + + integer + + + integer + + + integer + + + integer + + + integer + + + integer + + + + + + + + + + + + + integer + + + + + integer + + + + + integer + + + + + integer + + + + + integer + + + + + integer + + + + integer + + + integer + + + + + + \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index a1a8d2ba3eade..b1547f2ef4d92 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -9,6 +9,7 @@
+ diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml new file mode 100644 index 0000000000000..e1ec9032e072a --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml @@ -0,0 +1,53 @@ + + + + + + + <stories value="MAGETWO-91658: Wrong Checkout Totals Sort Order in cart"/> + <description value="Checkout Totals Sort Order configuration and displaying in cart"/> + <features value="Checkout"/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-94944"/> + <group value="Checkout"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="defaultCategory"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + + <createData entity="ApiCartRule" stepKey="cartRule"/> + + <createData entity="CheckoutShippingTotalsSortOrder" stepKey="setConfigShippingTotalsSortOrder"/> + </before> + + <actionGroup ref="VerifyDiscountAmount" stepKey="verifyStorefront"> + <argument name="productUrl" value="$$simpleProduct.sku$$.html"/> + <argument name="quantity" value="1"/> + <argument name="expectedDiscount" value="-$61.50"/> + </actionGroup> + + <actionGroup ref="CheckTotalsSortOrderInSummarySection" stepKey="checkTotalsSortOrderInSummarySection"> + <argument name="elementName" value="Shipping (Flat Rate - Fixed)"/> + <argument name="positionNumber" value="3"/> + </actionGroup> + + <after> + <createData entity="DefaultCheckoutTotalsSortOrder" stepKey="setDefaultTotalsSortOrder"/> + + <deleteData createDataKey="cartRule" stepKey="deleteCartRule"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="defaultCategory" stepKey="deleteCategory"/> + + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </after> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index 5b7585b8b2a3f..2ac4917683450 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -39,6 +39,35 @@ <requiredEntity type="SalesRuleLabel">SalesRuleLabelStore1</requiredEntity> </entity> + <entity name="ApiCartRule" type="SalesRule"> + <data key="name" unique="suffix">salesRule</data> + <data key="description">Sales Rule Descritpion</data> + <array key="website_ids"> + <item>1</item> + </array> + <array key="customer_group_ids"> + <item>0</item> + <item>1</item> + <item>3</item> + </array> + <data key="uses_per_customer">0</data> + <data key="is_active">true</data> + <data key="stop_rules_processing">true</data> + <data key="is_advanced">true</data> + <data key="sort_order">0</data> + <data key="simple_action">by_percent</data> + <data key="discount_amount">50</data> + <data key="discount_qty">0</data> + <data key="discount_step">0</data> + <data key="apply_to_shipping">false</data> + <data key="times_used">0</data> + <data key="is_rss">true</data> + <data key="coupon_type">NO_COUPON</data> + <data key="use_auto_generation">false</data> + <data key="uses_per_coupon">0</data> + <data key="simple_free_shipping">0</data> + </entity> + <entity name="SimpleSalesRule" type="SalesRule"> <data key="name" unique="suffix">SimpleSalesRule</data> <data key="is_active">true</data> From 10c08ae5c26307668964bf03ad7ef124ffddde5a Mon Sep 17 00:00:00 2001 From: Oksana_Kremen <Oksana_Kremen@epam.com> Date: Thu, 20 Sep 2018 19:36:02 +0300 Subject: [PATCH 029/310] MAGETWO-91733: Unusual behavior with the persistent shopping cart after the session is expired - Added observer to pass customer data from persistent session to checkout session --- ...tCheckoutSessionPersistentDataObserver.php | 90 +++++++++++ .../SetLoadPersistentQuoteObserver.php | 78 --------- ...ckoutSessionPersistentDataObserverTest.php | 149 ++++++++++++++++++ .../SetLoadPersistentQuoteObserverTest.php | 73 --------- .../Persistent/etc/frontend/events.xml | 2 +- .../Persistent/etc/webapi_rest/events.xml | 2 +- .../Persistent/etc/webapi_soap/events.xml | 2 +- 7 files changed, 242 insertions(+), 154 deletions(-) create mode 100644 app/code/Magento/Persistent/Observer/SetCheckoutSessionPersistentDataObserver.php delete mode 100644 app/code/Magento/Persistent/Observer/SetLoadPersistentQuoteObserver.php create mode 100644 app/code/Magento/Persistent/Test/Unit/Observer/SetCheckoutSessionPersistentDataObserverTest.php delete mode 100644 app/code/Magento/Persistent/Test/Unit/Observer/SetLoadPersistentQuoteObserverTest.php diff --git a/app/code/Magento/Persistent/Observer/SetCheckoutSessionPersistentDataObserver.php b/app/code/Magento/Persistent/Observer/SetCheckoutSessionPersistentDataObserver.php new file mode 100644 index 0000000000000..e89a09c0d2a9c --- /dev/null +++ b/app/code/Magento/Persistent/Observer/SetCheckoutSessionPersistentDataObserver.php @@ -0,0 +1,90 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Persistent\Observer; + +use Magento\Framework\Event\ObserverInterface; + +/** + * Class SetCheckoutSessionPersistentDataObserver + */ +class SetCheckoutSessionPersistentDataObserver implements ObserverInterface +{ + /** + * Persistent session + * + * @var \Magento\Persistent\Helper\Session + */ + private $persistentSession = null; + + /** + * Customer session + * + * @var \Magento\Customer\Model\Session + */ + private $customerSession; + + /** + * Persistent data + * + * @var \Magento\Persistent\Helper\Data + */ + private $persistentData = null; + + /** + * Customer Repository + * + * @var \Magento\Customer\Api\CustomerRepositoryInterface + */ + private $customerRepository = null; + + /** + * @param \Magento\Persistent\Helper\Session $persistentSession + * @param \Magento\Customer\Model\Session $customerSession + * @param \Magento\Persistent\Helper\Data $persistentData + * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository + */ + public function __construct( + \Magento\Persistent\Helper\Session $persistentSession, + \Magento\Customer\Model\Session $customerSession, + \Magento\Persistent\Helper\Data $persistentData, + \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository + ) { + $this->persistentSession = $persistentSession; + $this->customerSession = $customerSession; + $this->persistentData = $persistentData; + $this->customerRepository = $customerRepository; + } + + /** + * Pass customer data from persistent session to checkout session and set quote to be loaded even if not active + * + * @param \Magento\Framework\Event\Observer $observer + * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(\Magento\Framework\Event\Observer $observer) + { + /** @var $checkoutSession \Magento\Checkout\Model\Session */ + $checkoutSession = $observer->getEvent()->getData('checkout_session'); + if ($this->persistentData->isShoppingCartPersist() && $this->persistentSession->isPersistent()) { + $checkoutSession->setCustomerData( + $this->customerRepository->getById($this->persistentSession->getSession()->getCustomerId()) + ); + } + if (!(($this->persistentSession->isPersistent() && !$this->customerSession->isLoggedIn()) + && !$this->persistentData->isShoppingCartPersist() + )) { + return; + } + if ($checkoutSession) { + $checkoutSession->setLoadInactive(); + } + } +} diff --git a/app/code/Magento/Persistent/Observer/SetLoadPersistentQuoteObserver.php b/app/code/Magento/Persistent/Observer/SetLoadPersistentQuoteObserver.php deleted file mode 100644 index 6eeab94a91cca..0000000000000 --- a/app/code/Magento/Persistent/Observer/SetLoadPersistentQuoteObserver.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Persistent\Observer; - -use Magento\Framework\Event\ObserverInterface; - -class SetLoadPersistentQuoteObserver implements ObserverInterface -{ - /** - * Customer session - * - * @var \Magento\Customer\Model\Session - */ - protected $_customerSession; - - /** - * Checkout session - * - * @var \Magento\Checkout\Model\Session - */ - protected $_checkoutSession; - - /** - * Persistent session - * - * @var \Magento\Persistent\Helper\Session - */ - protected $_persistentSession = null; - - /** - * Persistent data - * - * @var \Magento\Persistent\Helper\Data - */ - protected $_persistentData = null; - - /** - * @param \Magento\Persistent\Helper\Session $persistentSession - * @param \Magento\Persistent\Helper\Data $persistentData - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Checkout\Model\Session $checkoutSession - */ - public function __construct( - \Magento\Persistent\Helper\Session $persistentSession, - \Magento\Persistent\Helper\Data $persistentData, - \Magento\Customer\Model\Session $customerSession, - \Magento\Checkout\Model\Session $checkoutSession - ) { - $this->_persistentSession = $persistentSession; - $this->_customerSession = $customerSession; - $this->_checkoutSession = $checkoutSession; - $this->_persistentData = $persistentData; - } - - /** - * Set quote to be loaded even if not active - * - * @param \Magento\Framework\Event\Observer $observer - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - if (!(($this->_persistentSession->isPersistent() && !$this->_customerSession->isLoggedIn()) - && !$this->_persistentData->isShoppingCartPersist() - )) { - return; - } - - if ($this->_checkoutSession) { - $this->_checkoutSession->setLoadInactive(); - } - } -} diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/SetCheckoutSessionPersistentDataObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/SetCheckoutSessionPersistentDataObserverTest.php new file mode 100644 index 0000000000000..01c217c1c5cd4 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Unit/Observer/SetCheckoutSessionPersistentDataObserverTest.php @@ -0,0 +1,149 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Persistent\Test\Unit\Observer; + +/** + * Class SetCheckoutSessionPersistentDataObserverTest + */ +class SetCheckoutSessionPersistentDataObserverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Persistent\Observer\SetCheckoutSessionPersistentDataObserver + */ + private $model; + + /** + * @var \Magento\Persistent\Helper\Data| \PHPUnit_Framework_MockObject_MockObject + */ + private $helperMock; + + /** + * @var \Magento\Persistent\Helper\Session| \PHPUnit_Framework_MockObject_MockObject + */ + private $sessionHelperMock; + + /** + * @var \Magento\Checkout\Model\Session| \PHPUnit_Framework_MockObject_MockObject + */ + private $checkoutSessionMock; + + /** + * @var \Magento\Customer\Model\Session| \PHPUnit_Framework_MockObject_MockObject + */ + private $customerSessionMock; + + /** + * @var \Magento\Persistent\Model\Session| \PHPUnit_Framework_MockObject_MockObject + */ + private $persistentSessionMock; + + /** + * @var \Magento\Customer\Api\CustomerRepositoryInterface| \PHPUnit_Framework_MockObject_MockObject + */ + private $customerRepositoryMock; + + /** + * @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject + */ + private $observerMock; + + /** + * @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->helperMock = $this->createMock(\Magento\Persistent\Helper\Data::class); + $this->sessionHelperMock = $this->createMock(\Magento\Persistent\Helper\Session::class); + $this->checkoutSessionMock = $this->createMock(\Magento\Checkout\Model\Session::class); + $this->customerSessionMock = $this->createMock(\Magento\Customer\Model\Session::class); + $this->observerMock = $this->createMock(\Magento\Framework\Event\Observer::class); + $this->eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getData']); + $this->persistentSessionMock = $this->createPartialMock( + \Magento\Persistent\Model\Session::class, + ['getCustomerId'] + ); + $this->customerRepositoryMock = $this->createMock( + \Magento\Customer\Api\CustomerRepositoryInterface::class + ); + $this->model = new \Magento\Persistent\Observer\SetCheckoutSessionPersistentDataObserver( + $this->sessionHelperMock, + $this->customerSessionMock, + $this->helperMock, + $this->customerRepositoryMock + ); + } + + /** + * Test execute method when session is not persistent + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testExecuteWhenSessionIsNotPersistent() + { + $this->observerMock->expects($this->once()) + ->method('getEvent') + ->will($this->returnValue($this->eventMock)); + $this->eventMock->expects($this->once()) + ->method('getData') + ->will($this->returnValue($this->checkoutSessionMock)); + $this->sessionHelperMock->expects($this->once()) + ->method('isPersistent') + ->will($this->returnValue(false)); + $this->checkoutSessionMock->expects($this->never()) + ->method('setLoadInactive'); + $this->checkoutSessionMock->expects($this->never()) + ->method('setCustomerData'); + $this->model->execute($this->observerMock); + } + + /** + * Test execute method when session is persistent + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testExecute() + { + $this->observerMock->expects($this->once()) + ->method('getEvent') + ->will($this->returnValue($this->eventMock)); + $this->eventMock->expects($this->once()) + ->method('getData') + ->will($this->returnValue($this->checkoutSessionMock)); + $this->sessionHelperMock->expects($this->exactly(2)) + ->method('isPersistent') + ->will($this->returnValue(true)); + $this->customerSessionMock->expects($this->once()) + ->method('isLoggedIn') + ->will($this->returnValue(false)); + $this->helperMock->expects($this->exactly(2)) + ->method('isShoppingCartPersist') + ->will($this->returnValue(true)); + $this->persistentSessionMock->expects($this->once()) + ->method('getCustomerId') + ->will($this->returnValue(123)); + $this->sessionHelperMock->expects($this->once()) + ->method('getSession') + ->will($this->returnValue($this->persistentSessionMock)); + $this->customerRepositoryMock->expects($this->once()) + ->method('getById') + ->will($this->returnValue(true)); //? + $this->checkoutSessionMock->expects($this->never()) + ->method('setLoadInactive'); + $this->checkoutSessionMock->expects($this->once()) + ->method('setCustomerData'); + $this->model->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/SetLoadPersistentQuoteObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/SetLoadPersistentQuoteObserverTest.php deleted file mode 100644 index fd78a6852ea59..0000000000000 --- a/app/code/Magento/Persistent/Test/Unit/Observer/SetLoadPersistentQuoteObserverTest.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Persistent\Test\Unit\Observer; - -class SetLoadPersistentQuoteObserverTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Persistent\Observer\SetLoadPersistentQuoteObserver - */ - protected $model; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $helperMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $sessionHelperMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $checkoutSessionMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $customerSessionMock; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $observerMock; - - protected function setUp() - { - $this->helperMock = $this->createMock(\Magento\Persistent\Helper\Data::class); - $this->sessionHelperMock = $this->createMock(\Magento\Persistent\Helper\Session::class); - $this->checkoutSessionMock = $this->createMock(\Magento\Checkout\Model\Session::class); - $this->customerSessionMock = $this->createMock(\Magento\Customer\Model\Session::class); - $this->observerMock = $this->createMock(\Magento\Framework\Event\Observer::class); - - $this->model = new \Magento\Persistent\Observer\SetLoadPersistentQuoteObserver( - $this->sessionHelperMock, - $this->helperMock, - $this->customerSessionMock, - $this->checkoutSessionMock - ); - } - - public function testExecuteWhenSessionIsNotPersistent() - { - $this->sessionHelperMock->expects($this->once())->method('isPersistent')->will($this->returnValue(false)); - $this->checkoutSessionMock->expects($this->never())->method('setLoadInactive'); - $this->model->execute($this->observerMock); - } - - public function testExecute() - { - $this->sessionHelperMock->expects($this->once())->method('isPersistent')->will($this->returnValue(true)); - $this->customerSessionMock->expects($this->once())->method('isLoggedIn')->will($this->returnValue(false)); - $this->helperMock->expects($this->once())->method('isShoppingCartPersist')->will($this->returnValue(true)); - $this->checkoutSessionMock->expects($this->never())->method('setLoadInactive'); - $this->model->execute($this->observerMock); - } -} diff --git a/app/code/Magento/Persistent/etc/frontend/events.xml b/app/code/Magento/Persistent/etc/frontend/events.xml index 193b9a10818e4..79720695ea6f6 100644 --- a/app/code/Magento/Persistent/etc/frontend/events.xml +++ b/app/code/Magento/Persistent/etc/frontend/events.xml @@ -49,7 +49,7 @@ <observer name="persistent" instance="Magento\Persistent\Observer\SetQuotePersistentDataObserver" /> </event> <event name="custom_quote_process"> - <observer name="persistent" instance="Magento\Persistent\Observer\SetLoadPersistentQuoteObserver" /> + <observer name="persistent" instance="Magento\Persistent\Observer\SetCheckoutSessionPersistentDataObserver" /> </event> <event name="customer_register_success"> <observer name="persistent" instance="Magento\Persistent\Observer\RemovePersistentCookieOnRegisterObserver" /> diff --git a/app/code/Magento/Persistent/etc/webapi_rest/events.xml b/app/code/Magento/Persistent/etc/webapi_rest/events.xml index 1eff845386bf4..79dffa1834563 100644 --- a/app/code/Magento/Persistent/etc/webapi_rest/events.xml +++ b/app/code/Magento/Persistent/etc/webapi_rest/events.xml @@ -22,7 +22,7 @@ <observer name="persistent" instance="Magento\Persistent\Observer\SetQuotePersistentDataObserver" /> </event> <event name="custom_quote_process"> - <observer name="persistent" instance="Magento\Persistent\Observer\SetLoadPersistentQuoteObserver" /> + <observer name="persistent" instance="Magento\Persistent\Observer\SetCheckoutSessionPersistentDataObserver" /> </event> <event name="customer_register_success"> <observer name="persistent" instance="Magento\Persistent\Observer\RemovePersistentCookieOnRegisterObserver" /> diff --git a/app/code/Magento/Persistent/etc/webapi_soap/events.xml b/app/code/Magento/Persistent/etc/webapi_soap/events.xml index 1eff845386bf4..79dffa1834563 100644 --- a/app/code/Magento/Persistent/etc/webapi_soap/events.xml +++ b/app/code/Magento/Persistent/etc/webapi_soap/events.xml @@ -22,7 +22,7 @@ <observer name="persistent" instance="Magento\Persistent\Observer\SetQuotePersistentDataObserver" /> </event> <event name="custom_quote_process"> - <observer name="persistent" instance="Magento\Persistent\Observer\SetLoadPersistentQuoteObserver" /> + <observer name="persistent" instance="Magento\Persistent\Observer\SetCheckoutSessionPersistentDataObserver" /> </event> <event name="customer_register_success"> <observer name="persistent" instance="Magento\Persistent\Observer\RemovePersistentCookieOnRegisterObserver" /> From 441a98190a29baa239b419fdb2f492ef3a82431d Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 21 Sep 2018 09:08:55 +0200 Subject: [PATCH 030/310] Added correct path to the phpunit file --- .travis.yml | 3 ++- dev/tests/api-functional/phpunit_graphql.xml.dist | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e909bb1980752..a80460095ce01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,5 +62,6 @@ script: # The scripts for grunt/phpunit type tests - if [ $TEST_SUITE == "functional" ]; then dev/tests/functional/vendor/phpunit/phpunit/phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi + - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js" ] && [ $TEST_SUITE != "api-functional" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi + - if [ $TEST_SUITE == "api-functional" ]; then phpunit -c dev/tests/$TEST_SUITE/phpunit_graphql.xml.dist; fi - if [ $TEST_SUITE == "js" ]; then grunt $GRUNT_COMMAND; fi diff --git a/dev/tests/api-functional/phpunit_graphql.xml.dist b/dev/tests/api-functional/phpunit_graphql.xml.dist index 4a57c338ca3a2..955a3a8953fa8 100644 --- a/dev/tests/api-functional/phpunit_graphql.xml.dist +++ b/dev/tests/api-functional/phpunit_graphql.xml.dist @@ -27,7 +27,7 @@ <const name="TESTS_INSTALL_CONFIG_FILE" value="config/install-config-mysql.php"/> <const name="TESTS_GLOBAL_CONFIG_FILE" value="config/config-global.php"/> <!-- Webserver URL --> - <const name="TESTS_BASE_URL" value="http://magento.url"/> + <const name="TESTS_BASE_URL" value="http://magento2.travis"/> <!-- Webserver API user --> <const name="TESTS_WEBSERVICE_USER" value="admin"/> <!-- Webserver API key --> From df755861a85c2087ba71e7adfa0884fdf46c2544 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 21 Sep 2018 10:55:42 +0200 Subject: [PATCH 031/310] Test api-functional execution with phpunit.xml.dist --- .travis.yml | 4 +- dev/tests/api-functional/phpunit.xml.dist | 56 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 dev/tests/api-functional/phpunit.xml.dist diff --git a/.travis.yml b/.travis.yml index a80460095ce01..c6077564b923d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,6 +62,6 @@ script: # The scripts for grunt/phpunit type tests - if [ $TEST_SUITE == "functional" ]; then dev/tests/functional/vendor/phpunit/phpunit/phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js" ] && [ $TEST_SUITE != "api-functional" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - - if [ $TEST_SUITE == "api-functional" ]; then phpunit -c dev/tests/$TEST_SUITE/phpunit_graphql.xml.dist; fi + - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi + #- if [ $TEST_SUITE == "api-functional" ]; then phpunit -c dev/tests/$TEST_SUITE/phpunit_graphql.xml.dist; fi - if [ $TEST_SUITE == "js" ]; then grunt $GRUNT_COMMAND; fi diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist new file mode 100644 index 0000000000000..955a3a8953fa8 --- /dev/null +++ b/dev/tests/api-functional/phpunit.xml.dist @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * PHPUnit configuration for GraphQL web API functional tests. + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.2/phpunit.xsd" + colors="true" + columns="max" + beStrictAboutTestsThatDoNotTestAnything="false" + bootstrap="./framework/bootstrap.php" +> + <!-- Test suites definition --> + <testsuites> + <testsuite name="Magento GraphQL web API functional tests"> + <directory suffix="Test.php">testsuite/Magento/GraphQl</directory> + </testsuite> + </testsuites> + + <!-- PHP INI settings and constants definition --> + <php> + <includePath>./testsuite</includePath> + <const name="TESTS_INSTALL_CONFIG_FILE" value="config/install-config-mysql.php"/> + <const name="TESTS_GLOBAL_CONFIG_FILE" value="config/config-global.php"/> + <!-- Webserver URL --> + <const name="TESTS_BASE_URL" value="http://magento2.travis"/> + <!-- Webserver API user --> + <const name="TESTS_WEBSERVICE_USER" value="admin"/> + <!-- Webserver API key --> + <const name="TESTS_WEBSERVICE_APIKEY" value="123123q"/> + <!-- Define if debugger should be started using XDEBUG_SESSION cookie --> + <const name="TESTS_XDEBUG_ENABLED" value="false"/> + <!-- Define XDEBUG_SESSION cookie value--> + <const name="TESTS_XDEBUG_SESSION" value="phpstorm" /> + + <ini name="date.timezone" value="America/Los_Angeles"/> + + <!-- Semicolon-separated 'glob' patterns, that match global XML configuration files --> + <const name="TESTS_GLOBAL_CONFIG_DIR" value="../../../app/etc"/> + <!-- Whether to cleanup the application before running tests or not --> + <const name="TESTS_CLEANUP" value="enabled"/> + <!--Defines if Magento should be installed before tests execution--> + <const name="TESTS_MAGENTO_INSTALLATION" value="disabled"/> + <!-- Magento mode for tests execution. Possible values are "default", "developer" and "production". --> + <const name="TESTS_MAGENTO_MODE" value="default"/> + </php> + + <!-- Test listeners --> + <listeners> + <listener class="Magento\TestFramework\Event\PhpUnit"/> + </listeners> +</phpunit> From 636142edf17227f7d72ccf0d13555971276aa6f7 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 21 Sep 2018 11:16:01 +0200 Subject: [PATCH 032/310] Test api-functional execution with production mode enabled --- dev/tests/api-functional/phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist index 955a3a8953fa8..a55531aba87e2 100644 --- a/dev/tests/api-functional/phpunit.xml.dist +++ b/dev/tests/api-functional/phpunit.xml.dist @@ -46,7 +46,7 @@ <!--Defines if Magento should be installed before tests execution--> <const name="TESTS_MAGENTO_INSTALLATION" value="disabled"/> <!-- Magento mode for tests execution. Possible values are "default", "developer" and "production". --> - <const name="TESTS_MAGENTO_MODE" value="default"/> + <const name="TESTS_MAGENTO_MODE" value="production"/> </php> <!-- Test listeners --> From 93f6e5e17d024e00cea0bcf6254afc47cf53b73a Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sat, 22 Sep 2018 11:46:44 +0200 Subject: [PATCH 033/310] Added schema concept for working with cart addresses and shipping methods --- .../Magento/QuoteGraphQl/etc/schema.graphqls | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 06b3328b9e058..b10575171781e 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -1,10 +1,82 @@ # Copyright © Magento, Inc. All rights reserved. # See COPYING.txt for license details. +type Query { + getAvailableShippingMethodsOnCart(input: AvailableShippingMethodsOnCartInput): AvailableShippingMethodsOnCartOutput @doc(description:"Returns available shipping methods for cart by address/address_id") +} + type Mutation { createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") + setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput + setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput + setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput +} + +input SetShippingAddressesOnCartInput { + cart_id: String! + customer_address_id: Int # Can be provided in one-page checkout and is required for multi-shipping checkout + address: CartAddressInput + cart_items: [CartItemQuantityInput!] +} + +input CartItemQuantityInput { + cart_item_id: Int! + quantity: Float! +} + +input SetBillingAddressOnCartInput { + cart_id: String! + customer_address_id: Int + address: CartAddressInput + # TODO: consider adding "Same as shipping" option +} + +input CartAddressInput { + firstname: String! + lastname: String! + company: String + street: [String!]! + city: String! + region: String + postcode: String + country_code: String! + telephone: String! + save_in_address_book: Boolean! +} + +input SetShippingMethodsOnCartInput { + shipping_methods: [ShippingMethodForAddressInput!]! +} + +input ShippingMethodForAddressInput { + cart_address_id: String! + shipping_method_code: String! +} + +type SetBillingAddressOnCartOutput { + cart: Cart! +} + +type SetShippingAddressesOnCartOutput { + cart: Cart! +} + +type SetShippingMethodsOnCartOutput { + cart: Cart! +} + +# If no address is provided, the system get address assigned to a quote +# If there's no address at all - the system returns all shipping methods +type AvailableShippingMethodsOnCartInput { + cart_id: String! + customer_address_id: Int + address: CartAddressInput +} + +type AvailableShippingMethodsOnCartOutput { + available_shipping_methods: [CheckoutShippingMethod] } input ApplyCouponToCartInput { @@ -18,12 +90,56 @@ type ApplyCouponToCartOutput { type Cart { applied_coupon: AppliedCoupon + addresses: [CartAddress]! } type CartAddress { + firstname: String! + lastname: String! + company: String + street: [String!]! + city: String! + region: CartAddressRegion + postcode: String + country: CartAddressCountry! + telephone: String! + address_type: AdressTypeEnum! + selected_shipping_method: CheckoutShippingMethod + available_shipping_methods: [CheckoutShippingMethod]! + items_weight: Float + customer_notes: String + cart_items: [CartItemQuantity] applied_coupon: AppliedCoupon } +type CartItemQuantity { + cart_item_id: String! + quantity: Float! +} + +type CartAddressRegion { + code: String + label: String +} + +type CartAddressCountry { + code: String + label: String +} + +type CheckoutShippingMethod { + code: String + label: String + free_shipping: Boolean! + error_message: String + # TODO: Add more complex structure for shipping rates +} + +enum AdressTypeEnum { + SHIPPING + BILLING +} + type AppliedCoupon { code: String! } From e327bb695bf2cb01240d5d442d5d269fb3c50064 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Sat, 22 Sep 2018 15:18:18 +0000 Subject: [PATCH 034/310] graphQl: shipping address concept Signed-off-by: vitaliyboyko <v.boyko@atwix.com> --- .../SetShippingAddressesOnCart.php | 104 ++++++++++++++++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php new file mode 100644 index 0000000000000..9d54792136367 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\ShippingAddressManagementInterface; + +/** + * @inheritdoc + */ +class SetShippingAddressesOnCart implements ResolverInterface +{ + /** + * @var ShippingAddressManagementInterface + */ + private $shippingAddressManagement; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var Address + */ + private $addressModel; + /** + * @var DataObjectHelper + */ + private $dataObjectHelper; + + /** + * @var MaskedQuoteIdToQuoteIdInterface + */ + private $maskedQuoteIdToQuoteId; + + /** + * @param ShippingAddressManagementInterface $shippingAddressManagement + * @param AddressRepositoryInterface $addressRepository + * @param Address $addressModel + * @param DataObjectHelper $dataObjectHelper + * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + */ + public function __construct( + ShippingAddressManagementInterface $shippingAddressManagement, + AddressRepositoryInterface $addressRepository, + Address $addressModel, + DataObjectHelper $dataObjectHelper, + MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + ) { + $this->shippingAddressManagement = $shippingAddressManagement; + $this->addressRepository = $addressRepository; + $this->addressModel = $addressModel; + $this->dataObjectHelper = $dataObjectHelper; + $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($args['input']['cart_id'])) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + $maskedCartId = $args['input']['cart_id']; + $cartId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); + + $customerAddressId = $args['input']['customer_address_id'] ?? 0; + $address = $args['input']['address'] ?? null; + $cartItems = $args['input']['cart_items'] ?? []; + + if (!$customerAddressId && !$address) { + throw new GraphQlInputException(__('Query should contain either address id or address input.')); + } + + if (!$cartItems) { + if($customerAddressId) { + $customerAddress = $this->addressRepository->getById($customerAddressId); + $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress); + $this->shippingAddressManagement->assign($cartId, $shippingAddress); + } else { + $shippingAddress = $this->addressModel->addData($address); + $this->shippingAddressManagement->assign($cartId, $shippingAddress); + } + } else { + //TODO: implement multi shipping address assign flow + } + + return []; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index b10575171781e..d13240a23140b 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -9,7 +9,7 @@ type Mutation { createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") - setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput + setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddress\\SetShippingAddressesOnCart") setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput } From 18f78d5c5b1a760813647b794e5365dfe885367e Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Mon, 24 Sep 2018 15:26:43 +0300 Subject: [PATCH 035/310] MAGETWO-91725: Reward Points Balance Update Emails are not being sent when balance change initiated by store front - Fixed an issue with incorrect work of 'Subscribe for Balance Updates' functionality; --- .../Model/Metadata/CustomerMetadata.php | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Model/Metadata/CustomerMetadata.php b/app/code/Magento/Customer/Model/Metadata/CustomerMetadata.php index 7ed806e657e82..7e8f0b9236fe9 100644 --- a/app/code/Magento/Customer/Model/Metadata/CustomerMetadata.php +++ b/app/code/Magento/Customer/Model/Metadata/CustomerMetadata.php @@ -46,7 +46,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getAttributes($formCode) { @@ -67,7 +67,7 @@ public function getAttributes($formCode) } /** - * {@inheritdoc} + * @inheritdoc */ public function getAttributeMetadata($attributeCode) { @@ -92,7 +92,7 @@ public function getAttributeMetadata($attributeCode) } /** - * {@inheritdoc} + * @inheritdoc */ public function getAllAttributesMetadata() { @@ -116,7 +116,7 @@ public function getAllAttributesMetadata() } /** - * {@inheritdoc} + * @inheritdoc */ public function getCustomAttributesMetadata($dataObjectClassName = self::DATA_INTERFACE_NAME) { @@ -134,13 +134,27 @@ public function getCustomAttributesMetadata($dataObjectClassName = self::DATA_IN $isDataObjectMethod = isset($this->customerDataObjectMethods['get' . $camelCaseKey]) || isset($this->customerDataObjectMethods['is' . $camelCaseKey]); - /** Even though disable_auto_group_change is system attribute, it should be available to the clients */ if (!$isDataObjectMethod - && (!$attributeMetadata->isSystem() || $attributeCode == 'disable_auto_group_change') + && (!$attributeMetadata->isSystem() + || in_array($attributeCode, $this->getAllowedSystemAttributesList()) + ) ) { $customAttributes[] = $attributeMetadata; } } return $customAttributes; } + + /** + * Get list of system attributes which should be available to the clients + * + * @return array + */ + private function getAllowedSystemAttributesList() + { + return [ + 'disable_auto_group_change', + 'reward_update_notification' + ]; + } } From 160f0879e39e39a6baa48723c05255c0fc37c860 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Mon, 24 Sep 2018 16:48:01 +0300 Subject: [PATCH 036/310] MAGETWO-91707: [Sigma Beauty]Cannot pause Youtube video in IE 11 - Fix added --- lib/web/fotorama/fotorama.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js index 89cb315b0d2c4..4bfa2f12d5248 100644 --- a/lib/web/fotorama/fotorama.js +++ b/lib/web/fotorama/fotorama.js @@ -2487,11 +2487,11 @@ fotoramaVersion = '4.6.4'; .append($videoPlay.clone()); // This solves tabbing problems - addFocus(frame, function () { + addFocus(frame, function (e) { setTimeout(function () { lockScroll($stage); }, 0); - clickToShow({index: frameData.eq, user: true}); + clickToShow({index: frameData.eq, user: true}, e); }); $stageFrame = $stageFrame.add($frame); @@ -3040,7 +3040,10 @@ fotoramaVersion = '4.6.4'; return time; } - that.showStage = function (silent, options, time) { + that.showStage = function (silent, options, time, e) { + if (e !== undefined && e.target.tagName == 'IFRAME') { + return; + } unloadVideo($videoPlaying, activeFrame.i !== data[normalizeIndex(repositionIndex)].i); frameDraw(activeIndexes, 'stage'); stageFramePosition(SLOW ? [dirtyIndex] : [dirtyIndex, getPrevIndex(dirtyIndex), getNextIndex(dirtyIndex)]); @@ -3128,7 +3131,7 @@ fotoramaVersion = '4.6.4'; } }; - that.show = function (options) { + that.show = function (options, e) { that.longPress.singlePressInProgress = true; var index = calcActiveIndex(options); @@ -3139,7 +3142,7 @@ fotoramaVersion = '4.6.4'; var silent = _activeFrame === activeFrame && !options.user; - that.showStage(silent, options, time); + that.showStage(silent, options, time, e); that.showNav(silent, options, time); showedFLAG = typeof lastActiveIndex !== 'undefined' && lastActiveIndex !== activeIndex; @@ -3499,7 +3502,7 @@ fotoramaVersion = '4.6.4'; $stage.on('mousemove', stageCursor); - function clickToShow(showOptions) { + function clickToShow(showOptions, e) { clearTimeout(clickToShow.t); if (opts.clicktransition && opts.clicktransition !== opts.transition) { @@ -3516,7 +3519,7 @@ fotoramaVersion = '4.6.4'; }, 10); }, 0); } else { - that.show(showOptions); + that.show(showOptions, e); } } From b4bb58cfd3e0c33c96b4100ea84ba30197ad7c23 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Mon, 24 Sep 2018 16:59:40 +0300 Subject: [PATCH 037/310] MAGETWO-91657: Advanced pricing the bulk discounts by percentage returns error when set - Added new validation rule for percentage value; --- .../Product/Form/Modifier/TierPrice.php | 4 +--- .../view/base/web/js/lib/validation/rules.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php index 5f8d7fcf9049a..1f0da62f61056 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php @@ -154,9 +154,7 @@ private function getUpdatedTierPriceStructure(array $priceMeta) 'addbefore' => '%', 'validation' => [ 'required-entry' => true, - 'validate-number' => true, - 'validate-greater-than-zero' => true, - 'less-than-equals-to' => 100 + 'validate-positive-percent-decimal' => true ], 'visible' => $firstOption && $firstOption['value'] == ProductPriceOptionsInterface::VALUE_PERCENT, diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 74f696f3f2a24..1c647bed2ea7b 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -686,6 +686,24 @@ define([ }, $.mage.__('The value is not within the specified range.') ], + 'validate-positive-percent-decimal': [ + function (value) { + var numValue; + + if (utils.isEmptyNoTrim(value) || !/^\s*-?\d*(\.\d*)?\s*$/.test(value)) { + return false; + } + + numValue = utils.parseNumber(value); + + if (isNaN(numValue)) { + return false; + } + + return utils.isBetween(numValue, 0.01, 100); + }, + $.mage.__('Please enter a valid percentage discount value greater than 0.') + ], 'validate-digits': [ function (value) { return utils.isEmptyNoTrim(value) || !/[^\d]/.test(value); From 5a38724d165ffd0c325d015cffaf7732834b7157 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Mon, 24 Sep 2018 20:00:27 +0300 Subject: [PATCH 038/310] MAGETWO-91650: Translation not working for product alerts - Add store id for product alert --- .../ProductAlert/Controller/Add/Price.php | 16 +++--- .../ProductAlert/Controller/Add/Stock.php | 28 ++++++++--- app/code/Magento/ProductAlert/Model/Email.php | 49 +++++++++++++------ .../Magento/ProductAlert/Model/Observer.php | 19 +++++++ app/code/Magento/ProductAlert/Model/Price.php | 10 ++++ app/code/Magento/ProductAlert/Model/Stock.php | 10 ++++ .../Magento/ProductAlert/etc/db_schema.xml | 16 ++++++ .../ProductAlert/etc/db_schema_whitelist.json | 20 +++++--- 8 files changed, 133 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/ProductAlert/Controller/Add/Price.php b/app/code/Magento/ProductAlert/Controller/Add/Price.php index 04e623105e645..fbaff109fd15a 100644 --- a/app/code/Magento/ProductAlert/Controller/Add/Price.php +++ b/app/code/Magento/ProductAlert/Controller/Add/Price.php @@ -6,6 +6,7 @@ namespace Magento\ProductAlert\Controller\Add; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\ProductAlert\Controller\Add as AddController; use Magento\Framework\App\Action\Context; use Magento\Customer\Model\Session as CustomerSession; @@ -16,7 +17,10 @@ use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\NoSuchEntityException; -class Price extends AddController +/** + * Controller for notifying about price. + */ +class Price extends AddController implements HttpPostActionInterface { /** * @var \Magento\Store\Model\StoreManagerInterface @@ -62,6 +66,8 @@ protected function isInternal($url) } /** + * Method for adding info about product alert price. + * * @return \Magento\Framework\Controller\Result\Redirect */ public function execute() @@ -75,6 +81,7 @@ public function execute() return $resultRedirect; } + $store = $this->storeManager->getStore(); try { /* @var $product \Magento\Catalog\Model\Product */ $product = $this->productRepository->getById($productId); @@ -83,11 +90,8 @@ public function execute() ->setCustomerId($this->customerSession->getCustomerId()) ->setProductId($product->getId()) ->setPrice($product->getFinalPrice()) - ->setWebsiteId( - $this->_objectManager->get(\Magento\Store\Model\StoreManagerInterface::class) - ->getStore() - ->getWebsiteId() - ); + ->setWebsiteId($store->getWebsiteId()) + ->setStoreId($store->getId()); $model->save(); $this->messageManager->addSuccess(__('You saved the alert subscription.')); } catch (NoSuchEntityException $noEntityException) { diff --git a/app/code/Magento/ProductAlert/Controller/Add/Stock.php b/app/code/Magento/ProductAlert/Controller/Add/Stock.php index 56d052f7e11e2..24cab39d544fc 100644 --- a/app/code/Magento/ProductAlert/Controller/Add/Stock.php +++ b/app/code/Magento/ProductAlert/Controller/Add/Stock.php @@ -6,6 +6,7 @@ namespace Magento\ProductAlert\Controller\Add; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\ProductAlert\Controller\Add as AddController; use Magento\Framework\App\Action\Context; use Magento\Customer\Model\Session as CustomerSession; @@ -13,29 +14,44 @@ use Magento\Framework\App\Action\Action; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\StoreManagerInterface; -class Stock extends AddController +/** + * Controller for notifying about stock. + */ +class Stock extends AddController implements HttpPostActionInterface { /** * @var \Magento\Catalog\Api\ProductRepositoryInterface */ protected $productRepository; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param \Magento\Framework\App\Action\Context $context * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param StoreManagerInterface|null $storeManager */ public function __construct( Context $context, CustomerSession $customerSession, - ProductRepositoryInterface $productRepository + ProductRepositoryInterface $productRepository, + StoreManagerInterface $storeManager = null ) { $this->productRepository = $productRepository; parent::__construct($context, $customerSession); + $this->storeManager = $storeManager ?: $this->_objectManager + ->get(\Magento\Store\Model\StoreManagerInterface::class); } /** + * Method for adding info about product alert stock. + * * @return \Magento\Framework\Controller\Result\Redirect */ public function execute() @@ -52,15 +68,13 @@ public function execute() try { /* @var $product \Magento\Catalog\Model\Product */ $product = $this->productRepository->getById($productId); + $store = $this->storeManager->getStore(); /** @var \Magento\ProductAlert\Model\Stock $model */ $model = $this->_objectManager->create(\Magento\ProductAlert\Model\Stock::class) ->setCustomerId($this->customerSession->getCustomerId()) ->setProductId($product->getId()) - ->setWebsiteId( - $this->_objectManager->get(\Magento\Store\Model\StoreManagerInterface::class) - ->getStore() - ->getWebsiteId() - ); + ->setWebsiteId($store->getWebsiteId()) + ->setStoreId($store->getId()); $model->save(); $this->messageManager->addSuccess(__('Alert subscription has been saved.')); } catch (NoSuchEntityException $noEntityException) { diff --git a/app/code/Magento/ProductAlert/Model/Email.php b/app/code/Magento/ProductAlert/Model/Email.php index 90d9e261f97d5..18adc54c2d660 100644 --- a/app/code/Magento/ProductAlert/Model/Email.php +++ b/app/code/Magento/ProductAlert/Model/Email.php @@ -136,6 +136,11 @@ class Email extends AbstractModel */ protected $_customerHelper; + /** + * @var int + */ + private $storeId = null; + /** * @param Context $context * @param Registry $registry @@ -210,6 +215,18 @@ public function setWebsite(\Magento\Store\Model\Website $website) return $this; } + /** + * Set store id from product alert. + * + * @param int $storeId + * @return $this + */ + public function setStoreId(int $storeId) + { + $this->storeId = $storeId; + return $this; + } + /** * Set website id * @@ -330,29 +347,18 @@ protected function _getStockBlock() */ public function send() { - if ($this->_website === null || $this->_customer === null) { - return false; - } - - if (!$this->_website->getDefaultGroup() || !$this->_website->getDefaultGroup()->getDefaultStore()) { - return false; - } - - if (!in_array($this->_type, ['price', 'stock'])) { + if ($this->_website === null || $this->_customer === null || !$this->isExistDefaultStore()) { return false; } $products = $this->getProducts(); - if (count($products) === 0) { - return false; - } - $templateConfigPath = $this->getTemplateConfigPath(); - if (!$templateConfigPath) { + if (!in_array($this->_type, ['price', 'stock']) || count($products) === 0 || !$templateConfigPath) { return false; } - $store = $this->_website->getDefaultStore(); + $storeId = $this->storeId ?: (int) $this->_customer->getStoreId(); + $store = $this->getStore($storeId); $storeId = $store->getId(); $this->_appEmulation->startEnvironmentEmulation($storeId); @@ -438,4 +444,17 @@ private function getTemplateConfigPath(): string ? self::XML_PATH_EMAIL_PRICE_TEMPLATE : self::XML_PATH_EMAIL_STOCK_TEMPLATE; } + + /** + * Check if exists default store. + * + * @return bool + */ + private function isExistDefaultStore(): bool + { + if (!$this->_website->getDefaultGroup() || !$this->_website->getDefaultGroup()->getDefaultStore()) { + return false; + } + return true; + } } diff --git a/app/code/Magento/ProductAlert/Model/Observer.php b/app/code/Magento/ProductAlert/Model/Observer.php index 1870989f11dc7..addc61d2f49a9 100644 --- a/app/code/Magento/ProductAlert/Model/Observer.php +++ b/app/code/Magento/ProductAlert/Model/Observer.php @@ -218,6 +218,7 @@ protected function _processPrice(\Magento\ProductAlert\Model\Email $email) $previousCustomer = null; $email->setWebsite($website); foreach ($collection as $alert) { + $this->setAlertStoreId($alert, $email); try { if (!$previousCustomer || $previousCustomer->getId() != $alert->getCustomerId()) { $customer = $this->customerRepository->getById($alert->getCustomerId()); @@ -311,6 +312,7 @@ protected function _processStock(\Magento\ProductAlert\Model\Email $email) $previousCustomer = null; $email->setWebsite($website); foreach ($collection as $alert) { + $this->setAlertStoreId($alert, $email); try { if (!$previousCustomer || $previousCustomer->getId() != $alert->getCustomerId()) { $customer = $this->customerRepository->getById($alert->getCustomerId()); @@ -427,4 +429,21 @@ public function process() return $this; } + + /** + * Set alert store id. + * + * @param \Magento\ProductAlert\Model\Price|\Magento\ProductAlert\Model\Stock $alert + * @param Email $email + * @return Observer + */ + private function setAlertStoreId($alert, \Magento\ProductAlert\Model\Email $email) : Observer + { + $alertStoreId = $alert->getStoreId(); + if ($alertStoreId) { + $email->setStoreId((int)$alertStoreId); + } + + return $this; + } } diff --git a/app/code/Magento/ProductAlert/Model/Price.php b/app/code/Magento/ProductAlert/Model/Price.php index 0c12b7cfb489e..ecdf4578838aa 100644 --- a/app/code/Magento/ProductAlert/Model/Price.php +++ b/app/code/Magento/ProductAlert/Model/Price.php @@ -26,6 +26,8 @@ * @method \Magento\ProductAlert\Model\Price setSendCount(int $value) * @method int getStatus() * @method \Magento\ProductAlert\Model\Price setStatus(int $value) + * @method int getStoreId() + * @method \Magento\ProductAlert\Model\Stock setStoreId(int $value) * * @author Magento Core Team <core@magentocommerce.com> * @@ -60,6 +62,8 @@ public function __construct( } /** + * Create customer collection. + * * @return void */ protected function _construct() @@ -68,6 +72,8 @@ protected function _construct() } /** + * Create customer collection. + * * @return Collection */ public function getCustomerCollection() @@ -76,6 +82,8 @@ public function getCustomerCollection() } /** + * Load by param. + * * @return $this */ public function loadByParam() @@ -87,6 +95,8 @@ public function loadByParam() } /** + * Method for deleting customer from website. + * * @param int $customerId * @param int $websiteId * @return $this diff --git a/app/code/Magento/ProductAlert/Model/Stock.php b/app/code/Magento/ProductAlert/Model/Stock.php index 4d4dea5c2fe7e..99ba95e633904 100644 --- a/app/code/Magento/ProductAlert/Model/Stock.php +++ b/app/code/Magento/ProductAlert/Model/Stock.php @@ -24,6 +24,8 @@ * @method \Magento\ProductAlert\Model\Stock setSendCount(int $value) * @method int getStatus() * @method \Magento\ProductAlert\Model\Stock setStatus(int $value) + * @method int getStoreId() + * @method \Magento\ProductAlert\Model\Stock setStoreId(int $value) * * @author Magento Core Team <core@magentocommerce.com> * @@ -58,6 +60,8 @@ public function __construct( } /** + * Class constructor. + * * @return void */ protected function _construct() @@ -66,6 +70,8 @@ protected function _construct() } /** + * Create customer collection. + * * @return Collection */ public function getCustomerCollection() @@ -74,6 +80,8 @@ public function getCustomerCollection() } /** + * Load by param. + * * @return $this */ public function loadByParam() @@ -85,6 +93,8 @@ public function loadByParam() } /** + * Method for deleting customer from website. + * * @param int $customerId * @param int $websiteId * @return $this diff --git a/app/code/Magento/ProductAlert/etc/db_schema.xml b/app/code/Magento/ProductAlert/etc/db_schema.xml index ddf8be8a37e9c..1d145482ea38f 100644 --- a/app/code/Magento/ProductAlert/etc/db_schema.xml +++ b/app/code/Magento/ProductAlert/etc/db_schema.xml @@ -18,6 +18,8 @@ comment="Price amount"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Website id"/> + <column xsi:type="smallint" name="store_id" padding="5" unsigned="true" nullable="true" identity="false" + default="0" comment="Store id"/> <column xsi:type="timestamp" name="add_date" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Product alert add date"/> <column xsi:type="timestamp" name="last_send_date" on_update="false" nullable="true" @@ -38,6 +40,9 @@ <constraint xsi:type="foreign" name="PRODUCT_ALERT_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID" table="product_alert_price" column="website_id" referenceTable="store_website" referenceColumn="website_id" onDelete="CASCADE"/> + <constraint xsi:type="foreign" name="PRODUCT_ALERT_PRICE_STORE_ID_STORE_STORE_ID" + table="product_alert_stock" column="website_id" referenceTable="store" + referenceColumn="store_id" onDelete="CASCADE"/> <index name="PRODUCT_ALERT_PRICE_CUSTOMER_ID" indexType="btree"> <column name="customer_id"/> </index> @@ -47,6 +52,9 @@ <index name="PRODUCT_ALERT_PRICE_WEBSITE_ID" indexType="btree"> <column name="website_id"/> </index> + <index name="PRODUCT_ALERT_PRICE_STORE_ID" indexType="btree"> + <column name="store_id"/> + </index> </table> <table name="product_alert_stock" resource="default" engine="innodb" comment="Product Alert Stock"> <column xsi:type="int" name="alert_stock_id" padding="10" unsigned="true" nullable="false" identity="true" @@ -57,6 +65,8 @@ default="0" comment="Product id"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Website id"/> + <column xsi:type="smallint" name="store_id" padding="5" unsigned="true" nullable="true" identity="false" + default="0" comment="Store id"/> <column xsi:type="timestamp" name="add_date" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Product alert add date"/> <column xsi:type="timestamp" name="send_date" on_update="false" nullable="true" @@ -71,6 +81,9 @@ <constraint xsi:type="foreign" name="PRODUCT_ALERT_STOCK_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID" table="product_alert_stock" column="website_id" referenceTable="store_website" referenceColumn="website_id" onDelete="CASCADE"/> + <constraint xsi:type="foreign" name="PRODUCT_ALERT_STOCK_STORE_ID_STORE_STORE_ID" + table="product_alert_stock" column="website_id" referenceTable="store" + referenceColumn="store_id" onDelete="CASCADE"/> <constraint xsi:type="foreign" name="PRODUCT_ALERT_STOCK_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID" table="product_alert_stock" column="customer_id" referenceTable="customer_entity" referenceColumn="entity_id" onDelete="CASCADE"/> @@ -86,5 +99,8 @@ <index name="PRODUCT_ALERT_STOCK_WEBSITE_ID" indexType="btree"> <column name="website_id"/> </index> + <index name="PRODUCT_ALERT_STOCK_STORE_ID" indexType="btree"> + <column name="store_id"/> + </index> </table> </schema> diff --git a/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json b/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json index 266d00e04c5bc..234e3d14876e5 100644 --- a/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json +++ b/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json @@ -9,19 +9,22 @@ "add_date": true, "last_send_date": true, "send_count": true, - "status": true + "status": true, + "store_id": true }, "index": { "PRODUCT_ALERT_PRICE_CUSTOMER_ID": true, "PRODUCT_ALERT_PRICE_PRODUCT_ID": true, - "PRODUCT_ALERT_PRICE_WEBSITE_ID": true + "PRODUCT_ALERT_PRICE_WEBSITE_ID": true, + "PRODUCT_ALERT_PRICE_STORE_ID": true }, "constraint": { "PRIMARY": true, "PRODUCT_ALERT_PRICE_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, "PRODUCT_ALERT_PRICE_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, "PRODUCT_ALERT_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "PRODUCT_ALERT_PRICE_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true + "PRODUCT_ALERT_PRICE_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true, + "PRODUCT_ALERT_PRICE_STORE_ID_STORE_STORE_ID": true } }, "product_alert_stock": { @@ -33,19 +36,22 @@ "add_date": true, "send_date": true, "send_count": true, - "status": true + "status": true, + "store_id": true }, "index": { "PRODUCT_ALERT_STOCK_CUSTOMER_ID": true, "PRODUCT_ALERT_STOCK_PRODUCT_ID": true, - "PRODUCT_ALERT_STOCK_WEBSITE_ID": true + "PRODUCT_ALERT_STOCK_WEBSITE_ID": true, + "PRODUCT_ALERT_STOCK_STORE_ID": true }, "constraint": { "PRIMARY": true, "PRODUCT_ALERT_STOCK_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, "PRODUCT_ALERT_STOCK_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, "PRODUCT_ALERT_STOCK_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "PRODUCT_ALERT_STOCK_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true + "PRODUCT_ALERT_STOCK_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true, + "PRODUCT_ALERT_STOCK_STORE_ID_STORE_STORE_ID": true } } -} \ No newline at end of file +} From d447ad902a61a3ef6b4b1f2303edfee479d1112e Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Tue, 25 Sep 2018 15:29:09 +0400 Subject: [PATCH 039/310] MAGETWO-91609: Problems with operator more/less in the "catalog Products List" widget - Add automated test --- .../Section/AdminCategoryContentSection.xml | 1 + .../AdminCategoryDisplaySettingsSection.xml | 15 ++ .../AdminCreateBlockWithWidgetActionGroup.xml | 49 ++++++ .../Mftf/Section/CatalogWidgetSection.xml | 25 +++ .../CatalogProductListWidgetOperatorsTest.xml | 156 ++++++++++++++++++ 5 files changed, 246 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml create mode 100644 app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminCreateBlockWithWidgetActionGroup.xml create mode 100644 app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml create mode 100644 app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml index 59537274f23c9..742ff4760eada 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml @@ -15,5 +15,6 @@ <element name="uploadImageFile" type="input" selector=".file-uploader-area>input"/> <element name="imageFileName" type="text" selector=".file-uploader-filename"/> <element name="removeImageButton" type="button" selector=".file-uploader-summary .action-remove"/> + <element name="AddCMSBlock" type="select" selector="//*[@name='landing_page']"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml new file mode 100644 index 0000000000000..daa00eb0a27b7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryDisplaySettingsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCategoryDisplaySettingsSection"> + <element name="settingsHeader" type="button" selector="//*[contains(text(),'Display Settings')]" timeout="30"/> + <element name="displayMode" type="button" selector="//*[@name='display_mode']"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminCreateBlockWithWidgetActionGroup.xml b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminCreateBlockWithWidgetActionGroup.xml new file mode 100644 index 0000000000000..1f54ff40283b1 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminCreateBlockWithWidgetActionGroup.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateBlockWithWidget"> + <arguments> + <argument name="addCondition" type="string"/> + <argument name="isCondition" type="string"/> + <argument name="fieldCondition" type="string"/> + </arguments> + + <click stepKey="clickShowHideButton" selector="{{BlockWYSIWYGSection.ShowHideBtn}}"/> + <waitForElementVisible stepKey="waitForInsertWidgetButton" selector="{{CatalogWidgetSection.insertWidgetButton}}"/> + + <selectOption stepKey="selectAllStoreView" userInput="All Store Views" selector="{{CatalogWidgetSection.storeViewOption}}"/> + <fillField selector="{{BlockContentSection.TextArea}}" userInput="" stepKey="makeContentFieldEmpty"/> + + <click selector="{{CatalogWidgetSection.insertWidgetButton}}" stepKey="clickInsertWidgetButton"/> + <waitForElementVisible stepKey="waitForInsertWidgetFrame" selector="{{InsertWidgetSection.widgetTypeDropDown}}" time="10"/> + + <selectOption selector="{{InsertWidgetSection.widgetTypeDropDown}}" userInput="Catalog Products List" stepKey="selectCatalogProductListOption"/> + <waitForElementVisible stepKey="waitForConditionsElementBecomeAvailable" selector="{{InsertWidgetSection.conditionsAddButton}}"/> + + <click selector="{{InsertWidgetSection.conditionsAddButton}}" stepKey="clickToAddCondition"/> + <waitForElementVisible stepKey="waitForSelectBoxOpened" selector="{{InsertWidgetSection.conditionsSelectBox}}"/> + + <selectOption selector="{{InsertWidgetSection.conditionsSelectBox}}" userInput="{{addCondition}}" stepKey="selectConditionsSelectBox"/> + <waitForElementVisible stepKey="seeConditionsAdded" selector="{{InsertWidgetSection.addCondition('1')}}"/> + + <click selector="{{InsertWidgetSection.conditionIs}}" stepKey="clickToConditionIs"/> + <selectOption selector="{{InsertWidgetSection.conditionOperator('1')}}" stepKey="selectOperatorGreaterThan" userInput="{{isCondition}}"/> + + <click selector="{{InsertWidgetSection.addCondition('1')}}" stepKey="clickAddConditionItem"/> + <waitForElementVisible stepKey="waitForConditionFieldOpened" selector="{{InsertWidgetSection.conditionField('1')}}"/> + + <fillField selector="{{InsertWidgetSection.conditionField('1')}}" stepKey="setOperator" userInput="{{fieldCondition}}"/> + <click selector="{{WidgetSection.InsertWidget}}" stepKey="clickInsertWidget"/> + + <waitForElementVisible stepKey="waitForInsertWidgetSaved" selector="{{InsertWidgetSection.save}}"/> + <click stepKey="clickSaveButton" selector="{{InsertWidgetSection.save}}"/> + <see userInput="You saved the block." stepKey="seeSavedBlockMsgOnForm"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml new file mode 100644 index 0000000000000..2957cfdfe6f43 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CatalogWidgetSection"> + <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin"/> + <element name="storeViewOption" type="button" selector="//*[@name='store_id']"/> + </section> + + <section name="InsertWidgetSection"> + <element name="widgetTypeDropDown" type="select" selector="#select_widget_type"/> + <element name="conditionsAddButton" type="button" selector=".rule-param.rule-param-new-child"/> + <element name="conditionsSelectBox" type="button" selector="#conditions__1__new_child"/> + <element name="addCondition" type="button" selector="//*[@id='conditions__1--{{arg1}}__value']/../preceding-sibling::a" parameterized="true"/> + <element name="conditionField" type="button" selector="#conditions__1--{{arg2}}__value" parameterized="true"/> + <element name="save" type="button" selector="#save-button"/> + <element name="conditionIs" type="button" selector="//*[@id='conditions__1--1__attribute']/following-sibling::span[1]"/> + <element name="conditionOperator" type="button" selector="#conditions__1--{{arg3}}__operator" parameterized="true"/> + <element name="checkElementStorefrontByPrice" type="button" selector="//*[@class='product-items widget-product-grid']//*[contains(text(),'${{arg4}}.00')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml new file mode 100644 index 0000000000000..53a4ccb608a6d --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CatalogProductListWidgetOperatorsTest"> + <annotations> + <features value="CatalogWidget"/> + <stories value="MAGETWO-91609: Problems with operator more/less in the 'catalog Products List' widget"/> + <title value="Checking operator more/less in the 'catalog Products List' widget"/> + <description value="Check 'less than', 'equals or greater than', 'equals or less than' operators"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94479"/> + <group value="CatalogWidget"/> + </annotations> + + <before> + <createData entity="SimpleSubCategory" stepKey="simplecategory"/> + <createData entity="SimpleProduct" stepKey="createFirstProduct"> + <requiredEntity createDataKey="simplecategory"/> + <field key="price">10</field> + </createData> + <createData entity="SimpleProduct" stepKey="createSecondProduct"> + <requiredEntity createDataKey="simplecategory"/> + <field key="price">50</field> + </createData> + <createData entity="SimpleProduct" stepKey="createThirdProduct"> + <requiredEntity createDataKey="simplecategory"/> + <field key="price">100</field> + </createData> + + <createData entity="_defaultBlock" stepKey="createPreReqBlock"/> + <!--User log in on back-end as admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + </before> + + <!--Open block with widget.--> + <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage1"> + <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> + </actionGroup> + + <actionGroup ref="AdminCreateBlockWithWidget" stepKey="adminCreateBlockWithWidget"> + <argument name="addCondition" value="Price"/> + <argument name="isCondition" value="greater than"/> + <argument name="fieldCondition" value="20"/> + </actionGroup> + + <!--Go to Catalog > Categories (choose category where created products)--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="onCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoadAddProducts" after="onCategoryIndexPage"/> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickExpandAll" after="waitForCategoryPageLoadAddProducts"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(SimpleSubCategory.name)}}" stepKey="clickCategoryLink"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + + <!--Content > Add CMS Block: name saved block--> + <waitForElementVisible selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="waitForContentSection"/> + <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> + <waitForPageLoad stepKey="waitForContentLoad"/> + + <selectOption selector="{{AdminCategoryContentSection.AddCMSBlock}}" stepKey="selectSavedBlock" userInput="{{_defaultBlock.title}}"/> + + <!--Display Settings > Display Mode: Static block only--> + <waitForElementVisible selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" stepKey="waitForDisplaySettingsSection"/> + <conditionalClick selector="{{AdminCategoryDisplaySettingsSection.settingsHeader}}" dependentSelector="{{AdminCategoryDisplaySettingsSection.displayMode}}" visible="false" stepKey="openDisplaySettingsSection"/> + <waitForPageLoad stepKey="waitForDisplaySettingsLoad"/> + <selectOption stepKey="selectStaticBlockOnlyOption" userInput="Static block only" selector="{{AdminCategoryDisplaySettingsSection.displayMode}}"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategoryWithProducts"/> + <waitForPageLoad stepKey="waitForCategorySaved"/> + <see userInput="You saved the category." stepKey="seeSuccessMessage"/> + + <!--Go to Storefront > category--> + <amOnPage url="$$simplecategory.name$$.html" stepKey="goToStorefrontCategoryPage"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoaded"/> + + <!--Check operators Greater than--> + <dontSeeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('10')}}" stepKey="dontSeeElementByPrice20"/> + <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('50')}}" stepKey="seeElementByPrice50"/> + <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('100')}}" stepKey="seeElementByPrice100"/> + + <!--Open block with widget.--> + <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage2"> + <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> + </actionGroup> + + <actionGroup ref="AdminCreateBlockWithWidget" stepKey="adminCreateBlockWithWidgetLessThan"> + <argument name="addCondition" value="Price"/> + <argument name="isCondition" value="less than"/> + <argument name="fieldCondition" value="20"/> + </actionGroup> + + <!--Go to Storefront > category--> + <amOnPage url="$$simplecategory.name$$.html" stepKey="goToStorefrontCategoryPage2"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoaded2"/> + + <!--Check operators Greater than--> + <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('10')}}" stepKey="seeElementByPrice20"/> + <dontSeeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('50')}}" stepKey="dontSeeElementByPrice50"/> + <dontSeeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('100')}}" stepKey="dontSeeElementByPrice100"/> + + <!--Open block with widget.--> + <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage3"> + <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> + </actionGroup> + + <actionGroup ref="AdminCreateBlockWithWidget" stepKey="adminCreateBlockWithWidgetEqualsOrGreaterThan"> + <argument name="addCondition" value="Price"/> + <argument name="isCondition" value="equals or greater than"/> + <argument name="fieldCondition" value="50"/> + </actionGroup> + + <!--Go to Storefront > category--> + <amOnPage url="$$simplecategory.name$$.html" stepKey="goToStorefrontCategoryPage3"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoaded3"/> + + <!--Check operators Greater than--> + <dontSeeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('10')}}" stepKey="dontSeeElementByPrice20s"/> + <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('50')}}" stepKey="seeElementByPrice50s"/> + <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('100')}}" stepKey="seeElementByPrice100s"/> + + <!--Open block with widget.--> + <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage4"> + <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> + </actionGroup> + + <actionGroup ref="AdminCreateBlockWithWidget" stepKey="adminCreateBlockWithWidgetEqualsOrLessThan"> + <argument name="addCondition" value="Price"/> + <argument name="isCondition" value="equals or less than"/> + <argument name="fieldCondition" value="50"/> + </actionGroup> + + <!--Go to Storefront > category--> + <amOnPage url="$$simplecategory.name$$.html" stepKey="goToStorefrontCategoryPage4"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoaded4"/> + + <!--Check operators Greater than--> + <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('10')}}" stepKey="seeElementByPrice20s"/> + <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('50')}}" stepKey="seeElementByPrice50t"/> + <dontSeeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('100')}}" stepKey="dontSeeElementByPrice100s"/> + + <after> + <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> + <deleteData createDataKey="simplecategory" stepKey="deleteSimpleCategory"/> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createThirdProduct" stepKey="deleteThirdProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + </test> +</tests> From 99b518a6ce248fc61db7a194a05d477422d71b2e Mon Sep 17 00:00:00 2001 From: Aleksey Tsoy <aleksey_tsoy@epam.com> Date: Tue, 25 Sep 2018 18:10:16 +0600 Subject: [PATCH 040/310] MAGETWO-91658: Wrong Checkout Totals Sort Order in cart - Added automated test --- .../Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml index e1ec9032e072a..08623da68a8ef 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml @@ -5,8 +5,7 @@ * See COPYING.txt for license details. */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="CheckoutTotalsSortOrderInCartTest"> <annotations> <title value="Checkout Totals Sort Order configuration and displaying in cart"/> From 12634b0bc936f92ab8b37dbfec5f781e790551d7 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Tue, 25 Sep 2018 15:57:04 +0300 Subject: [PATCH 041/310] MAGETWO-95137: [2.3] Return path e-mail variable system/smtp/return_path_email doesn't work --- app/code/Magento/Email/Model/Transport.php | 44 ++++++++++++++-------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Email/Model/Transport.php b/app/code/Magento/Email/Model/Transport.php index 5a4d64e6d6c0e..c270c3086cca9 100644 --- a/app/code/Magento/Email/Model/Transport.php +++ b/app/code/Magento/Email/Model/Transport.php @@ -31,6 +31,23 @@ class Transport implements TransportInterface */ const XML_PATH_SENDING_RETURN_PATH_EMAIL = 'system/smtp/return_path_email'; + /** + * Configuration of whether return path should be set or no. + * + * Possible values are: + * 0 - no + * 1 - yes (set value as FROM address) + * 2 - use custom value + * + * @var int + */ + private $isSetReturnPath; + + /** + * @var string|null + */ + private $returnPathValue; + /** * @var Sendmail */ @@ -51,25 +68,15 @@ public function __construct( ScopeConfigInterface $scopeConfig, $parameters = null ) { - /* configuration of whether return path should be set or no. Possible values are: - * 0 - no - * 1 - yes (set value as FROM address) - * 2 - use custom value - * @see Magento\Config\Model\Config\Source\Yesnocustom - */ - $isSetReturnPath = $scopeConfig->getValue( + $this->isSetReturnPath = (int) $scopeConfig->getValue( self::XML_PATH_SENDING_SET_RETURN_PATH, ScopeInterface::SCOPE_STORE ); - $returnPathValue = $scopeConfig->getValue( + $this->returnPathValue = $scopeConfig->getValue( self::XML_PATH_SENDING_RETURN_PATH_EMAIL, ScopeInterface::SCOPE_STORE ); - if ($isSetReturnPath == '2' && $returnPathValue !== null) { - $parameters .= ' -f' . \escapeshellarg($returnPathValue); - } - $this->zendTransport = new Sendmail($parameters); $this->message = $message; } @@ -80,9 +87,16 @@ public function __construct( public function sendMessage() { try { - $this->zendTransport->send( - Message::fromString($this->message->getRawMessage()) - ); + $zendMessage = Message::fromString($this->message->getRawMessage()); + if (2 === $this->isSetReturnPath && $this->returnPathValue) { + $zendMessage->setSender($this->returnPathValue); + } elseif (1 === $this->isSetReturnPath && $zendMessage->getFrom()->count()) { + $fromAddressList = $zendMessage->getFrom(); + $fromAddressList->rewind(); + $zendMessage->setSender($fromAddressList->current()->getEmail()); + } + + $this->zendTransport->send($zendMessage); } catch (\Exception $e) { throw new MailException(new Phrase($e->getMessage()), $e); } From b7264dfc698df3536f82c5403fea685169b71226 Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <mikalai_shostka@epam.com> Date: Tue, 25 Sep 2018 18:18:27 +0300 Subject: [PATCH 042/310] MAGETWO-91596: Making order in admin with grouped product by adding it by sku with empty qty field leads to unability to order - Add automated test --- .../AdminOrderFormItemsOrderedSection.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml new file mode 100644 index 0000000000000..11673f1f0fe26 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderFormItemsOrderedSection"> + <element name="addProductsBySku" type="button" selector="//section[@id='order-items']//span[contains(text(),'Add Products By SKU')]"/> + <element name="configureButtonBySku" type="button" selector="//div[@class='sku-configure-button']//span[contains(text(),'Configure')]"/> + <element name="configureProductOk" type="button" selector="//div[@class='page-main-actions']//span[contains(text(),'OK')]"/> + <element name="configureProductQtyField" type="input" selector="//*[@id='super-product-table']/tbody/tr[{{arg}}]/td[5]/input[1]" parameterized="true"/> + <element name="addProductToOrder" type="input" selector="//*[@title='Add Products to Order']"/> + <element name="itemsOrderedSummaryText" type="textarea" selector="//table[@class='data-table admin__table-primary order-tables']/tfoot/tr"/> + </section> +</sections> \ No newline at end of file From caab8f7e1a1c213a7a0928b9c88f9760903ae17a Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 26 Sep 2018 13:12:32 +0300 Subject: [PATCH 043/310] MAGETWO-95137: [2.3] Return path e-mail variable system/smtp/return_path_email doesn't work --- app/code/Magento/Email/Model/Transport.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Email/Model/Transport.php b/app/code/Magento/Email/Model/Transport.php index c270c3086cca9..28d2d171eecf4 100644 --- a/app/code/Magento/Email/Model/Transport.php +++ b/app/code/Magento/Email/Model/Transport.php @@ -32,7 +32,7 @@ class Transport implements TransportInterface const XML_PATH_SENDING_RETURN_PATH_EMAIL = 'system/smtp/return_path_email'; /** - * Configuration of whether return path should be set or no. + * Whether return path should be set or no. * * Possible values are: * 0 - no From 77d2386c243125a31788a11e15771ee25e786993 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Wed, 26 Sep 2018 16:18:19 +0400 Subject: [PATCH 044/310] MAGETWO-91602: Visual Merchandiser incorrectly displays/sorts configurable product price in Tile view - Add automated test --- .../Section/AdminCreateProductConfigurationsPanelSection.xml | 1 + .../Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index 99e47baac37d5..7901b6f2290c9 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -35,5 +35,6 @@ <element name="applySingleQuantityToEachSkus" type="radio" selector=".admin__field-label[for='apply-single-inventory-radio']" timeout="30"/> <element name="quantity" type="input" selector="#apply-single-inventory-input"/> <element name="gridLoadingMask" type="text" selector="[data-role='spinner'][data-component*='product_attributes_listing']"/> + <element name="attributeColorCheckbox" type="select" selector="//div[contains(text(),'color') and @class='data-grid-cell-content']/../preceding-sibling::td/label/input"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml index 25e03676f6870..b7ed7c05e071e 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminManageSwatchSection.xml @@ -21,5 +21,6 @@ <element name="nthChooseColor" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.colorpicker_handler" parameterized="true"/> <element name="nthUploadFile" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) .swatch_row_name.btn_choose_file_upload" parameterized="true"/> <element name="nthDelete" type="button" selector="#swatch-visual-options-panel table tbody tr:nth-of-type({{var}}) button.delete-option" parameterized="true"/> + <element name="deleteBtn" type="button" selector="#manage-options-panel:nth-of-type({{var}}) button.delete-option" parameterized="true"/> </section> </sections> From 3073ed5e8670ab9b095cc6fb6d29d875d4235ce2 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 26 Sep 2018 14:31:18 +0200 Subject: [PATCH 045/310] Shipping method set concept --- .../SetShippingMethodsOnCart.php | 105 ++++++++++++++++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 9 +- 2 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php new file mode 100644 index 0000000000000..448bdcbb37a6d --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver\ShippingMethod; + +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\Stdlib\ArrayManager; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; + +class SetShippingMethodsOnCart implements ResolverInterface +{ + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @var MaskedQuoteIdToQuoteIdInterface + */ + private $maskedQuoteIdToQuoteId; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * SetShippingMethodsOnCart constructor. + * @param ArrayManager $arrayManager + * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + */ + public function __construct( + ArrayManager $arrayManager, + MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, + CartRepositoryInterface $cartRepository + ) { + $this->arrayManager = $arrayManager; + $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->cartRepository = $cartRepository; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $shippingMethods = $this->arrayManager->get('input/shipping_methods', $args); + $maskedCartId = $this->arrayManager->get('input/cart_id', $args); + + if (!$maskedCartId) { + // TODO: throw an exception + } + + if (!$shippingMethods) { + // TODO: throw an exception? + } + + foreach ($shippingMethods as $shippingMethod) { + if (empty($shippingMethod['cart_address_id'])) { + // TODO: throw input exception + } + + if (empty($shippingMethod['shipping_method_code'])) { + // TODO: throw input exception + } + + // TODO: move to a separate class + // TODO: check current customer can apply operations on specified cart + } + + $quoteId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); + $quote = $this->cartRepository->get($quoteId); // TODO: catch no such entity exception + $this->setShippingMethods($shippingMethods, $quote); + + $quote->collectTotals(); + $quote->save(); + //$this->cartRepository->save($quote); + + return 'Success!'; + } + + private function setShippingMethods($shippingMethods, CartInterface $quote) + { + $addresses = $quote->getAllShippingAddresses(); + /** @var \Magento\Quote\Model\Quote\Address $address */ + foreach ($addresses as $address) { + $addressId = $address->getId(); + $shippingMethodForAddress = array_search($addressId, array_column($shippingMethods, 'cart_address_id')); + if ($shippingMethodForAddress !== false) { + $address->setShippingMethod($shippingMethods[$shippingMethodForAddress]['shipping_method_code']); +// $address->setCollectShippingRates(1); + $address->save(); + } + } + // TODO: make sure that shipping method is assigned for all addresses + } +} \ No newline at end of file diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index b10575171781e..027e59c327ebd 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -11,7 +11,7 @@ type Mutation { removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput - setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput + setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingMethod\\SetShippingMethodsOnCart") } input SetShippingAddressesOnCartInput { @@ -47,11 +47,12 @@ input CartAddressInput { } input SetShippingMethodsOnCartInput { + cart_id: String! shipping_methods: [ShippingMethodForAddressInput!]! } input ShippingMethodForAddressInput { - cart_address_id: String! + cart_address_id: String! # todo: int? shipping_method_code: String! } @@ -64,12 +65,12 @@ type SetShippingAddressesOnCartOutput { } type SetShippingMethodsOnCartOutput { - cart: Cart! + cart: String } # If no address is provided, the system get address assigned to a quote # If there's no address at all - the system returns all shipping methods -type AvailableShippingMethodsOnCartInput { +input AvailableShippingMethodsOnCartInput { cart_id: String! customer_address_id: Int address: CartAddressInput From bba7e3525d99eca28f3966af6732b7b41ae01493 Mon Sep 17 00:00:00 2001 From: "Hakobyan, Lusine" <Lusine_Hakobyan@epam.com> Date: Thu, 27 Sep 2018 15:10:36 +0400 Subject: [PATCH 046/310] MAGETWO-91733: Unusual behavior with the persistent shopping cart after the session is expired - Add automated test --- ...ingCartBehaviorAfterSessionExpiredTest.xml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml new file mode 100644 index 0000000000000..c66a2979aa7f5 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CheckShoppingCartBehaviorAfterSessionExpiredTest"> + <annotations> + <features value="Persistent"/> + <stories value="MAGETWO-91733 - Unusual behavior with the persistent shopping cart after the session is expired"/> + <title value="Checking behavior with the persistent shopping cart after the session is expired"/> + <description value="Checking behavior with the persistent shopping cart after the session is expired"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-95118"/> + <group value="persistent"/> + </annotations> + <before> + <!--Enable Persistence--> + <createData entity="PersistentConfigEnabled" stepKey="enablePersistent"/> + <!--Create product--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create new customer --> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="Simple_US_Customer_NY"/> + </actionGroup> + <!--Add shipping information--> + <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> + <argument name="Address" value="US_Address_NY"/> + </actionGroup> + </before> + <after> + <!--Roll back configuration--> + <createData entity="PersistentConfigDefault" stepKey="setDefaultPersistentState"/> + <!--Delete product--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + </after> + <!-- Add simple product to cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart1"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <!--Reset cookies and refresh the page--> + <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> + <reloadPage stepKey="reloadPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <!--Check product exists in cart--> + <see userInput="$$createProduct.name$$" stepKey="ProductExistsInCart"/> + </test> +</tests> From 27474ec6be80104d0b78231719610dacc5de7f2b Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Tue, 25 Sep 2018 15:17:21 +0300 Subject: [PATCH 047/310] MAGETWO-91764: Frontend base url https results in admin too many redirects #8800 - Fixed an issue with cyclic redirects when using 'custom admin url' and 'secure urls in admin'. --- .../Magento/Backend/Model/AdminPathConfig.php | 38 ++++++++-------- .../Test/Unit/Model/AdminPathConfigTest.php | 44 +++++++++++++------ 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Backend/Model/AdminPathConfig.php b/app/code/Magento/Backend/Model/AdminPathConfig.php index e7338adca4a2a..0e77835a5134c 100644 --- a/app/code/Magento/Backend/Model/AdminPathConfig.php +++ b/app/code/Magento/Backend/Model/AdminPathConfig.php @@ -48,10 +48,7 @@ public function __construct( } /** - * {@inheritdoc} - * - * @param \Magento\Framework\App\RequestInterface $request - * @return string + * @inheritdoc */ public function getCurrentSecureUrl(\Magento\Framework\App\RequestInterface $request) { @@ -59,28 +56,29 @@ public function getCurrentSecureUrl(\Magento\Framework\App\RequestInterface $req } /** - * {@inheritdoc} - * - * @param string $path - * @return bool + * @inheritdoc */ public function shouldBeSecure($path) { - return parse_url( - (string)$this->coreConfig->getValue(Store::XML_PATH_UNSECURE_BASE_URL, 'default'), - PHP_URL_SCHEME - ) === 'https' - || $this->backendConfig->isSetFlag(Store::XML_PATH_SECURE_IN_ADMINHTML) - && parse_url( - (string)$this->coreConfig->getValue(Store::XML_PATH_SECURE_BASE_URL, 'default'), - PHP_URL_SCHEME - ) === 'https'; + $baseUrl = (string)$this->coreConfig->getValue(Store::XML_PATH_UNSECURE_BASE_URL, 'default'); + if (parse_url($baseUrl, PHP_URL_SCHEME) === 'https') { + return true; + } + + if ($this->backendConfig->isSetFlag(Store::XML_PATH_SECURE_IN_ADMINHTML)) { + if ($this->backendConfig->isSetFlag('admin/url/use_custom')) { + $adminBaseUrl = (string)$this->coreConfig->getValue('admin/url/custom', 'default'); + } else { + $adminBaseUrl = (string)$this->coreConfig->getValue(Store::XML_PATH_SECURE_BASE_URL, 'default'); + } + return parse_url($adminBaseUrl, PHP_URL_SCHEME) === 'https'; + } + + return false; } /** - * {@inheritdoc} - * - * @return string + * @inheritdoc */ public function getDefaultPath() { diff --git a/app/code/Magento/Backend/Test/Unit/Model/AdminPathConfigTest.php b/app/code/Magento/Backend/Test/Unit/Model/AdminPathConfigTest.php index 4911dc1e9968e..b373459b7864d 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/AdminPathConfigTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/AdminPathConfigTest.php @@ -76,17 +76,35 @@ public function testGetCurrentSecureUrl() * @param $unsecureBaseUrl * @param $useSecureInAdmin * @param $secureBaseUrl + * @param $useCustomUrl + * @param $customUrl * @param $expected * @dataProvider shouldBeSecureDataProvider */ - public function testShouldBeSecure($unsecureBaseUrl, $useSecureInAdmin, $secureBaseUrl, $expected) - { - $coreConfigValueMap = [ + public function testShouldBeSecure( + $unsecureBaseUrl, + $useSecureInAdmin, + $secureBaseUrl, + $useCustomUrl, + $customUrl, + $expected + ) { + $coreConfigValueMap = $this->returnValueMap([ [\Magento\Store\Model\Store::XML_PATH_UNSECURE_BASE_URL, 'default', null, $unsecureBaseUrl], [\Magento\Store\Model\Store::XML_PATH_SECURE_BASE_URL, 'default', null, $secureBaseUrl], - ]; - $this->coreConfig->expects($this->any())->method('getValue')->will($this->returnValueMap($coreConfigValueMap)); - $this->backendConfig->expects($this->any())->method('isSetFlag')->willReturn($useSecureInAdmin); + ['admin/url/custom', 'default', null, $customUrl], + ]); + $backendConfigFlagsMap = $this->returnValueMap([ + [\Magento\Store\Model\Store::XML_PATH_SECURE_IN_ADMINHTML, $useSecureInAdmin], + ['admin/url/use_custom', $useCustomUrl], + ]); + $this->coreConfig->expects($this->atLeast(1))->method('getValue') + ->will($coreConfigValueMap); + $this->coreConfig->expects($this->atMost(2))->method('getValue') + ->will($coreConfigValueMap); + + $this->backendConfig->expects($this->atMost(2))->method('isSetFlag') + ->will($backendConfigFlagsMap); $this->assertEquals($expected, $this->adminPathConfig->shouldBeSecure('')); } @@ -96,13 +114,13 @@ public function testShouldBeSecure($unsecureBaseUrl, $useSecureInAdmin, $secureB public function shouldBeSecureDataProvider() { return [ - ['http://localhost/', false, 'default', false], - ['http://localhost/', true, 'default', false], - ['https://localhost/', false, 'default', true], - ['https://localhost/', true, 'default', true], - ['http://localhost/', false, 'https://localhost/', false], - ['http://localhost/', true, 'https://localhost/', true], - ['https://localhost/', true, 'https://localhost/', true], + ['http://localhost/', false, 'default', false, '', false], + ['http://localhost/', true, 'default', false, '', false], + ['https://localhost/', false, 'default', false, '', true], + ['https://localhost/', true, 'default', false, '', true], + ['http://localhost/', false, 'https://localhost/', false, '', false], + ['http://localhost/', true, 'https://localhost/', false, '', true], + ['https://localhost/', true, 'https://localhost/', false, '', true], ]; } From 312449eb5672f67dd09f6804f1ddd61e714069d2 Mon Sep 17 00:00:00 2001 From: Aleksey Tsoy <aleksey_tsoy@epam.com> Date: Fri, 28 Sep 2018 13:27:10 +0600 Subject: [PATCH 048/310] MAGETWO-91649: #13513: Magento ignore store-level url_key of child category in URL rewrite process for global scope - Added automated test --- .../ActionGroup/AdminCategoryActionGroup.xml | 24 ++++++- .../StorefrontCategoryActionGroup.xml | 15 +++++ ...iteStoreLevelUrlKeyOfChildCategoryTest.xml | 67 +++++++++++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 7c04e9bd83d56..b5b69cefff9a6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -237,8 +237,30 @@ </arguments> <amOnPage url="{{AdminCategoryPage.page}}" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(Category.Name)}}" stepKey="navigateToCreatedCategory" /> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandAll"/> <waitForPageLoad stepKey="waitForPageLoad2"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(Category.Name)}}" stepKey="navigateToCreatedCategory" /> <waitForLoadingMaskToDisappear stepKey="waitForSpinner" /> </actionGroup> + + <actionGroup name="ChangeSeoUrlKey"> + <arguments> + <argument name="value" type="string"/> + </arguments> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{value}}" stepKey="enterURLKey"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> + </actionGroup> + + <actionGroup name="ChangeSeoUrlKeyForSubCategory"> + <arguments> + <argument name="value" type="string"/> + </arguments> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection"/> + <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckDefaultValue"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{value}}" stepKey="enterURLKey"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index 4376e78242fbd..a8256b2d55937 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -50,4 +50,19 @@ <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="switchCategoryViewToListMode"/> <waitForElement selector="{{StorefrontCategoryMainSection.CategoryTitle}}" time="30" stepKey="waitForCategoryReload"/> </actionGroup> + + <actionGroup name="GoToSubCategoryPage"> + <arguments> + <argument name="parentCategory"/> + <argument name="subCategory"/> + <argument name="urlPath" type="string"/> + </arguments> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName(parentCategory.name)}}" stepKey="moveMouseOnMainCategory"/> + <waitForElementVisible selector="{{StorefrontHeaderSection.NavigationCategoryByName(subCategory.name)}}" stepKey="waitForSubCategoryVisible"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(subCategory.name)}}" stepKey="goToCategory"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeInCurrentUrl url="{{urlPath}}.html" stepKey="checkUrl"/> + <seeInTitle userInput="{{subCategory.name}}" stepKey="assertCategoryNameInTitle"/> + <see userInput="{{subCategory.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml new file mode 100644 index 0000000000000..67870c51140a6 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="RewriteStoreLevelUrlKeyOfChildCategoryTest"> + <annotations> + <title value="Rewriting Store-level URL key of child category"/> + <stories value="MAGETWO-91649: #13513: Magento ignore store-level url_key of child category in URL rewrite process for global scope"/> + <description value="Rewriting Store-level URL key of child category"/> + <features value="CatalogUrlRewrite"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94934"/> + <group value="CatalogUrlRewrite"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> + + <createData entity="_defaultCategory" stepKey="defaultCategory"/> + <createData entity="SubCategoryWithParent" stepKey="subCategory"> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + </before> + + <actionGroup ref="navigateToCreatedCategory" stepKey="navigateToCreatedSubCategory"> + <argument name="Category" value="$$subCategory$$"/> + </actionGroup> + + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewForSubCategory"/> + + <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyForSubCategory"> + <argument name="value" value="bags-second"/> + </actionGroup> + + <actionGroup ref="navigateToCreatedCategory" stepKey="navigateToCreatedDefaultCategory"> + <argument name="Category" value="$$defaultCategory$$"/> + </actionGroup> + + <actionGroup ref="ChangeSeoUrlKey" stepKey="changeSeoUrlKeyForDefaultCategory"> + <argument name="value" value="gear-global"/> + </actionGroup> + + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"/> + + <actionGroup ref="GoToSubCategoryPage" stepKey="goToSubCategoryPage"> + <argument name="parentCategory" value="$$defaultCategory$$"/> + <argument name="subCategory" value="$$subCategory$$"/> + <argument name="urlPath" value="gear-global/bags-second"/> + </actionGroup> + + <after> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> + <actionGroup ref="logout" stepKey="logout"/> + + <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> + <deleteData createDataKey="defaultCategory" stepKey="deleteNewRootCategory"/> + </after> + </test> +</tests> From f3772212cdc4a7eb34ed7e85e274a0774b6c51de Mon Sep 17 00:00:00 2001 From: Yurii Borysov <yurii_borysov@epam.com> Date: Fri, 28 Sep 2018 14:42:08 +0300 Subject: [PATCH 049/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Update sorting logic - Add sorting widget --- .../Product/Form/Modifier/GroupedTest.php | 2 + .../Product/Form/Modifier/Grouped.php | 34 ++- .../adminhtml/web/css/grouped-product.css | 37 ++++ .../adminhtml/web/js/grouped-product-grid.js | 209 ++++++++++++++++++ .../web/template/components/position.html | 19 ++ 5 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js create mode 100644 app/code/Magento/GroupedProduct/view/adminhtml/web/template/components/position.html diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GroupedTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GroupedTest.php index 6ef87117deae2..327b47d4a75d8 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GroupedTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GroupedTest.php @@ -34,6 +34,7 @@ class GroupedTest extends AbstractModifierTest const LINKED_PRODUCT_NAME = 'linked'; const LINKED_PRODUCT_QTY = '0'; const LINKED_PRODUCT_POSITION = 1; + const LINKED_PRODUCT_POSITION_CALCULATED = 1; const LINKED_PRODUCT_PRICE = '1'; /** @@ -212,6 +213,7 @@ public function testModifyData() 'price' => null, 'qty' => self::LINKED_PRODUCT_QTY, 'position' => self::LINKED_PRODUCT_POSITION, + 'positionCalculated' => self::LINKED_PRODUCT_POSITION_CALCULATED, 'thumbnail' => null, 'type_id' => null, 'status' => null, diff --git a/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php b/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php index 2d1a1d19757e2..57d9bc78aaf28 100644 --- a/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php +++ b/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php @@ -133,7 +133,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -143,12 +143,17 @@ public function modifyData(array $data) if ($modelId) { $storeId = $this->locator->getStore()->getId(); $data[$product->getId()]['links'][self::LINK_TYPE] = []; - foreach ($this->productLinkRepository->getList($product) as $linkItem) { + $linkedItems = $this->productLinkRepository->getList($product); + usort($linkedItems, function ($a, $b) { + return $a->getPosition() <=> $b->getPosition(); + }); + foreach ($linkedItems as $index => $linkItem) { if ($linkItem->getLinkType() !== self::LINK_TYPE) { continue; } /** @var \Magento\Catalog\Api\Data\ProductInterface $linkedProduct */ $linkedProduct = $this->productRepository->get($linkItem->getLinkedProductSku(), false, $storeId); + $linkItem->setPosition($index); $data[$modelId]['links'][self::LINK_TYPE][] = $this->fillData($linkedProduct, $linkItem); } $data[$modelId][self::DATA_SOURCE_DEFAULT]['current_store_id'] = $storeId; @@ -175,6 +180,7 @@ protected function fillData(ProductInterface $linkedProduct, ProductLinkInterfac 'price' => $currency->toCurrency(sprintf("%f", $linkedProduct->getPrice())), 'qty' => $linkItem->getExtensionAttributes()->getQty(), 'position' => $linkItem->getPosition(), + 'positionCalculated' => $linkItem->getPosition(), 'thumbnail' => $this->imageHelper->init($linkedProduct, 'product_listing_thumbnail')->getUrl(), 'type_id' => $linkedProduct->getTypeId(), 'status' => $this->status->getOptionText($linkedProduct->getStatus()), @@ -185,7 +191,7 @@ protected function fillData(ProductInterface $linkedProduct, ProductLinkInterfac } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyMeta(array $meta) { @@ -454,7 +460,7 @@ protected function getGrid() 'label' => null, 'renderDefaultRecord' => false, 'template' => 'ui/dynamic-rows/templates/grid', - 'component' => 'Magento_Ui/js/dynamic-rows/dynamic-rows-grid', + 'component' => 'Magento_GroupedProduct/js/grouped-product-grid', 'addButton' => false, 'itemTemplate' => 'record', 'dataScope' => 'data.links', @@ -555,6 +561,22 @@ protected function fillMeta() ], ], ], + 'positionCalculated' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'label' => __('Position'), + 'dataType' => Form\Element\DataType\Number::NAME, + 'formElement' => Form\Element\Input::NAME, + 'componentType' => Form\Field::NAME, + 'elementTmpl' => 'Magento_GroupedProduct/components/position', + 'sortOrder' => 90, + 'fit' => true, + 'dataScope' => 'positionCalculated' + ], + ], + ], + ], 'actionDelete' => [ 'arguments' => [ 'data' => [ @@ -563,7 +585,7 @@ protected function fillMeta() 'componentType' => 'actionDelete', 'dataType' => Form\Element\DataType\Text::NAME, 'label' => __('Actions'), - 'sortOrder' => 90, + 'sortOrder' => 100, 'fit' => true, ], ], @@ -577,7 +599,7 @@ protected function fillMeta() 'formElement' => Form\Element\Input::NAME, 'componentType' => Form\Field::NAME, 'dataScope' => 'position', - 'sortOrder' => 100, + 'sortOrder' => 110, 'visible' => false, ], ], diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css b/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css index 3d723387d23b0..1916f222a499b 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css @@ -66,3 +66,40 @@ overflow: hidden; text-overflow: ellipsis; } + + +.position { + width:90px; +} + +.icon-rearrange-position > span { + border: 0; + clip: rect(0, 0, 0, 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +.icon-forward, +.icon-backward { + -webkit-font-smoothing: antialiased; + font-family: 'Admin Icons'; + font-size: 17px; + speak: none; +} + +.position-widget-input { + text-align: center; + width: 40px; +} + +.icon-forward:before { + content: '\e618'; +} + +.icon-backward:before { + content: '\e619'; +} \ No newline at end of file diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js new file mode 100644 index 0000000000000..0a585eb92958d --- /dev/null +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -0,0 +1,209 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore', + 'uiRegistry', + 'Magento_Ui/js/dynamic-rows/dynamic-rows-grid' +], function (_, registry, dynamicRowsGrid) { + 'use strict'; + return dynamicRowsGrid.extend({ + + /** + * Set max element position + * + * @param {Number} position - element position + * @param {Object} elem - instance + */ + setMaxPosition: function (position, elem) { + + if (position || position === 0) { + this.checkMaxPosition(position); + this.sort(position, elem); + if (~~position === this.maxPosition && ~~position > this.getDefaultPageBoundary() + 1) { + this.shiftNextPagesPositions(position); + } + } else { + this.maxPosition += 1; + } + }, + + /** + * Shift positions for next page elements + * + * @param position + */ + shiftNextPagesPositions: function (position) { + console.log("todo: shifting positions to right on next pages related data"); + }, + + + /** + * Update position for element after position from another page is entered + * + * @param data + * @param event + */ + updateGridPosition: function (data, event) { + var inputValue = ~~event.target.value, + recordData = this.recordData(), + record, + updatedRecord; + + record = this.elems().find(function (obj) { + return obj.dataScope === data.parentScope + }).data(); + + if (inputValue === ~~record.positionCalculated) { + return false; + } + + this.elems([]); + + updatedRecord = this.getUpdatedRecordIndex(recordData, record.id); + + if (inputValue >= this.recordData().size() - 1) { + recordData[updatedRecord].position = this.getGlobalMaxPosition() + 1; + } else { + recordData[updatedRecord].position = inputValue; + recordData.forEach(function (value, index) { + if (~~value.id !== ~~record.id) { + recordData[index].position = (index !== inputValue) ? index : index + 1; + } + }); + } + + this.reloadGridData(recordData); + + }, + + /** + * Get updated record index + * + * @param recordData + * @param recordId + * @return {number} + */ + getUpdatedRecordIndex: function (recordData, recordId) { + return recordData.map(function (o) { + return ~~o.id + }).indexOf(~~recordId); + }, + + /** + * + * @param recordData to reprocess + */ + reloadGridData: function (recordData) { + this.recordData(recordData.sort(function (a, b) { + return ~~a.position - ~~b.position; + })); + this._updateCollection(); + this.reload(); + }, + + /** + * Event handler for "Send to bottom" button + * + * @param positionObj + * @return {boolean} + */ + sendToBottom: function (positionObj) { + + var objectToUpdate = this.getObjectToUpdate(positionObj), + recordData = this.recordData(), + updatedRecord; + + if (~~this.currentPage() === this.pages) { + objectToUpdate.position = this.maxPosition; + } else { + this.elems([]); + updatedRecord = this.getUpdatedRecordIndex(recordData, objectToUpdate.data().id); + recordData[updatedRecord].position = this.getGlobalMaxPosition() + 1; + this.reloadGridData(recordData); + } + + return false; + }, + + /** + * Event handler for "Send to top" button + * + * @param positionObj + * @returns {boolean} + */ + sendToTop: function (positionObj) { + var objectToUpdate = this.getObjectToUpdate(positionObj), + recordData = this.recordData(), + updatedRecord; + + //isFirst + if (~~this.currentPage() === 1) { + objectToUpdate.position = 0; + } else { + this.elems([]); + updatedRecord = this.getUpdatedRecordIndex(recordData, objectToUpdate.data().id); + recordData.forEach(function (value, index) { + recordData[index].position = (index === updatedRecord) ? 0 : value.position + 1; + }); + this.reloadGridData(recordData); + } + return false; + }, + + /** + * Get element from grid for update + * + * @param object + * @return {*} + */ + getObjectToUpdate: function (object) { + return this.elems().filter(function (item) { + return item.name === object.parentName; + })[0]; + }, + + /** + * Value function for position input + * + * @param data + * @return {number} + */ + getCalculatedPosition: function (data) { + return (~~this.currentPage() - 1) * this.pageSize + this.elems().pluck("name").indexOf(data.name); + }, + + /** + * Returns start index for current page + * + * @return {number} + */ + getStartIndex() { + return (~~this.currentPage() - 1) * this.pageSize; + }, + + /** + * Return Page Boundary + * + * @return {number} + */ + getDefaultPageBoundary: function () { + return (~~this.currentPage() * this.pageSize) - 1; + }, + + /** + * Returns position for last element to be moved after + * + * @return {number} + */ + getGlobalMaxPosition() { + return _.max(this.recordData().map(function (r) { + return ~~r.position + })); + } + + + }); +}); \ No newline at end of file diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/template/components/position.html b/app/code/Magento/GroupedProduct/view/adminhtml/web/template/components/position.html new file mode 100644 index 0000000000000..dbfeff2e32c04 --- /dev/null +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/template/components/position.html @@ -0,0 +1,19 @@ +<div class="position"> + <a href="#" class="move-top icon-backward icon-rearrange-position" + data-bind=" + click: $parent.sendToTop.bind($parent) + "> + <span>Top</span> + </a> + <input type="text" class="position-widget-input" + data-bind=" + value: $parent.getCalculatedPosition($record()), + event: {blur: $parent.updateGridPosition.bind($parent)} + "/> + <a href="#" class="move-bottom icon-forward icon-rearrange-position" + data-bind=" + click: $parent.sendToBottom.bind($parent) + "> + <span>Bottom</span> + </a> +</div> \ No newline at end of file From c5bf8738d26a07a19a477e14aaf79708ba8052fd Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 28 Sep 2018 16:59:42 +0400 Subject: [PATCH 050/310] MAGETWO-91707: [Sigma Beauty]Cannot pause Youtube video in IE 11 - Add automated test --- .../StorefrontProductInfoMainSection.xml | 4 + .../YoutubeVideoWindowOnProductPageTest.xml | 92 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 564122f71b9f4..c8e1ebcf12d94 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -10,5 +10,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="productVideo" type="text" selector="//*[@class='product-video' and @data-type='{{videoType}}']" parameterized="true"/> + <element name="clickInVideo" type="video" selector="//*[@class='fotorama__stage__shaft']"/> + <element name="videoPausedMode" type="video" selector="//*[contains(@class, 'paused-mode')]"/> + <element name="videoPlayedMode" type="video" selector="//*[contains(@class,'playing-mode')]"/> + <element name="frameVideo" type="video" selector="widget2"/> </section> </sections> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml new file mode 100644 index 0000000000000..7249a4223503e --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="YoutubeVideoWindowOnProductPageTest"> + <annotations> + <features value="ProductVideo"/> + <stories value="MAGETWO-91707: [Sigma Beauty]Cannot pause Youtube video in IE 11"/> + <testCaseId value="MAGETWO-95254"/> + <title value="Youtube video window on the product page"/> + <description value="Check Youtube video window on the product page"/> + <severity value="MAJOR"/> + <group value="ProductVideo"/> + </annotations> + + <before> + <!--Log In--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create category--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!--Create product--> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Set product video Youtube api key configuration --> + <createData entity="ProductVideoYoutubeApiKeyConfig" stepKey="setStoreConfig" after="loginAsAdmin"/> + </before> + + <!--Open simple product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <waitForPageLoad stepKey="wait1"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openFirstProductForEdit"/> + + <!-- Add product video --> + <actionGroup ref="addProductVideo" stepKey="addProductVideo" after="openFirstProductForEdit"/> + <!-- Assert product video in admin product form --> + <actionGroup ref="assertProductVideoAdminProductPage" stepKey="assertProductVideoAdminProductPage" after="addProductVideo"/> + + <!-- Save the product --> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveFirstProduct"/> + <waitForPageLoad stepKey="waitForFirstProductSaved"/> + + <!-- Assert product video in storefront product page --> + <amOnPage url="$$createProduct.name$$.html" stepKey="goToStorefrontCategoryPage"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoaded"/> + <actionGroup ref="assertProductVideoStorefrontProductPage" stepKey="assertProductVideoStorefrontProductPage" after="waitForStorefrontPageLoaded"/> + + <!--Click Play video button--> + <click stepKey="clickToPlayVideo" selector="{{StorefrontProductInfoMainSection.clickInVideo}}"/> + <wait stepKey="waitFiveSecondToPlayVideo" time="5"/> + <switchToIFrame selector="{{StorefrontProductInfoMainSection.frameVideo}}" stepKey="switchToFrame"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.videoPlayedMode}}" stepKey="waitForVideoPlayed"/> + <seeElement selector="{{StorefrontProductInfoMainSection.videoPlayedMode}}" stepKey="AssertVideoIsPlayed"/> + <switchToIFrame stepKey="switchBack1"/> + + <!--Click Pause button--> + <click stepKey="clickToStopVideo" selector="{{StorefrontProductInfoMainSection.clickInVideo}}"/> + <wait stepKey="waitFiveSecondToStopVideo" time="5"/> + <switchToIFrame selector="{{StorefrontProductInfoMainSection.frameVideo}}" stepKey="switchToFrame2"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.videoPausedMode}}" stepKey="waitForVideoPaused"/> + <seeElement selector="{{StorefrontProductInfoMainSection.videoPausedMode}}" stepKey="AssertVideoIsPaused"/> + <switchToIFrame stepKey="switchBack2"/> + + <!--Click Play video button again. Make sure that Video continued playing--> + <click stepKey="clickAgainToPlayVideo" selector="{{StorefrontProductInfoMainSection.clickInVideo}}"/> + <wait stepKey="waitAgainFiveSecondToPlayVideo" time="5"/> + <switchToIFrame selector="{{StorefrontProductInfoMainSection.frameVideo}}" stepKey="switchToFrame3"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.videoPlayedMode}}" stepKey="waitForVideoPlayedAgain"/> + <seeElement selector="{{StorefrontProductInfoMainSection.videoPlayedMode}}" stepKey="AssertVideoIsPlayedAgain"/> + <switchToIFrame stepKey="switchBack3"/> + + <after> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategoryFirst"/> + <!-- Set product video configuration to default --> + <createData entity="DefaultProductVideoConfig" stepKey="setStoreDefaultConfig" before="logout"/> + <!--Log Out--> + <actionGroup ref="logout" stepKey="logout"/> + </after> + </test> +</tests> + From 8f8fc8d9b868e3e73e2267ccca910efa5fa57378 Mon Sep 17 00:00:00 2001 From: Oksana_Kremen <Oksana_Kremen@epam.com> Date: Fri, 28 Sep 2018 17:24:18 +0300 Subject: [PATCH 051/310] MAGETWO-91751: Apostrophe displays as code in the text field box - Skipping to escape single quote --- app/code/Magento/Widget/Model/Widget.php | 6 +++- .../Widget/Test/Unit/Model/WidgetTest.php | 29 ++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index 085e6ec3b2d77..5ba03d008ded0 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -84,6 +84,8 @@ public function __construct( } /** + * Get math random + * * @return \Magento\Framework\Math\Random * * @deprecated 100.1.0 @@ -315,7 +317,7 @@ public function getWidgetDeclaration($type, $params = [], $asIs = true) } } if (isset($value)) { - $directive .= sprintf(' %s="%s"', $name, $this->escaper->escapeQuote($value)); + $directive .= sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false)); } } @@ -339,6 +341,8 @@ public function getWidgetDeclaration($type, $params = [], $asIs = true) } /** + * Get widget page varname + * * @param array $params * @return string * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php index b85a458ed4121..5c546d7e2435c 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php @@ -32,6 +32,9 @@ class WidgetTest extends \PHPUnit\Framework\TestCase */ private $conditionsHelper; + /** + * @inheritdoc + */ protected function setUp() { $this->dataStorageMock = $this->getMockBuilder(\Magento\Widget\Model\Config\Data::class) @@ -55,6 +58,9 @@ protected function setUp() ); } + /** + * Unit test for getWidget + */ public function testGetWidgets() { $expected = ['val1', 'val2']; @@ -65,6 +71,9 @@ public function testGetWidgets() $this->assertEquals($expected, $result); } + /** + * Unit test for getWidgetsWithFilter + */ public function testGetWidgetsWithFilter() { $configFile = __DIR__ . '/_files/mappedConfigArrayAll.php'; @@ -78,6 +87,9 @@ public function testGetWidgetsWithFilter() $this->assertEquals($expected, $result); } + /** + * Unit test for getWidgetsWithUnknownFilter + */ public function testGetWidgetsWithUnknownFilter() { $configFile = __DIR__ . '/_files/mappedConfigArrayAll.php'; @@ -90,6 +102,9 @@ public function testGetWidgetsWithUnknownFilter() $this->assertEquals($expected, $result); } + /** + * Unit test for getWidgetByClassType + */ public function testGetWidgetByClassType() { $widgetOne = ['@' => ['type' => 'type1']]; @@ -101,6 +116,9 @@ public function testGetWidgetByClassType() $this->assertNull($this->widget->getWidgetByClassType('type2')); } + /** + * Unit test for getConfigAsObject + */ public function testGetConfigAsObject() { $configFile = __DIR__ . '/_files/mappedConfigArrayAll.php'; @@ -135,6 +153,9 @@ public function testGetConfigAsObject() $this->assertSame($supportedContainersExpected, $resultObject->getSupportedContainers()); } + /** + * Unit test for getConfigAsObjectWidgetNoFound + */ public function testGetConfigAsObjectWidgetNoFound() { $this->dataStorageMock->expects($this->once()) @@ -146,6 +167,9 @@ public function testGetConfigAsObjectWidgetNoFound() $this->assertSame([], $resultObject->getData()); } + /** + * Unit test for getWidgetDeclaration + */ public function testGetWidgetDeclaration() { $mathRandomMock = $this->createPartialMock(\Magento\Framework\Math\Random::class, ['getRandomString']); @@ -175,7 +199,7 @@ public function testGetWidgetDeclaration() $this->conditionsHelper->expects($this->once())->method('encode')->with($conditions) ->willReturn('encoded-conditions-string'); $this->escaperMock->expects($this->atLeastOnce()) - ->method('escapeQuote') + ->method('escapeHtmlAttr') ->willReturnMap([ ['my "widget"', false, 'my "widget"'], ['1', false, '1'], @@ -203,6 +227,9 @@ public function testGetWidgetDeclaration() $this->assertContains('type_name=""}}', $result); } + /** + * Unit test for getWidgetDeclarationWithZeroValueParam + */ public function testGetWidgetDeclarationWithZeroValueParam() { $mathRandomMock = $this->createPartialMock(\Magento\Framework\Math\Random::class, ['getRandomString']); From a7a43be69cad7ef6ace82ad97756b112ecd6415e Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Mon, 1 Oct 2018 09:56:38 +0300 Subject: [PATCH 052/310] MAGETWO-91650: Translation not working for product alerts - Revert changes --- app/code/Magento/ProductAlert/Model/Email.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/ProductAlert/Model/Email.php b/app/code/Magento/ProductAlert/Model/Email.php index 18adc54c2d660..7aee4ca01240d 100644 --- a/app/code/Magento/ProductAlert/Model/Email.php +++ b/app/code/Magento/ProductAlert/Model/Email.php @@ -359,7 +359,6 @@ public function send() $storeId = $this->storeId ?: (int) $this->_customer->getStoreId(); $store = $this->getStore($storeId); - $storeId = $store->getId(); $this->_appEmulation->startEnvironmentEmulation($storeId); @@ -408,6 +407,18 @@ public function send() return true; } + /** + * Retrieve the store for the email + * + * @param int $storeId + * @return StoreInterface + * @throws NoSuchEntityException + */ + private function getStore(int $storeId): StoreInterface + { + return $this->_storeManager->getStore($storeId); + } + /** * Retrieve the block for the email based on type * From 720a5c63e11d75615e2f3de6c452f5aa097ba0fe Mon Sep 17 00:00:00 2001 From: Yurii Borysov <yurii_borysov@epam.com> Date: Mon, 1 Oct 2018 17:51:19 +0300 Subject: [PATCH 053/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix CR Issues --- .../GroupedProduct/view/adminhtml/web/css/grouped-product.css | 2 +- .../view/adminhtml/web/js/grouped-product-grid.js | 2 +- .../view/adminhtml/web/template/components/position.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css b/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css index 1916f222a499b..cd0aa25ae1f88 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css @@ -102,4 +102,4 @@ .icon-backward:before { content: '\e619'; -} \ No newline at end of file +} diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 0a585eb92958d..7c9563bb1a0ce 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -206,4 +206,4 @@ define([ }); -}); \ No newline at end of file +}); diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/template/components/position.html b/app/code/Magento/GroupedProduct/view/adminhtml/web/template/components/position.html index dbfeff2e32c04..050fb3c2898ce 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/template/components/position.html +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/template/components/position.html @@ -16,4 +16,4 @@ "> <span>Bottom</span> </a> -</div> \ No newline at end of file +</div> From b77c7acf6ee5dca25ab9431e83de91bb00b58795 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Mon, 1 Oct 2018 17:59:51 +0300 Subject: [PATCH 054/310] MAGETWO-58210: Import Products sets default behaviour as append, need add_update - Added "add_update" behavior for checking by default --- .../ImportExport/Model/Import/Entity/AbstractEntity.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php index e965e8ad207fd..96704667bfc61 100644 --- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php +++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php @@ -554,6 +554,7 @@ public function getBehavior() $this->_parameters['behavior'] ) || $this->_parameters['behavior'] != ImportExport::BEHAVIOR_APPEND && + $this->_parameters['behavior'] != ImportExport::BEHAVIOR_ADD_UPDATE && $this->_parameters['behavior'] != ImportExport::BEHAVIOR_REPLACE && $this->_parameters['behavior'] != ImportExport::BEHAVIOR_DELETE ) { @@ -828,6 +829,8 @@ public function validateData() } /** + * Get instance of error aggregator. + * * @return ProcessingErrorAggregatorInterface */ public function getErrorAggregator() From 440e9eb40884e12236f820f9ba1e52cb704e702f Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Tue, 2 Oct 2018 09:47:30 +0300 Subject: [PATCH 055/310] MAGETWO-91711: Unable to delete downloadable product links if sample links are not deleted - Set empty arrays if info doesn't come from request. --- .../Product/Initialization/Helper/Plugin/Downloadable.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Downloadable.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Downloadable.php index f56b219f72db2..a283891afc406 100644 --- a/app/code/Magento/Downloadable/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Downloadable.php +++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Downloadable.php @@ -11,6 +11,9 @@ use Magento\Downloadable\Api\Data\SampleInterfaceFactory; use Magento\Downloadable\Api\Data\LinkInterfaceFactory; +/** + * Class for initialization downloadable info from request. + */ class Downloadable { /** @@ -92,6 +95,8 @@ public function afterInitialize( } } $extension->setDownloadableProductLinks($links); + } else { + $extension->setDownloadableProductLinks([]); } if (isset($downloadable['sample']) && is_array($downloadable['sample'])) { $samples = []; @@ -107,6 +112,8 @@ public function afterInitialize( } } $extension->setDownloadableProductSamples($samples); + } else { + $extension->setDownloadableProductSamples([]); } $product->setExtensionAttributes($extension); if ($product->getLinksPurchasedSeparately()) { From 5f9a9901530d8023b6756a0a73a2a9c7a4c1ee35 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Tue, 28 Aug 2018 13:38:50 +0300 Subject: [PATCH 056/310] MAGETWO-91569: Special characters in CSV return error: General system exception happened - Added message to "Select File to Import" field in import form --- .../ImportExport/Block/Adminhtml/Import/Edit/Form.php | 5 ++++- .../CatalogImportExport/_files/product_export_data.php | 2 +- .../_files/product_export_data_special_chars.php | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php index 39d0d5c7feaee..8fd73e466083a 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php @@ -212,7 +212,10 @@ protected function _prepareForm() 'label' => __('Select File to Import'), 'title' => __('Select File to Import'), 'required' => true, - 'class' => 'input-file' + 'class' => 'input-file', + 'note' => __( + 'File must be saved in UTF-8 encoding for proper import' + ), ] ); $fieldsets['upload']->addField( diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php index 89fb9952cc6f1..477494626b9fb 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php @@ -70,7 +70,7 @@ )->setPrice( 10 )->addData( - ['text_attribute' => '!@#$%^&*()_+1234567890-=|\\:;"\'<,>.?/'] + ['text_attribute' => '!@#$%^&*()_+1234567890-=|\\:;"\'<,>.?/›ƒª'] )->setTierPrice( [0 => ['website_id' => 0, 'cust_group' => 0, 'price_qty' => 3, 'price' => 8]] )->setVisibility( diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php index a2c2c1815a8a9..591dccf229f89 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars.php @@ -25,7 +25,7 @@ ->setName('New Product') ->setSku('simple "1"') ->setPrice(10) - ->addData(['text_attribute' => '!@#$%^&*()_+1234567890-=|\\:;"\'<,>.?/']) + ->addData(['text_attribute' => '!@#$%^&*()_+1234567890-=|\\:;"\'<,>.?/›ƒª']) ->setTierPrice([0 => ['website_id' => 0, 'cust_group' => 0, 'price_qty' => 3, 'price' => 8]]) ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) From 7c5962a43c38024275530ba71215acdd9573bbb5 Mon Sep 17 00:00:00 2001 From: vprohorov <prohorov.vital@gmail.com> Date: Wed, 19 Sep 2018 02:57:08 +0300 Subject: [PATCH 057/310] MAGETWO-91684: Issue with Newsletter subscriptions - Changing subscription behavior to websites --- .../Model/ResourceModel/Subscriber.php | 43 ++++++++++--------- .../Magento/Newsletter/Model/Subscriber.php | 3 ++ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php index b6e53b3695f78..100d179cbb60e 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php @@ -5,6 +5,9 @@ */ namespace Magento\Newsletter\Model\ResourceModel; +use Magento\Framework\App\ObjectManager; +use Magento\Store\Model\StoreManagerInterface; + /** * Newsletter subscriber resource model * @@ -48,6 +51,11 @@ class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ protected $mathRandom; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * Construct * @@ -55,15 +63,18 @@ class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb * @param \Magento\Framework\Stdlib\DateTime\DateTime $date * @param \Magento\Framework\Math\Random $mathRandom * @param string $connectionName + * @param StoreManagerInterface $storeManager */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, \Magento\Framework\Stdlib\DateTime\DateTime $date, \Magento\Framework\Math\Random $mathRandom, - $connectionName = null + $connectionName = null, + StoreManagerInterface $storeManager = null ) { $this->_date = $date; $this->mathRandom = $mathRandom; + $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); parent::__construct($context, $connectionName); } @@ -117,19 +128,15 @@ public function loadByEmail($subscriberEmail) */ public function loadByCustomerData(\Magento\Customer\Api\Data\CustomerInterface $customer) { + $storeIds = $this->storeManager->getWebsite($customer->getWebsiteId())->getStoreIds(); + $select = $this->connection ->select() ->from($this->getMainTable()) - ->where('customer_id=:customer_id and store_id=:store_id'); - - $result = $this->connection - ->fetchRow( - $select, - [ - 'customer_id' => $customer->getId(), - 'store_id' => $customer->getStoreId() - ] - ); + ->where('customer_id = ?', $customer->getId()) + ->where('store_id IN (?)', $storeIds); + + $result = $this->connection->fetchRow($select); if ($result) { return $result; @@ -138,16 +145,10 @@ public function loadByCustomerData(\Magento\Customer\Api\Data\CustomerInterface $select = $this->connection ->select() ->from($this->getMainTable()) - ->where('subscriber_email=:subscriber_email and store_id=:store_id'); - - $result = $this->connection - ->fetchRow( - $select, - [ - 'subscriber_email' => $customer->getEmail(), - 'store_id' => $customer->getStoreId() - ] - ); + ->where('subscriber_email = ?', $customer->getEmail()) + ->where('store_id IN (?)', $storeIds); + + $result = $this->connection->fetchRow($select); if ($result) { return $result; diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index 03976dfcdc197..9611e4b67f1e2 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -392,6 +392,9 @@ public function loadByCustomerId($customerId) try { $customerData = $this->customerRepository->getById($customerId); $customerData->setStoreId($this->_storeManager->getStore()->getId()); + if ($customerData->getWebsiteId() === null) { + $customerData->setWebsiteId($this->_storeManager->getStore()->getWebsiteId()); + } $data = $this->getResource()->loadByCustomerData($customerData); $this->addData($data); if (!empty($data) && $customerData->getId() && !$this->getCustomerId()) { From 9ff3f48412067730c9926bf6354d896d4b21f4a8 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 3 Oct 2018 13:47:29 +0300 Subject: [PATCH 058/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Magento/Backend/Block/Media/Uploader.php | 26 +++++-- .../Backend/Model/Image/ImageUploadConfig.php | 72 +++++++++++++++++++ .../Image/ImageUploadConfigInterface.php | 37 ++++++++++ app/code/Magento/Backend/etc/adminhtml/di.xml | 1 + .../Magento/Backend/etc/adminhtml/system.xml | 15 +++- app/code/Magento/Backend/etc/config.xml | 1 + .../adminhtml/templates/media/uploader.phtml | 3 +- .../view/adminhtml/web/js/media-uploader.js | 16 +++-- .../Magento/Framework/File/Uploader.php | 4 +- .../Framework/Image/Adapter/Config.php | 12 ++++ .../Image/Adapter/UploadConfigInterface.php | 2 + 11 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 app/code/Magento/Backend/Model/Image/ImageUploadConfig.php create mode 100644 app/code/Magento/Backend/Model/Image/ImageUploadConfigInterface.php diff --git a/app/code/Magento/Backend/Block/Media/Uploader.php b/app/code/Magento/Backend/Block/Media/Uploader.php index eb98808dd644b..6962141c465b8 100644 --- a/app/code/Magento/Backend/Block/Media/Uploader.php +++ b/app/code/Magento/Backend/Block/Media/Uploader.php @@ -10,6 +10,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Image\Adapter\UploadConfigInterface; +use Magento\Backend\Model\Image\ImageUploadConfigInterface; /** * Adminhtml media library uploader @@ -39,9 +40,9 @@ class Uploader extends \Magento\Backend\Block\Widget private $jsonEncoder; /** - * @var UploadConfigInterface + * @var ImageUploadConfigInterface */ - private $imageConfig; + private $imageUploadConfig; /** * @param \Magento\Backend\Block\Template\Context $context @@ -49,18 +50,19 @@ class Uploader extends \Magento\Backend\Block\Widget * @param array $data * @param Json $jsonEncoder * @param UploadConfigInterface $imageConfig + * @param ImageUploadConfigInterface $imageUploadConfig */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\File\Size $fileSize, array $data = [], Json $jsonEncoder = null, - UploadConfigInterface $imageConfig = null + UploadConfigInterface $imageConfig = null, + ImageUploadConfigInterface $imageUploadConfig = null ) { $this->_fileSizeService = $fileSize; $this->jsonEncoder = $jsonEncoder ?: ObjectManager::getInstance()->get(Json::class); - $this->imageConfig = $imageConfig ?: ObjectManager::getInstance()->get(UploadConfigInterface::class); - + $this->imageUploadConfig = $imageUploadConfig ?: ObjectManager::getInstance()->get(UploadConfigInterface::class); parent::__construct($context, $data); } @@ -111,7 +113,7 @@ public function getFileSizeService() */ public function getImageUploadMaxWidth() { - return $this->imageConfig->getMaxWidth(); + return $this->imageUploadConfig->getMaxWidth(); } /** @@ -121,7 +123,17 @@ public function getImageUploadMaxWidth() */ public function getImageUploadMaxHeight() { - return $this->imageConfig->getMaxHeight(); + return $this->imageUploadConfig->getMaxHeight(); + } + + /** + * Get image resize configuration + * + * @return bool + */ + public function getIsResizeEnabled(): bool + { + return $this->imageUploadConfig->isResizeEnabled(); } /** diff --git a/app/code/Magento/Backend/Model/Image/ImageUploadConfig.php b/app/code/Magento/Backend/Model/Image/ImageUploadConfig.php new file mode 100644 index 0000000000000..b7e13b1e8274c --- /dev/null +++ b/app/code/Magento/Backend/Model/Image/ImageUploadConfig.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Model\Image; + +/** + * Image uploader config provider. + */ +class ImageUploadConfig implements ImageUploadConfigInterface +{ + /** + * Config path for the maximal image width value + */ + const XML_PATH_MAX_WIDTH_IMAGE = 'system/upload_configuration/max_width'; + + /** + * Config path for the maximal image height value + */ + const XML_PATH_MAX_HEIGHT_IMAGE = 'system/upload_configuration/max_height'; + + /** + * Config path for the maximal image height value + */ + const XML_PATH_ENABLE_RESIZE = 'system/upload_configuration/enable_resize'; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $config; + + /** + * @param \Magento\Framework\App\Config\ScopeConfigInterface $config + */ + public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $config) + { + $this->config = $config; + } + + /** + * Get maximal width value for resized image + * + * @return int + */ + public function getMaxWidth(): int + { + return (int)$this->config->getValue(self::XML_PATH_MAX_WIDTH_IMAGE); + } + + /** + * Get maximal height value for resized image + * + * @return int + */ + public function getMaxHeight(): int + { + return (int)$this->config->getValue(self::XML_PATH_MAX_HEIGHT_IMAGE); + } + + /** + * Get config value for frontend resize + * + * @return bool + */ + public function isResizeEnabled(): bool + { + return (bool)$this->config->getValue(self::XML_PATH_ENABLE_RESIZE); + } +} diff --git a/app/code/Magento/Backend/Model/Image/ImageUploadConfigInterface.php b/app/code/Magento/Backend/Model/Image/ImageUploadConfigInterface.php new file mode 100644 index 0000000000000..254a13407b2c1 --- /dev/null +++ b/app/code/Magento/Backend/Model/Image/ImageUploadConfigInterface.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Model\Image; + +/** + * Interface ImageUploadConfigInterface + * + * Used to retrieve configuration for frontend image uploader + */ +interface ImageUploadConfigInterface +{ + /** + * Get maximal width value for resized image + * + * @return int + */ + public function getMaxWidth(): int; + + /** + * Get maximal height value for resized image + * + * @return int + */ + public function getMaxHeight(): int; + + /** + * Get config value for frontend resize + * + * @return bool + */ + public function isResizeEnabled(): bool; +} diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index 3384384343fe9..dcfc99cfed310 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -168,4 +168,5 @@ </arguments> </type> <preference for="CsrfRequestValidator" type="Magento\Backend\App\Request\BackendValidator" /> + <preference for="Magento\Backend\Model\Image\ImageUploadConfigInterface" type="Magento\Backend\Model\Image\ImageUploadConfig" /> </config> diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index e061455acbe6b..3a0d3e50acc5a 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -338,15 +338,26 @@ </group> <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Images Upload Configuration</label> - <field id="max_width" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable_resize" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <label>Enable Front-end Resize</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>Resize performed by javascript during file upload.</comment> + </field> + <field id="max_width" translate="label comment" type="text" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Maximum Width</label> <validate>validate-greater-than-zero validate-number required-entry</validate> <comment>Maximum allowed width for uploaded image.</comment> + <depends> + <field id="enable_resize">1</field> + </depends> </field> - <field id="max_height" translate="label comment" type="text" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_height" translate="label comment" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Maximum Height</label> <validate>validate-greater-than-zero validate-number required-entry</validate> <comment>Maximum allowed height for uploaded image.</comment> + <depends> + <field id="enable_resize">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Backend/etc/config.xml b/app/code/Magento/Backend/etc/config.xml index 45d283ad3ff22..8283fa18dd370 100644 --- a/app/code/Magento/Backend/etc/config.xml +++ b/app/code/Magento/Backend/etc/config.xml @@ -29,6 +29,7 @@ <enable_charts>1</enable_charts> </dashboard> <upload_configuration> + <enable_resize>1</enable_resize> <max_width>1920</max_width> <max_height>1200</max_height> </upload_configuration> diff --git a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml index 966372773f295..edd54ae38b6a1 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml @@ -14,7 +14,8 @@ "Magento_Backend/js/media-uploader" : { "maxFileSize": <?= /* @escapeNotVerified */ $block->getFileSizeService()->getMaxFileSize() ?>, "maxWidth":<?= /* @escapeNotVerified */ $block->getImageUploadMaxWidth() ?> , - "maxHeight": <?= /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?> + "maxHeight": <?= /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?>, + "isResizeEnabled": <?= /* @noEscape */ $block->getIsResizeEnabled() ?> } }' > diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js index 7e0b6bdfb46dd..35e9ec244df24 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js @@ -35,7 +35,10 @@ define([ _create: function () { var self = this, - progressTmpl = mageTemplate('[data-template="uploader"]'); + progressTmpl = mageTemplate('[data-template="uploader"]'), + resizeConfig = { + action: 'resize' + }; this.element.find('input[type=file]').fileupload({ dataType: 'json', @@ -120,14 +123,19 @@ define([ stop: fileUploader.uploaderConfig.stop }); + if (typeof this.options.isResizeEnabled !== 'undefined' && this.options.isResizeEnabled === true) { + resizeConfig.maxWidth = this.options.maxWidth; + resizeConfig.maxHeight = this.options.maxHeight; + } + this.element.find('input[type=file]').fileupload('option', { process: [{ action: 'load', fileTypes: /^image\/(gif|jpeg|png)$/, maxFileSize: this.options.maxFileSize - }, { - action: 'resize' - }, { + }, + resizeConfig, + { action: 'save' }] }); diff --git a/lib/internal/Magento/Framework/File/Uploader.php b/lib/internal/Magento/Framework/File/Uploader.php index 9a6787ddbaffd..0785177042451 100644 --- a/lib/internal/Magento/Framework/File/Uploader.php +++ b/lib/internal/Magento/Framework/File/Uploader.php @@ -140,14 +140,14 @@ class Uploader * @deprecated * @see \Magento\Framework\Image\Adapter\UploadConfigInterface::getMaxWidth() */ - const MAX_IMAGE_WIDTH = 4096; + const MAX_IMAGE_WIDTH = 1920; /** * Maximum Image Height resolution in pixels. For image resizing on client side * @deprecated * @see \Magento\Framework\Image\Adapter\UploadConfigInterface::getMaxHeight() */ - const MAX_IMAGE_HEIGHT = 2160; + const MAX_IMAGE_HEIGHT = 1200; /** * Resulting of uploaded file diff --git a/lib/internal/Magento/Framework/Image/Adapter/Config.php b/lib/internal/Magento/Framework/Image/Adapter/Config.php index a159bc9ffd448..84b6b6dcfbdd1 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Config.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Config.php @@ -16,8 +16,16 @@ class Config implements ConfigInterface, UploadConfigInterface const XML_PATH_IMAGE_ADAPTERS = 'dev/image/adapters'; + /** + * Config path for the maximal image width value + * @deprecated + */ const XML_PATH_MAX_WIDTH_IMAGE = 'system/upload_configuration/max_width'; + /** + * Config path for the maximal image height value + * @deprecated + */ const XML_PATH_MAX_HEIGHT_IMAGE = 'system/upload_configuration/max_height'; /** @@ -57,6 +65,8 @@ public function getAdapters() * Get Maximum Image Width resolution in pixels. For image resizing on client side. * * @return int + * @deprecated + * @see \Magento\Backend\Model\Image\ImageUploadConfigInterface::getMaxHeight() */ public function getMaxWidth(): int { @@ -67,6 +77,8 @@ public function getMaxWidth(): int * Get Maximum Image Height resolution in pixels. For image resizing on client side. * * @return int + * @deprecated + * @see \Magento\Backend\Model\Image\ImageUploadConfigInterface::getMaxHeight() */ public function getMaxHeight(): int { diff --git a/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php b/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php index c42879060b0b2..990062b83ef6f 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php @@ -9,6 +9,8 @@ /** * Interface UploadConfigInterface + * @deprecated because new interface was introduced + * @see \Magento\Backend\Model\Image\ImageUploadConfigInterface; */ interface UploadConfigInterface { From e5a30aab22728751c8a79a1d0062f4e2206766e4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 3 Oct 2018 14:32:22 +0300 Subject: [PATCH 059/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- app/code/Magento/Backend/Block/Media/Uploader.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Block/Media/Uploader.php b/app/code/Magento/Backend/Block/Media/Uploader.php index 6962141c465b8..1995d72661e2e 100644 --- a/app/code/Magento/Backend/Block/Media/Uploader.php +++ b/app/code/Magento/Backend/Block/Media/Uploader.php @@ -62,7 +62,8 @@ public function __construct( ) { $this->_fileSizeService = $fileSize; $this->jsonEncoder = $jsonEncoder ?: ObjectManager::getInstance()->get(Json::class); - $this->imageUploadConfig = $imageUploadConfig ?: ObjectManager::getInstance()->get(UploadConfigInterface::class); + $this->imageUploadConfig = $imageUploadConfig + ?: ObjectManager::getInstance()->get(ImageUploadConfigInterface::class); parent::__construct($context, $data); } From 7547dd2e0a66d66fc0ed1b4dcb2ccd9de792a6b4 Mon Sep 17 00:00:00 2001 From: Oksana_Kremen <Oksana_Kremen@epam.com> Date: Wed, 3 Oct 2018 15:38:50 +0300 Subject: [PATCH 060/310] MAGETWO-91750: Multiselect attribute values is not searchable under Quick Search when more than one value is selected - Added possibility to retrieve multiselect option values for catalog fulltext search reindexation --- .../Indexer/Fulltext/Action/DataProvider.php | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 83058b6f0ad55..3882921680a42 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -12,6 +12,8 @@ use Magento\Store\Model\Store; /** + * Data provider class + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) * @api @@ -570,8 +572,8 @@ public function prepareProductIndex($indexData, $productData, $storeId) } } foreach ($indexData as $entityId => $attributeData) { - foreach ($attributeData as $attributeId => $attributeValue) { - $value = $this->getAttributeValue($attributeId, $attributeValue, $storeId); + foreach ($attributeData as $attributeId => $attributeValues) { + $value = $this->getAttributeValue($attributeId, $attributeValues, $storeId); if (!empty($value)) { if (isset($index[$attributeId])) { $index[$attributeId][$entityId] = $value; @@ -602,16 +604,16 @@ public function prepareProductIndex($indexData, $productData, $storeId) * Retrieve attribute source value for search * * @param int $attributeId - * @param mixed $valueId + * @param mixed $valueIds * @param int $storeId * @return string */ - private function getAttributeValue($attributeId, $valueId, $storeId) + private function getAttributeValue($attributeId, $valueIds, $storeId) { $attribute = $this->getSearchableAttribute($attributeId); - $value = $this->engine->processAttributeValue($attribute, $valueId); + $value = $this->engine->processAttributeValue($attribute, $valueIds); if (false !== $value) { - $optionValue = $this->getAttributeOptionValue($attributeId, $valueId, $storeId); + $optionValue = $this->getAttributeOptionValue($attributeId, $valueIds, $storeId); if (null === $optionValue) { $value = $this->filterAttributeValue($value); } else { @@ -626,13 +628,15 @@ private function getAttributeValue($attributeId, $valueId, $storeId) * Get attribute option value * * @param int $attributeId - * @param int $valueId + * @param int|string $valueIds * @param int $storeId * @return null|string */ - private function getAttributeOptionValue($attributeId, $valueId, $storeId) + private function getAttributeOptionValue($attributeId, $valueIds, $storeId) { $optionKey = $attributeId . '-' . $storeId; + $attributeValueIds = explode(',', $valueIds); + $attributeOptionValue = ''; if (!array_key_exists($optionKey, $this->attributeOptions) ) { $attribute = $this->getSearchableAttribute($attributeId); @@ -650,8 +654,10 @@ private function getAttributeOptionValue($attributeId, $valueId, $storeId) $this->attributeOptions[$optionKey] = null; } } - - return $this->attributeOptions[$optionKey][$valueId] ?? null; + foreach ($attributeValueIds as $attrValueId) { + $attributeOptionValue .= $this->attributeOptions[$optionKey][$attrValueId] . ' '; + } + return empty($attributeOptionValue) ? null : trim($attributeOptionValue); } /** From 94ce40832e4c405f22a748ff8445c1b5777589a1 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Wed, 3 Oct 2018 17:44:23 +0300 Subject: [PATCH 061/310] MAGETWO-91649: #13513: Magento ignore store-level url_key of child category in URL rewrite process for global scope - Fixed codestyle issue in 'ChildrenUrlRewriteGenerator'; --- .../Model/Category/ChildrenUrlRewriteGenerator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Category/ChildrenUrlRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/Category/ChildrenUrlRewriteGenerator.php index f6049bbff02c4..d18034220cfa8 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Category/ChildrenUrlRewriteGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Category/ChildrenUrlRewriteGenerator.php @@ -71,7 +71,8 @@ public function __construct( public function generate($storeId, Category $category, $rootCategoryId = null) { $mergeDataProvider = clone $this->mergeDataProviderPrototype; - if ($childrenIds = $this->childrenCategoriesProvider->getChildrenIds($category, true)) { + $childrenIds = $this->childrenCategoriesProvider->getChildrenIds($category, true); + if ($childrenIds) { foreach ($childrenIds as $childId) { /** @var Category $childCategory */ $childCategory = $this->categoryRepository->get($childId, $storeId); From 24672336afc2a748ab6f221a534f93ff1e894fdd Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 3 Oct 2018 18:00:18 +0200 Subject: [PATCH 062/310] Concept for setting shipping method only for single address checkout --- .../SetShippingMethodsOnCart.php | 111 +++++++++++------- .../Magento/QuoteGraphQl/etc/schema.graphqls | 5 +- 2 files changed, 71 insertions(+), 45 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index 448bdcbb37a6d..f2e1be3bb0508 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -7,44 +7,63 @@ namespace Magento\QuoteGraphQl\Model\Resolver\ShippingMethod; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\StateException; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\Quote\Model\ShippingMethodManagementInterface; +use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; +/** + * Class SetShippingMethodsOnCart + * + * Mutation resolver for setting shipping methods for shopping cart + */ class SetShippingMethodsOnCart implements ResolverInterface { - /** - * @var CartRepositoryInterface - */ - private $cartRepository; - /** * @var MaskedQuoteIdToQuoteIdInterface */ private $maskedQuoteIdToQuoteId; - /** * @var ArrayManager */ private $arrayManager; + /** + * @var ShippingMethodManagementInterface + */ + private $shippingMethodManagement; + + /** + * @var IsCartMutationAllowedForCurrentUser + */ + private $isCartMutationAllowedForCurrentUser; + /** * SetShippingMethodsOnCart constructor. * @param ArrayManager $arrayManager * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + * @param ShippingMethodManagementInterface $shippingMethodManagement */ public function __construct( ArrayManager $arrayManager, MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - CartRepositoryInterface $cartRepository + ShippingMethodManagementInterface $shippingMethodManagement, + IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser ) { $this->arrayManager = $arrayManager; $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->cartRepository = $cartRepository; + $this->shippingMethodManagement = $shippingMethodManagement; + $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; } /** @@ -56,50 +75,56 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $maskedCartId = $this->arrayManager->get('input/cart_id', $args); if (!$maskedCartId) { - // TODO: throw an exception + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } if (!$shippingMethods) { - // TODO: throw an exception? + throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); } - foreach ($shippingMethods as $shippingMethod) { - if (empty($shippingMethod['cart_address_id'])) { - // TODO: throw input exception - } - - if (empty($shippingMethod['shipping_method_code'])) { - // TODO: throw input exception - } + $shippingMethod = reset($shippingMethods); // TODO: provide implementation for multishipping - // TODO: move to a separate class - // TODO: check current customer can apply operations on specified cart + if (!$shippingMethod['shipping_carrier_code']) { // FIXME: check the E_WARNING here + throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - $quoteId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); - $quote = $this->cartRepository->get($quoteId); // TODO: catch no such entity exception - $this->setShippingMethods($shippingMethods, $quote); + if (!$shippingMethod['shipping_method_code']) { // FIXME: check the E_WARNING here + throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); + } - $quote->collectTotals(); - $quote->save(); - //$this->cartRepository->save($quote); + try { + $cartId = $this->maskedQuoteIdToQuoteId->execute((string) $maskedCartId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) + ); + } - return 'Success!'; - } + if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) { + throw new GraphQlAuthorizationException( + __( + 'The current user cannot perform operations on cart "%masked_cart_id"', + ['masked_cart_id' => $maskedCartId] + ) + ); + } - private function setShippingMethods($shippingMethods, CartInterface $quote) - { - $addresses = $quote->getAllShippingAddresses(); - /** @var \Magento\Quote\Model\Quote\Address $address */ - foreach ($addresses as $address) { - $addressId = $address->getId(); - $shippingMethodForAddress = array_search($addressId, array_column($shippingMethods, 'cart_address_id')); - if ($shippingMethodForAddress !== false) { - $address->setShippingMethod($shippingMethods[$shippingMethodForAddress]['shipping_method_code']); -// $address->setCollectShippingRates(1); - $address->save(); - } + try { + $this->shippingMethodManagement->set( + $cartId, + $shippingMethods['shipping_carrier_code'], + $shippingMethods['shipping_method_code'] + ); + } catch (InputException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); + } catch (CouldNotSaveException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); + } catch (StateException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException(__($exception->getMessage())); } - // TODO: make sure that shipping method is assigned for all addresses + + return 'Success!'; // TODO we should return cart here } } \ No newline at end of file diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 8ecb340db691b..45f98daeaed06 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -53,7 +53,8 @@ input SetShippingMethodsOnCartInput { } input ShippingMethodForAddressInput { - cart_address_id: String! # todo: int? + cart_address_id: int + shipping_carrier_code: String! shipping_method_code: String! } @@ -66,7 +67,7 @@ type SetShippingAddressesOnCartOutput { } type SetShippingMethodsOnCartOutput { - cart: String + cart: String #TODO: temp placeholder, should be Cart! } # If no address is provided, the system get address assigned to a quote From ef8f126056b004cea86c5ce726794844e969f848 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Thu, 4 Oct 2018 15:15:11 +0300 Subject: [PATCH 063/310] graph-ql: concept of shipping address coverage, added separate flows for multi shipping and single shipping --- .../MultiShipping.php | 23 ++++ .../SingleShipping.php | 63 ++++++++++ .../SetShippingAddressesOnCart.php | 109 ++++++++++++------ 3 files changed, 162 insertions(+), 33 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php new file mode 100644 index 0000000000000..71560a26c03ee --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php @@ -0,0 +1,23 @@ +<?php +/** + * @author Atwix Team + * @copyright Copyright (c) 2018 Atwix (https://www.atwix.com/) + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart; + +class MultiShipping +{ + /** + * @param int $cartId + * @param array $cartItems + * @param int|null $customerAddressId + * @param array|null $address + * @return void + */ + public function setAddress(int $cartId, array $cartItems, ?int $customerAddressId, ?array $address): void + { + //TODO: implement multi shipping + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php new file mode 100644 index 0000000000000..536a907541b1a --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php @@ -0,0 +1,63 @@ +<?php +/** + * @author Atwix Team + * @copyright Copyright (c) 2018 Atwix (https://www.atwix.com/) + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart; + +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\ShippingAddressManagementInterface; +use Magento\Customer\Api\AddressRepositoryInterface; + +class SingleShipping +{ + /** + * @var ShippingAddressManagementInterface + */ + private $shippingAddressManagement; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var Address + */ + private $addressModel; + + /** + * @param ShippingAddressManagementInterface $shippingAddressManagement + * @param AddressRepositoryInterface $addressRepository + * @param Address $addressModel + */ + public function __construct( + ShippingAddressManagementInterface $shippingAddressManagement, + AddressRepositoryInterface $addressRepository, + Address $addressModel + ) { + $this->shippingAddressManagement = $shippingAddressManagement; + $this->addressRepository = $addressRepository; + $this->addressModel = $addressModel; + } + + /** + * @param int $cartId + * @param int|null $customerAddressId + * @param array|null $address + * @return void + */ + public function setAddress(int $cartId, ?int $customerAddressId, ?array $address): void + { + if($customerAddressId) { + $customerAddress = $this->addressRepository->getById($customerAddressId); + $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress); + } else { + $shippingAddress = $this->addressModel->addData($address); + } + + $this->shippingAddressManagement->assign($cartId, $shippingAddress); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php index 9d54792136367..f3960f66d2792 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php @@ -7,15 +7,15 @@ namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress; -use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; -use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\ShippingAddressManagementInterface; +use Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart\MultiShipping; +use Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart\SingleShipping; /** * @inheritdoc @@ -23,48 +23,49 @@ class SetShippingAddressesOnCart implements ResolverInterface { /** - * @var ShippingAddressManagementInterface + * @var DataObjectHelper */ - private $shippingAddressManagement; + private $dataObjectHelper; /** - * @var AddressRepositoryInterface + * @var MaskedQuoteIdToQuoteIdInterface */ - private $addressRepository; + private $maskedQuoteIdToQuoteId; /** - * @var Address + * @var MultiShipping */ - private $addressModel; + private $multiShipping; + /** - * @var DataObjectHelper + * @var SingleShipping */ - private $dataObjectHelper; + private $singleShipping; /** - * @var MaskedQuoteIdToQuoteIdInterface + * @var ShippingAddressManagementInterface */ - private $maskedQuoteIdToQuoteId; + private $shippingAddressManagement; /** - * @param ShippingAddressManagementInterface $shippingAddressManagement - * @param AddressRepositoryInterface $addressRepository - * @param Address $addressModel * @param DataObjectHelper $dataObjectHelper * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + * @param MultiShipping $multiShipping + * @param SingleShipping $singleShipping + * @param ShippingAddressManagementInterface $shippingAddressManagement */ public function __construct( - ShippingAddressManagementInterface $shippingAddressManagement, - AddressRepositoryInterface $addressRepository, - Address $addressModel, DataObjectHelper $dataObjectHelper, - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, + MultiShipping $multiShipping, + SingleShipping $singleShipping, + ShippingAddressManagementInterface $shippingAddressManagement ) { - $this->shippingAddressManagement = $shippingAddressManagement; - $this->addressRepository = $addressRepository; - $this->addressModel = $addressModel; $this->dataObjectHelper = $dataObjectHelper; $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->multiShipping = $multiShipping; + $this->singleShipping = $singleShipping; + $this->shippingAddressManagement = $shippingAddressManagement; } /** @@ -78,7 +79,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $maskedCartId = $args['input']['cart_id']; $cartId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); - $customerAddressId = $args['input']['customer_address_id'] ?? 0; + $customerAddressId = $args['input']['customer_address_id'] ?? null; $address = $args['input']['address'] ?? null; $cartItems = $args['input']['cart_items'] ?? []; @@ -86,19 +87,61 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new GraphQlInputException(__('Query should contain either address id or address input.')); } + //TODO: how to determine whether is multi shipping or not if (!$cartItems) { - if($customerAddressId) { - $customerAddress = $this->addressRepository->getById($customerAddressId); - $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress); - $this->shippingAddressManagement->assign($cartId, $shippingAddress); - } else { - $shippingAddress = $this->addressModel->addData($address); - $this->shippingAddressManagement->assign($cartId, $shippingAddress); - } + //TODO: assign cart items + $this->singleShipping->setAddress($cartId, $customerAddressId, $address); } else { - //TODO: implement multi shipping address assign flow + $this->multiShipping->setAddress($cartId, $cartItems, $customerAddressId, $address); } - return []; + //TODO: implement Cart object in the separate resolver + $shippingAddress = $this->shippingAddressManagement->get($cartId); + return [ + 'cart' => [ + 'applied_coupon' => [ + 'code' => '' + ], + 'addresses' => [[ + 'firstname' => $shippingAddress->getFirstname(), + 'lastname' => $shippingAddress->getLastname(), + 'company' => $shippingAddress->getCompany(), + 'street' => $shippingAddress->getStreet(), + 'city' => $shippingAddress->getCity(), + 'region' => [ + 'code' => $shippingAddress->getRegionCode(), + 'label' => $shippingAddress->getRegion() + ], + 'country' => [ + 'code' => $shippingAddress->getCountryId(), + 'label' => '' + ], + 'postcode' => $shippingAddress->getPostcode(), + 'telephone' => $shippingAddress->getTelephone(), + 'address_type' => 'SHIPPING', + 'selected_shipping_method' => [ + 'code' => 'test', + 'label' => 'test', + 'free_shipping' => 'test', + 'error_message' => 'test' + ], + 'available_shipping_methods' => [[ + 'code' => 'test', + 'label' => 'test', + 'free_shipping' => 'test', + 'error_message' => 'test' + ]], + 'items_weight' => [0], + 'customer_notes' => $shippingAddress->getLastname(), + 'cart_items' => [[ + 'cart_item_id' => '', + 'quantity' => 0 + ]], + 'applied_coupon' => [ + 'code' => '' + ] + ]] + ] + ]; } } From e7c2ddcfbfa9668c0a7d8c8706892740add84c5d Mon Sep 17 00:00:00 2001 From: Yurii Borysov <yurii_borysov@epam.com> Date: Thu, 4 Oct 2018 16:12:11 +0300 Subject: [PATCH 064/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix elements arrangement issue --- .../adminhtml/web/css/grouped-product.css | 13 +++++-- .../adminhtml/web/js/grouped-product-grid.js | 34 +++++++++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css b/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css index cd0aa25ae1f88..9142eaf90899e 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/css/grouped-product.css @@ -67,9 +67,8 @@ text-overflow: ellipsis; } - .position { - width:90px; + width: 100px; } .icon-rearrange-position > span { @@ -91,6 +90,11 @@ speak: none; } +.position > * { + float: left; + margin: 3px; +} + .position-widget-input { text-align: center; width: 40px; @@ -103,3 +107,8 @@ .icon-backward:before { content: '\e619'; } + +.icon-rearrange-position, .icon-rearrange-position:hover { + color: #d9d9d9; + text-decoration: none; +} diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 7c9563bb1a0ce..c1a35cb0afede 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -36,7 +36,17 @@ define([ * @param position */ shiftNextPagesPositions: function (position) { - console.log("todo: shifting positions to right on next pages related data"); + var recordData = this.recordData(); + if (~~this.currentPage() === this.pages()) { + return false; + } else { + var startIndex = ~~this.currentPage() * this.pageSize, + offset = position - startIndex + 1; + for (var index = startIndex; index < recordData.length; index++) { + recordData[index].position = index + offset; + } + this.recordData(recordData); + } }, @@ -47,30 +57,36 @@ define([ * @param event */ updateGridPosition: function (data, event) { - var inputValue = ~~event.target.value, + var inputValue = parseInt(event.target.value), recordData = this.recordData(), record, + previousValue, updatedRecord; record = this.elems().find(function (obj) { return obj.dataScope === data.parentScope - }).data(); + }); - if (inputValue === ~~record.positionCalculated) { + previousValue = this.getCalculatedPosition(record); + + if (isNaN(inputValue) || inputValue < 0 || inputValue === previousValue) { return false; } this.elems([]); - updatedRecord = this.getUpdatedRecordIndex(recordData, record.id); + updatedRecord = this.getUpdatedRecordIndex(recordData, record.data().id); if (inputValue >= this.recordData().size() - 1) { recordData[updatedRecord].position = this.getGlobalMaxPosition() + 1; } else { - recordData[updatedRecord].position = inputValue; recordData.forEach(function (value, index) { - if (~~value.id !== ~~record.id) { - recordData[index].position = (index !== inputValue) ? index : index + 1; + if (~~value.id === ~~record.data().id) { + recordData[index].position = inputValue; + } else if (inputValue > previousValue && index <= inputValue) { + recordData[index].position = index - 1; + } else if (inputValue < previousValue && index >= inputValue) { + recordData[index].position = index + 1; } }); } @@ -203,7 +219,5 @@ define([ return ~~r.position })); } - - }); }); From 929ed778dd4f6157d2ddfbc49289661559d384eb Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 4 Oct 2018 18:07:35 +0300 Subject: [PATCH 065/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Magento/Backend/Block/Media/Uploader.php | 6 +++--- .../adminhtml/templates/media/uploader.phtml | 2 +- .../view/adminhtml/web/js/media-uploader.js | 18 ++++++++++-------- .../templates/browser/content/uploader.phtml | 15 ++++++++++----- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Backend/Block/Media/Uploader.php b/app/code/Magento/Backend/Block/Media/Uploader.php index 1995d72661e2e..22e9197418e4c 100644 --- a/app/code/Magento/Backend/Block/Media/Uploader.php +++ b/app/code/Magento/Backend/Block/Media/Uploader.php @@ -130,11 +130,11 @@ public function getImageUploadMaxHeight() /** * Get image resize configuration * - * @return bool + * @return int */ - public function getIsResizeEnabled(): bool + public function getIsResizeEnabled(): int { - return $this->imageUploadConfig->isResizeEnabled(); + return (int)$this->imageUploadConfig->isResizeEnabled(); } /** diff --git a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml index edd54ae38b6a1..d6e97cf4d58e3 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml @@ -13,7 +13,7 @@ data-mage-init='{ "Magento_Backend/js/media-uploader" : { "maxFileSize": <?= /* @escapeNotVerified */ $block->getFileSizeService()->getMaxFileSize() ?>, - "maxWidth":<?= /* @escapeNotVerified */ $block->getImageUploadMaxWidth() ?> , + "maxWidth": <?= /* @escapeNotVerified */ $block->getImageUploadMaxWidth() ?>, "maxHeight": <?= /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?>, "isResizeEnabled": <?= /* @noEscape */ $block->getIsResizeEnabled() ?> } diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js index 35e9ec244df24..2a15f5262bda1 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js @@ -36,10 +36,17 @@ define([ var self = this, progressTmpl = mageTemplate('[data-template="uploader"]'), - resizeConfig = { - action: 'resize' + isResizeEnabled = this.options.isResizeEnabled, + resizeConfiguration = { + action: 'resize', + maxWidth: this.options.maxWidth, + maxHeight: this.options.maxHeight }; + if (isResizeEnabled === 0) { + resizeConfiguration = {action: 'resize'}; + } + this.element.find('input[type=file]').fileupload({ dataType: 'json', formData: { @@ -123,18 +130,13 @@ define([ stop: fileUploader.uploaderConfig.stop }); - if (typeof this.options.isResizeEnabled !== 'undefined' && this.options.isResizeEnabled === true) { - resizeConfig.maxWidth = this.options.maxWidth; - resizeConfig.maxHeight = this.options.maxHeight; - } - this.element.find('input[type=file]').fileupload('option', { process: [{ action: 'load', fileTypes: /^image\/(gif|jpeg|png)$/, maxFileSize: this.options.maxFileSize }, - resizeConfig, + resizeConfiguration, { action: 'save' }] diff --git a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml index 5f4e40667eda5..b1824f639c940 100644 --- a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml +++ b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml @@ -17,6 +17,13 @@ foreach ($filters as $media_type) { }, $media_type['files'])); } +$resizeConfig = ($block->getIsResizeEnabled()) + ? "{action: 'resize', maxWidth: " + . $block->getImageUploadMaxWidth() + . ", maxHeight: " + . $block->getImageUploadMaxHeight() + . "}" + : "{action: 'resize'}"; ?> <div id="<?= $block->getHtmlId() ?>" class="uploader"> @@ -145,11 +152,9 @@ require([ action: 'load', fileTypes: /^image\/(gif|jpeg|png)$/, maxFileSize: <?= (int) $block->getFileSizeService()->getMaxFileSize() ?> * 10 - }, { - action: 'resize', - maxWidth: <?= /* @escapeNotVerified */ $block->getImageUploadMaxWidth() ?> , - maxHeight: <?= /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?> - }, { + }, + <?= /* @noEscape*/ $resizeConfig ?>, + { action: 'save' }] }); From 3bd78c6897fdccf6deb351b4b3510e979bb673a6 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Thu, 4 Oct 2018 20:42:45 +0300 Subject: [PATCH 066/310] MAGETWO-91650: Translation not working for product alerts - Add foreign key for store table --- app/code/Magento/ProductAlert/etc/db_schema.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ProductAlert/etc/db_schema.xml b/app/code/Magento/ProductAlert/etc/db_schema.xml index 1d145482ea38f..2b3d9c0db3bc5 100644 --- a/app/code/Magento/ProductAlert/etc/db_schema.xml +++ b/app/code/Magento/ProductAlert/etc/db_schema.xml @@ -41,7 +41,7 @@ table="product_alert_price" column="website_id" referenceTable="store_website" referenceColumn="website_id" onDelete="CASCADE"/> <constraint xsi:type="foreign" name="PRODUCT_ALERT_PRICE_STORE_ID_STORE_STORE_ID" - table="product_alert_stock" column="website_id" referenceTable="store" + table="product_alert_stock" column="store_id" referenceTable="store" referenceColumn="store_id" onDelete="CASCADE"/> <index name="PRODUCT_ALERT_PRICE_CUSTOMER_ID" indexType="btree"> <column name="customer_id"/> @@ -82,7 +82,7 @@ table="product_alert_stock" column="website_id" referenceTable="store_website" referenceColumn="website_id" onDelete="CASCADE"/> <constraint xsi:type="foreign" name="PRODUCT_ALERT_STOCK_STORE_ID_STORE_STORE_ID" - table="product_alert_stock" column="website_id" referenceTable="store" + table="product_alert_stock" column="store_id" referenceTable="store" referenceColumn="store_id" onDelete="CASCADE"/> <constraint xsi:type="foreign" name="PRODUCT_ALERT_STOCK_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID" table="product_alert_stock" column="customer_id" referenceTable="customer_entity" From e140d29a3e6ec297be4e8fffdfea07bbc521b971 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Thu, 4 Oct 2018 14:57:27 -0500 Subject: [PATCH 067/310] MAGETWO-89232: Validation error appears if duplicate product twice --- .../Model/Exception/UrlAlreadyExistsException.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/UrlRewrite/Model/Exception/UrlAlreadyExistsException.php b/app/code/Magento/UrlRewrite/Model/Exception/UrlAlreadyExistsException.php index c7e83819f2569..fdfadc5737af2 100644 --- a/app/code/Magento/UrlRewrite/Model/Exception/UrlAlreadyExistsException.php +++ b/app/code/Magento/UrlRewrite/Model/Exception/UrlAlreadyExistsException.php @@ -8,10 +8,12 @@ use Magento\Framework\Phrase; /** + * Exception for already created url. + * * @api * @since 100.2.0 */ -class UrlAlreadyExistsException extends \Magento\Framework\Exception\LocalizedException +class UrlAlreadyExistsException extends \Magento\Framework\Exception\AlreadyExistsException { /** * @var array @@ -34,6 +36,8 @@ public function __construct(Phrase $phrase = null, \Exception $cause = null, $co } /** + * Returns urls. + * * @return array * @since 100.2.0 */ From 78e8059da0e05ccdacc0c5b8777dbfa10794e3ed Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Fri, 5 Oct 2018 11:47:10 +0300 Subject: [PATCH 068/310] MAGETWO-91657: Advanced pricing the bulk discounts by percentage returns error when set - Fixed PhpDoc; --- .../Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php index 1f0da62f61056..a529580e29239 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php @@ -54,7 +54,7 @@ public function modifyData(array $data) } /** - * Add tier price info to meta array. {@inheritdoc} + * Add tier price info to meta array. * * @since 101.1.0 * @param array $meta From 3a65d73ff8b2cc5d801de99f182389a52edec318 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 5 Oct 2018 11:52:38 +0300 Subject: [PATCH 069/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Magento/Catalog/etc/adminhtml/system.xml | 17 +++++++++++++++++ app/code/Magento/Catalog/etc/config.xml | 3 +++ 2 files changed, 20 insertions(+) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 71a799fd22427..26da542cde65b 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -10,6 +10,9 @@ <tab id="catalog" translate="label" sortOrder="200"> <label>Catalog</label> </tab> + <tab id="advanced" translate="label" sortOrder="999999"> + <label>Advanced</label> + </tab> <section id="catalog" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <class>separator-top</class> <label>Catalog</label> @@ -193,5 +196,19 @@ </field> </group> </section> + <section id="system" translate="label" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> + <class>separator-top</class> + <label>System</label> + <tab>advanced</tab> + <resource>Magento_Config::config_system</resource> + <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> + <label>Images Upload Configuration</label> + <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <label>Quality</label> + <validate>validate-greater-than-zero validate-number required-entry digits-range-1-100</validate> + <comment>Jpeg quality for images 1-100%.</comment> + </field> + </group> + </section> </system> </config> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index f52760aa50743..9c9054dd0ce22 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -66,6 +66,9 @@ <product_custom_options_fodler>custom_options</product_custom_options_fodler> </allowed_resources> </media_storage_configuration> + <upload_configuration> + <jpeg_quality>80</jpeg_quality> + </upload_configuration> </system> <design> <watermark> From 6209f4107673c89b134e2aacd3a23ae708f383ef Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Thu, 4 Oct 2018 13:50:15 +0300 Subject: [PATCH 070/310] MAGETWO-91784: On Payment screen up and down arrow key allow to add -ve numbers - Trim the first dash in "Credit Card Number" field --- .../credit-card-validation/credit-card-number-validator.js | 2 +- .../base/web/js/model/credit-card-validation/validator.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js b/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js index 8fb12093e36e4..785b636d5832f 100644 --- a/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js +++ b/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js @@ -36,7 +36,7 @@ define([ return resultWrapper(null, false, false); } - value = value.replace(/\-|\s/g, ''); + value = value.replace(/|\s/g, ''); if (!/^\d*$/.test(value)) { return resultWrapper(null, false, false); diff --git a/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/validator.js b/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/validator.js index c6f1bad31fc07..f9af514a6d4da 100644 --- a/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/validator.js +++ b/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/validator.js @@ -23,6 +23,12 @@ }(function ($, cvvValidator, creditCardNumberValidator, yearValidator, monthValidator, creditCardData) { 'use strict'; + $('.payment-method-content input[type="number"]').on('keyup', function() { + if ($(this).val() < 0) { + $(this).val($(this).val().replace(/^-/, '')); + } + }); + $.each({ 'validate-card-type': [ function (number, item, allowedTypes) { From a71bea80510b8db011812f7052d5f227ebbf2901 Mon Sep 17 00:00:00 2001 From: vprohorov <prohorov.vital@gmail.com> Date: Mon, 17 Sep 2018 11:47:18 +0300 Subject: [PATCH 071/310] MAGETWO-62728: My Wishlist - quantity input box issue - Adding min/max qty checks - Adding js validation --- .../Customer/Wishlist/Item/Column/Cart.php | 26 +++++++++ .../frontend/templates/item/column/cart.phtml | 3 +- .../view/frontend/web/js/add-to-wishlist.js | 56 ++++++++++--------- 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php index b043a8d4b684c..fe0683a52fe97 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php @@ -6,6 +6,8 @@ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter; + /** * Wishlist block customer item cart column * @@ -35,4 +37,28 @@ public function getProductItem() { return $this->getItem()->getProduct(); } + + /** + * Get min and max qty for wishlist form. + * + * @return array + */ + public function getMinMaxQty() + { + $stockItem = $this->stockRegistry->getStockItem( + $this->getItem()->getProduct()->getId(), + $this->getItem()->getProduct()->getStore()->getWebsiteId() + ); + + $params = []; + + $params['minAllowed'] = (float)$stockItem->getMinSaleQty(); + if ($stockItem->getMaxSaleQty()) { + $params['maxAllowed'] = (float)$stockItem->getMaxSaleQty(); + } else { + $params['maxAllowed'] = (float)StockDataFilter::MAX_QTY_VALUE; + } + + return $params; + } } diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml index e124edc6a43e1..c1cccae85971c 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml @@ -11,6 +11,7 @@ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); $product = $item->getProduct(); +$allowedQty = $block->getMinMaxQty(); ?> <?php foreach ($block->getChildNames() as $childName): ?> <?= /* @noEscape */ $block->getLayout()->renderElement($childName, false) ?> @@ -21,7 +22,7 @@ $product = $item->getProduct(); <div class="field qty"> <label class="label" for="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> - <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true,'maxlength':8}" + <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true, 'validate-item-quantity':{'minAllowed':<?= $allowedQty['minAllowed'] ?>,'maxAllowed':<?= $allowedQty['maxAllowed'] ?>}}" name="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" value="<?= /* @noEscape */ (int)($block->getAddToCartQty($item) * 1) ?>"> </div> </div> diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index 7ce934317263b..db5f77348b2c0 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -63,12 +63,6 @@ define([ isFileUploaded = false, self = this; - if (event.handleObj.selector == this.options.qtyInfo) { //eslint-disable-line eqeqeq - this._updateAddToWishlistButton({}); - event.stopPropagation(); - - return; - } $(event.handleObj.selector).each(function (index, element) { if ($(element).is('input[type=text]') || $(element).is('input[type=email]') || @@ -89,9 +83,7 @@ define([ } }); - if (isFileUploaded) { - this.bindFormSubmit(); - } + this.bindFormSubmit(isFileUploaded); this._updateAddToWishlistButton(dataToAdd); event.stopPropagation(); }, @@ -195,34 +187,44 @@ define([ /** * Bind form submit. + * + @param {boolean} isFileUploaded */ - bindFormSubmit: function () { + bindFormSubmit: function (isFileUploaded) { var self = this; $('[data-action="add-to-wishlist"]').on('click', function (event) { var element, params, form, action; - event.stopPropagation(); - event.preventDefault(); + if (!$($(self.options.qtyInfo).closest('form')).valid()) { + event.stopPropagation(); + event.preventDefault(); + return; + } - element = $('input[type=file]' + self.options.customOptionsInfo); - params = $(event.currentTarget).data('post'); - form = $(element).closest('form'); - action = params.action; + if (isFileUploaded) { - if (params.data.id) { - $('<input>', { - type: 'hidden', - name: 'id', - value: params.data.id - }).appendTo(form); - } + element = $('input[type=file]' + self.options.customOptionsInfo); + params = $(event.currentTarget).data('post'); + form = $(element).closest('form'); + action = params.action; - if (params.data.uenc) { - action += 'uenc/' + params.data.uenc; - } + if (params.data.id) { + $('<input>', { + type: 'hidden', + name: 'id', + value: params.data.id + }).appendTo(form); + } - $(form).attr('action', action).submit(); + if (params.data.uenc) { + action += 'uenc/' + params.data.uenc; + } + + $(form).attr('action', action).submit(); + event.stopPropagation(); + event.preventDefault(); + } }); } }); From 03e8408b6184d3478251f30cc69956a06752503f Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Fri, 5 Oct 2018 13:51:24 +0300 Subject: [PATCH 072/310] MAGETWO-91636: Tooltip Position on Mobile out of view range - Fix css styles for 768px --- .../web/css/source/module/checkout/_tooltip.less | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less index 273f626ec03d6..bf264a98f33b8 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less @@ -137,9 +137,12 @@ } } -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break >= @screen__m) { .field-tooltip { .field-tooltip-content { + .lib-css(right, @checkout-tooltip-content-mobile__right); + .lib-css(top, @checkout-tooltip-content-mobile__top); + left: auto; &:extend(.abs-checkout-tooltip-content-position-top-mobile all); } } From e194b139c8889587a90aca5cee470e0a40f26422 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 5 Oct 2018 14:04:50 +0300 Subject: [PATCH 073/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- app/code/Magento/Catalog/Helper/Image.php | 1 + .../Magento/Catalog/Model/Product/Image.php | 17 +++++++++++++---- .../Model/Product/Image/ParamsBuilder.php | 9 +++------ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index 758e59790d241..74153267f41d6 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -298,6 +298,7 @@ public function resize($width, $height = null) * * @param int $quality * @return $this + * @deprecated */ public function setQuality($quality) { diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index f1ae9ac62dc9b..564b817fc167e 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -24,6 +24,11 @@ */ class Image extends \Magento\Framework\Model\AbstractModel { + /** + * Config path for the jpeg image quality value + */ + const XML_PATH_JPEG_QUALITY = 'system/upload_configuration/jpeg_quality'; + /** * @var int */ @@ -38,8 +43,9 @@ class Image extends \Magento\Framework\Model\AbstractModel * Default quality value (for JPEG images only). * * @var int + * @deprecated */ - protected $_quality = 80; + protected $_quality = null; /** * @var bool @@ -289,6 +295,7 @@ public function getHeight() * * @param int $quality * @return $this + * @deprecated */ public function setQuality($quality) { @@ -303,7 +310,9 @@ public function setQuality($quality) */ public function getQuality() { - return $this->_quality; + return $this->_quality === null + ? $this->_scopeConfig->getValue(self::XML_PATH_JPEG_QUALITY) + : $this->_quality; } /** @@ -461,7 +470,7 @@ public function getImageProcessor() $this->_processor->keepTransparency($this->_keepTransparency); $this->_processor->constrainOnly($this->_constrainOnly); $this->_processor->backgroundColor($this->_backgroundColor); - $this->_processor->quality($this->_quality); + $this->_processor->quality($this->getQuality()); return $this->_processor; } @@ -843,7 +852,7 @@ private function getMiscParams() 'transparency' => $this->_keepTransparency, 'background' => $this->_backgroundColor, 'angle' => $this->_angle, - 'quality' => $this->_quality + 'quality' => $this->getQuality() ] ); } diff --git a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php index dd8d352fecebc..21fde84931fa6 100644 --- a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php +++ b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php @@ -10,17 +10,13 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\View\ConfigInterface; use Magento\Store\Model\ScopeInterface; +use Magento\Catalog\Model\Product\Image; /** * Builds parameters array used to build Image Asset */ class ParamsBuilder { - /** - * @var int - */ - private $defaultQuality = 80; - /** * @var array */ @@ -100,11 +96,12 @@ private function overwriteDefaultValues(array $imageArguments): array $transparency = $imageArguments['transparency'] ?? $this->defaultKeepTransparency; $background = $imageArguments['background'] ?? $this->defaultBackground; $angle = $imageArguments['angle'] ?? $this->defaultAngle; + $quality = (int) $this->scopeConfig->getValue(Image::XML_PATH_JPEG_QUALITY); return [ 'background' => (array) $background, 'angle' => $angle, - 'quality' => $this->defaultQuality, + 'quality' => $quality, 'keep_aspect_ratio' => (bool) $aspectRatio, 'keep_frame' => (bool) $frame, 'keep_transparency' => (bool) $transparency, From c8a059dc87f1ce4c478d052ba1795e6d92f2cceb Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Fri, 5 Oct 2018 16:23:53 +0300 Subject: [PATCH 074/310] MAGETWO-91496: Instantiating WYSIWYG in DynamicRows - Add wysiwyg suffix to html and js --- .../view/base/web/js/form/element/wysiwyg.js | 20 +++++++++++++++---- .../Framework/Data/Form/Element/Editor.php | 8 ++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js index 6507da5e1a933..0c11ceeb03dae 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js @@ -20,6 +20,7 @@ define([ return Abstract.extend({ defaults: { elementSelector: 'textarea', + suffixRegExpPattern: '\\${ \\$.wysiwygUniqueSuffix }', value: '', $wysiwygEditorButton: '', links: { @@ -61,12 +62,23 @@ define([ return this; }, + /** @inheritdoc */ + initConfig: function (config) { + var pattern = config.suffixRegExpPattern || this.constructor.defaults.suffixRegExpPattern; + + config.content = config.content.replace(new RegExp(pattern, 'g'), this.getUniqueSuffix(config)); + this._super(); + + return this; + }, + /** - * @inheritdoc + * Build unique id based on name, underscore separated. + * + * @param {Object} config */ - destroy: function () { - this._super(); - wysiwyg.removeEvents(this.wysiwygId); + getUniqueSuffix: function (config) { + return config.name.replace(/(\.|-)/g, '_'); }, /** diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php index c438edf3aa9ac..dee0b6c842f5f 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php @@ -542,4 +542,12 @@ protected function getInlineJs($jsSetupObject, $forceLoad) </script>'; return $jsString; } + + /** + * @inheritdoc + */ + public function getHtmlId() + { + return parent::getHtmlId() . '${ $.wysiwygUniqueSuffix }'; + } } From 87cfb718edcde6b21375e398dbc8fd35cb84dcbe Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sat, 6 Oct 2018 12:40:15 +0200 Subject: [PATCH 075/310] Working concept for setting shipping address for a shopping cart --- .../SetShippingMethodsOnCart.php | 81 +++++++++++++------ .../Magento/QuoteGraphQl/etc/schema.graphqls | 8 +- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index f2e1be3bb0508..c2d14668a2e70 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -7,7 +7,8 @@ namespace Magento\QuoteGraphQl\Model\Resolver\ShippingMethod; -use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Checkout\Api\ShippingInformationManagementInterface; +use Magento\Checkout\Model\ShippingInformation; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; @@ -19,8 +20,10 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Stdlib\ArrayManager; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; -use Magento\Quote\Model\ShippingMethodManagementInterface; use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; +use Magento\Quote\Model\Quote\AddressFactory as QuoteAddressFactory; +use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; +use Magento\Checkout\Model\ShippingInformationFactory; /** * Class SetShippingMethodsOnCart @@ -29,41 +32,64 @@ */ class SetShippingMethodsOnCart implements ResolverInterface { + /** + * @var ShippingInformationFactory + */ + private $shippingInformationFactory; + + /** + * @var QuoteAddressFactory + */ + private $quoteAddressFactory; + + /** + * @var QuoteAddressResource + */ + private $quoteAddressResource; + /** * @var MaskedQuoteIdToQuoteIdInterface */ private $maskedQuoteIdToQuoteId; + /** * @var ArrayManager */ private $arrayManager; /** - * @var ShippingMethodManagementInterface + * @var IsCartMutationAllowedForCurrentUser */ - private $shippingMethodManagement; + private $isCartMutationAllowedForCurrentUser; /** - * @var IsCartMutationAllowedForCurrentUser + * @var ShippingInformationManagementInterface */ - private $isCartMutationAllowedForCurrentUser; + private $shippingInformationManagement; /** * SetShippingMethodsOnCart constructor. * @param ArrayManager $arrayManager * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId - * @param ShippingMethodManagementInterface $shippingMethodManagement + * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser */ public function __construct( ArrayManager $arrayManager, MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - ShippingMethodManagementInterface $shippingMethodManagement, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser, + ShippingInformationManagementInterface $shippingInformationManagement, + QuoteAddressFactory $quoteAddressFacrory, + QuoteAddressResource $quoteAddressResource, + ShippingInformationFactory $shippingInformationFactory ) { $this->arrayManager = $arrayManager; $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->shippingMethodManagement = $shippingMethodManagement; $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; + $this->shippingInformationManagement = $shippingInformationManagement; + + $this->quoteAddressResource = $quoteAddressResource; + $this->quoteAddressFactory = $quoteAddressFacrory; + $this->shippingInformationFactory = $shippingInformationFactory; } /** @@ -77,17 +103,18 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!$maskedCartId) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } - if (!$shippingMethods) { throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); } $shippingMethod = reset($shippingMethods); // TODO: provide implementation for multishipping + if (!$shippingMethod['cart_address_id']) { + throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); + } if (!$shippingMethod['shipping_carrier_code']) { // FIXME: check the E_WARNING here throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - if (!$shippingMethod['shipping_method_code']) { // FIXME: check the E_WARNING here throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); } @@ -109,22 +136,28 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value ); } + $quoteAddress = $this->quoteAddressFactory->create(); + $this->quoteAddressResource->load($quoteAddress, $shippingMethod['cart_address_id']); + + /** @var ShippingInformation $shippingInformation */ + $shippingInformation = $this->shippingInformationFactory->create(); + + /* If the address is not a shipping address (but billing) the system will find the proper shipping address for + the selected cart and set the information there (actual for single shipping address) */ + $shippingInformation->setShippingAddress($quoteAddress); + $shippingInformation->setShippingCarrierCode($shippingMethod['shipping_carrier_code']); + $shippingInformation->setShippingMethodCode($shippingMethod['shipping_method_code']); + try { - $this->shippingMethodManagement->set( - $cartId, - $shippingMethods['shipping_carrier_code'], - $shippingMethods['shipping_method_code'] - ); - } catch (InputException $exception) { - throw new GraphQlInputException(__($exception->getMessage())); - } catch (CouldNotSaveException $exception) { - throw new GraphQlInputException(__($exception->getMessage())); - } catch (StateException $exception) { - throw new GraphQlInputException(__($exception->getMessage())); + $this->shippingInformationManagement->saveAddressInformation($cartId, $shippingInformation); } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException(__($exception->getMessage())); + } catch (StateException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); + } catch (InputException $exception) { + throw new GraphQlInputException(__($exception->getMessage())); } return 'Success!'; // TODO we should return cart here } -} \ No newline at end of file +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 45f98daeaed06..730a54377b370 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -6,9 +6,9 @@ type Query { } type Mutation { - createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") - applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") - removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") + createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") + applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart") + removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingMethod\\SetShippingMethodsOnCart") @@ -53,7 +53,7 @@ input SetShippingMethodsOnCartInput { } input ShippingMethodForAddressInput { - cart_address_id: int + cart_address_id: Int! shipping_carrier_code: String! shipping_method_code: String! } From 5f1b4014093e062169dd9597752eac1c970898e2 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 8 Oct 2018 14:10:04 +0300 Subject: [PATCH 076/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Block/DataProviders/ImageUploadConfig.php | 37 +++++++++++++++++++ .../Magento/Backend/Block/Media/Uploader.php | 10 ----- .../adminhtml/templates/media/uploader.phtml | 2 +- .../Product/Helper/Form/Gallery/Content.php | 20 ++++++++-- .../layout/cms_wysiwyg_images_index.xml | 6 ++- .../templates/browser/content/uploader.phtml | 6 +-- 6 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php diff --git a/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php b/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php new file mode 100644 index 0000000000000..1ba3daf35d2cf --- /dev/null +++ b/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Block\DataProviders; + +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Backend\Model\Image\ImageUploadConfigInterface; + +/** + * Provides additional data for image uploader + */ +class ImageUploadConfig implements ArgumentInterface +{ + /** + * @var ImageUploadConfigInterface + */ + private $imageUploadConfig; + + public function __construct(ImageUploadConfigInterface $imageUploadConfig) + { + $this->imageUploadConfig = $imageUploadConfig; + } + + /** + * Get image resize configuration + * + * @return int + */ + public function getIsResizeEnabled(): int + { + return (int)$this->imageUploadConfig->isResizeEnabled(); + } +} diff --git a/app/code/Magento/Backend/Block/Media/Uploader.php b/app/code/Magento/Backend/Block/Media/Uploader.php index 22e9197418e4c..c332c91b54b27 100644 --- a/app/code/Magento/Backend/Block/Media/Uploader.php +++ b/app/code/Magento/Backend/Block/Media/Uploader.php @@ -127,16 +127,6 @@ public function getImageUploadMaxHeight() return $this->imageUploadConfig->getMaxHeight(); } - /** - * Get image resize configuration - * - * @return int - */ - public function getIsResizeEnabled(): int - { - return (int)$this->imageUploadConfig->isResizeEnabled(); - } - /** * Prepares layout and set element renderer * diff --git a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml index d6e97cf4d58e3..4d9ba6a8c4bad 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/media/uploader.phtml @@ -15,7 +15,7 @@ "maxFileSize": <?= /* @escapeNotVerified */ $block->getFileSizeService()->getMaxFileSize() ?>, "maxWidth": <?= /* @escapeNotVerified */ $block->getImageUploadMaxWidth() ?>, "maxHeight": <?= /* @escapeNotVerified */ $block->getImageUploadMaxHeight() ?>, - "isResizeEnabled": <?= /* @noEscape */ $block->getIsResizeEnabled() ?> + "isResizeEnabled": <?= /* @noEscape */ $block->getImageUploadConfigData()->getIsResizeEnabled() ?> } }' > diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php index e1208e25cc7c8..d9e343fd2b3f0 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php @@ -13,11 +13,12 @@ */ namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery; +use Magento\Framework\App\ObjectManager; use Magento\Backend\Block\Media\Uploader; use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\FileSystemException; - +use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider; /** * Block for gallery content. */ @@ -43,21 +44,30 @@ class Content extends \Magento\Backend\Block\Widget */ private $imageHelper; + /** + * @var ImageUploadConfigDataProvider + */ + private $imageUploadConfigDataProvider; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder * @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig * @param array $data + * @param ImageUploadConfigDataProvider $imageUploadConfigDataProvider */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\Json\EncoderInterface $jsonEncoder, \Magento\Catalog\Model\Product\Media\Config $mediaConfig, - array $data = [] + array $data = [], + ImageUploadConfigDataProvider $imageUploadConfigDataProvider = null ) { $this->_jsonEncoder = $jsonEncoder; $this->_mediaConfig = $mediaConfig; parent::__construct($context, $data); + $this->imageUploadConfigDataProvider = $imageUploadConfigDataProvider + ?: ObjectManager::getInstance()->get(ImageUploadConfigDataProvider::class); } /** @@ -67,7 +77,11 @@ public function __construct( */ protected function _prepareLayout() { - $this->addChild('uploader', \Magento\Backend\Block\Media\Uploader::class); + $this->addChild( + 'uploader', + \Magento\Backend\Block\Media\Uploader::class, + ['image_upload_config_data' => $this->imageUploadConfigDataProvider] + ); $this->getUploader()->getConfig()->setUrl( $this->_urlBuilder->addSessionParam()->getUrl('catalog/product_gallery/upload') diff --git a/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml b/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml index 1bc8828ef6c8e..6703b6c277123 100644 --- a/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml +++ b/app/code/Magento/Cms/view/adminhtml/layout/cms_wysiwyg_images_index.xml @@ -9,7 +9,11 @@ <container name="root"> <block class="Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Content" name="wysiwyg_images.content" template="Magento_Cms::browser/content.phtml"> <block class="Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Tree" name="wysiwyg_images.tree" template="Magento_Cms::browser/tree.phtml"/> - <block class="Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Content\Uploader" name="wysiwyg_images.uploader" template="Magento_Cms::browser/content/uploader.phtml"/> + <block class="Magento\Cms\Block\Adminhtml\Wysiwyg\Images\Content\Uploader" name="wysiwyg_images.uploader" template="Magento_Cms::browser/content/uploader.phtml"> + <arguments> + <argument name="image_upload_config_data" xsi:type="object">Magento\Backend\Block\DataProviders\ImageUploadConfig</argument> + </arguments> + </block> </block> </container> </layout> diff --git a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml index b1824f639c940..c6810f774238c 100644 --- a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml +++ b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml @@ -17,11 +17,11 @@ foreach ($filters as $media_type) { }, $media_type['files'])); } -$resizeConfig = ($block->getIsResizeEnabled()) +$resizeConfig = ($block->getImageUploadConfigData()->getIsResizeEnabled()) ? "{action: 'resize', maxWidth: " - . $block->getImageUploadMaxWidth() + . $block->escapeHtml($block->getImageUploadMaxWidth()) . ", maxHeight: " - . $block->getImageUploadMaxHeight() + . $block->escapeHtml($block->getImageUploadMaxHeight()) . "}" : "{action: 'resize'}"; ?> From eb2dd92bfab57e45a50b60f7d29adb80bff27c38 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Mon, 8 Oct 2018 19:19:25 +0300 Subject: [PATCH 077/310] MAGETWO-91651: Navigation Menu problem on Mobile theme - Added scroll to top menu items --- lib/web/mage/menu.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/menu.js b/lib/web/mage/menu.js index b8000640506f1..7096c4ecb7d6d 100644 --- a/lib/web/mage/menu.js +++ b/lib/web/mage/menu.js @@ -439,6 +439,7 @@ define([ event.preventDefault(); target = $(event.target).closest('.ui-menu-item'); + target.get(0).scrollIntoView(); if (!target.hasClass('level-top') || !target.has('.ui-menu').length) { window.location.href = target.find('> a').attr('href'); From e819fc98e142b3f93a45cad3ae6ee061209265a5 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 9 Oct 2018 13:00:35 +0300 Subject: [PATCH 078/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Test/AdminSimpleProductImagesTest.xml | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml index 69931395a35d5..b10c8e5ad54a0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml @@ -87,10 +87,9 @@ <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorMedium"/> <!-- 10~ mb is allowed --> - <!-- Skipped MAGETWO-94092 --> - <!--<attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge"/> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge"/> <waitForPageLoad stepKey="waitForUploadLarge"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge"/>--> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge"/> <!-- *.gif is allowed --> <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="gif.gif" stepKey="attachGif"/> @@ -115,8 +114,7 @@ <!-- See all of the images that we uploaded --> <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('small')}}" stepKey="seeSmall"/> <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('medium')}}" stepKey="seeMedium"/> - <!-- Skipped MAGETWO-94092 --> - <!--<seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge"/>--> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge"/> <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('gif')}}" stepKey="seeGif"/> <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('jpg')}}" stepKey="seeJpg"/> <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('png')}}" stepKey="seePng"/> @@ -136,9 +134,9 @@ <!-- Upload an image --> <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="medium.jpg" stepKey="attachMedium2"/> - <waitForPageLoad stepKey="waitForUploadMedium2"/> - <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorMedium2"/> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="large.jpg" stepKey="attachLarge2"/> + <waitForPageLoad stepKey="waitForUploadLarge2"/> + <dontSeeElement selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="dontSeeErrorLarge2"/> <!-- Set url key --> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection2"/> @@ -154,16 +152,16 @@ <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku3"> <argument name="product" value="$$secondProduct$$"/> </actionGroup> - <seeElement selector="img.admin__control-thumbnail[src*='/medium']" stepKey="seeImgInGrid"/> + <seeElement selector="img.admin__control-thumbnail[src*='/large']" stepKey="seeImgInGrid"/> <!-- Go to the category page and see the uploaded image --> <amOnPage url="$$category.name$$.html" stepKey="goToCategoryPage2"/> - <seeElement selector=".products-grid img[src*='/medium']" stepKey="seeUploadedImg"/> + <seeElement selector=".products-grid img[src*='/large']" stepKey="seeUploadedImg"/> <!-- Go to the product page and see the uploaded image --> <amOnPage url="$$secondProduct.name$$.html" stepKey="goToStorefront2"/> <waitForPageLoad stepKey="waitForStorefront2"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('medium')}}" stepKey="seeMedium2"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('Large')}}" stepKey="seeLarge2"/> </test> <test name="AdminSimpleProductRemoveImagesTest"> From 2850e6ee21634c9251125cfca82fb83b600e29eb Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 9 Oct 2018 16:40:16 +0300 Subject: [PATCH 079/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Block/DataProviders/ImageUploadConfig.php | 3 + .../Magento/Backend/Block/Media/Uploader.php | 11 +++- .../Product/Helper/Form/Gallery/Content.php | 1 + app/code/Magento/Catalog/Helper/Image.php | 6 ++ .../Magento/Catalog/Model/Product/Image.php | 58 +++++++++++++++++-- .../Model/Product/Image/ParamsBuilder.php | 7 +++ 6 files changed, 81 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php b/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php index 1ba3daf35d2cf..6ec40cfa0b2d4 100644 --- a/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php +++ b/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php @@ -20,6 +20,9 @@ class ImageUploadConfig implements ArgumentInterface */ private $imageUploadConfig; + /** + * @param ImageUploadConfigInterface $imageUploadConfig + */ public function __construct(ImageUploadConfigInterface $imageUploadConfig) { $this->imageUploadConfig = $imageUploadConfig; diff --git a/app/code/Magento/Backend/Block/Media/Uploader.php b/app/code/Magento/Backend/Block/Media/Uploader.php index c332c91b54b27..941d6f37737fe 100644 --- a/app/code/Magento/Backend/Block/Media/Uploader.php +++ b/app/code/Magento/Backend/Block/Media/Uploader.php @@ -44,6 +44,13 @@ class Uploader extends \Magento\Backend\Block\Widget */ private $imageUploadConfig; + /** + * @var UploadConfigInterface + * @deprecated + * @see \Magento\Backend\Model\Image\ImageUploadConfigInterface + */ + private $imageConfig; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\File\Size $fileSize @@ -58,10 +65,12 @@ public function __construct( array $data = [], Json $jsonEncoder = null, UploadConfigInterface $imageConfig = null, - ImageUploadConfigInterface $imageUploadConfig = null + ImageUploadConfigInterface $imageUploadConfig = null ) { $this->_fileSizeService = $fileSize; $this->jsonEncoder = $jsonEncoder ?: ObjectManager::getInstance()->get(Json::class); + $this->imageConfig = $imageConfig + ?: ObjectManager::getInstance()->get(UploadConfigInterface::class); $this->imageUploadConfig = $imageUploadConfig ?: ObjectManager::getInstance()->get(ImageUploadConfigInterface::class); parent::__construct($context, $data); diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php index d9e343fd2b3f0..063503682f4db 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php @@ -19,6 +19,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\FileSystemException; use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider; + /** * Block for gallery content. */ diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index 74153267f41d6..4a12bf7093943 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -407,6 +407,7 @@ public function rotate($angle) /** * Add watermark to image + * * size param in format 100x200 * * @param string $fileName @@ -534,6 +535,8 @@ public function getUrl() } /** + * Save changes + * * @return $this */ public function save() @@ -554,6 +557,8 @@ public function getResizedImageInfo() } /** + * Getter for placeholder url + * * @param null|string $placeholder * @return string */ @@ -656,6 +661,7 @@ protected function getWatermarkPosition() /** * Set watermark size + * * param size in format 100x200 * * @param string $size diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index 564b817fc167e..8ad4e89d7b480 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -15,6 +15,8 @@ use Magento\Catalog\Model\Product\Image\ParamsBuilder; /** + * Image operations + * * @method string getFile() * @method string getLabel() * @method string getPosition() @@ -209,16 +211,16 @@ class Image extends \Magento\Framework\Model\AbstractModel * @param \Magento\Framework\Image\Factory $imageFactory * @param \Magento\Framework\View\Asset\Repository $assetRepo * @param \Magento\Framework\View\FileSystem $viewFileSystem + * @param ImageFactory $viewAssetImageFactory + * @param PlaceholderFactory $viewAssetPlaceholderFactory * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data - * @param ImageFactory|null $viewAssetImageFactory - * @param PlaceholderFactory|null $viewAssetPlaceholderFactory - * @param SerializerInterface|null $serializer + * @param SerializerInterface $serializer * @param ParamsBuilder $paramsBuilder * @SuppressWarnings(PHPMD.ExcessiveParameterList) - * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @SuppressWarnings(PHPMD.UnusedLocalVariable)1 */ public function __construct( \Magento\Framework\Model\Context $context, @@ -255,6 +257,8 @@ public function __construct( } /** + * Set image width property + * * @param int $width * @return $this */ @@ -265,6 +269,8 @@ public function setWidth($width) } /** + * Get image width property + * * @return int */ public function getWidth() @@ -273,6 +279,8 @@ public function getWidth() } /** + * Set image height property + * * @param int $height * @return $this */ @@ -283,6 +291,8 @@ public function setHeight($height) } /** + * Get image height property + * * @return int */ public function getHeight() @@ -316,6 +326,8 @@ public function getQuality() } /** + * Set _keepAspectRatio property + * * @param bool $keep * @return $this */ @@ -326,6 +338,8 @@ public function setKeepAspectRatio($keep) } /** + * Set _keepFrame property + * * @param bool $keep * @return $this */ @@ -336,6 +350,8 @@ public function setKeepFrame($keep) } /** + * Set _keepTransparency + * * @param bool $keep * @return $this */ @@ -346,6 +362,8 @@ public function setKeepTransparency($keep) } /** + * Set _constrainOnly + * * @param bool $flag * @return $this */ @@ -356,6 +374,8 @@ public function setConstrainOnly($flag) } /** + * Set background color + * * @param int[] $rgbArray * @return $this */ @@ -366,6 +386,8 @@ public function setBackgroundColor(array $rgbArray) } /** + * Set size + * * @param string $size * @return $this */ @@ -420,6 +442,8 @@ public function setBaseFile($file) } /** + * Get base filename + * * @return string */ public function getBaseFile() @@ -428,6 +452,8 @@ public function getBaseFile() } /** + * Get new file + * * @deprecated 101.1.0 * @return bool|string */ @@ -447,6 +473,8 @@ public function isBaseFilePlaceholder() } /** + * Set image processor + * * @param MagentoImage $processor * @return $this */ @@ -457,6 +485,8 @@ public function setImageProcessor($processor) } /** + * Get image processor + * * @return MagentoImage */ public function getImageProcessor() @@ -475,6 +505,8 @@ public function getImageProcessor() } /** + * Resize image + * * @see \Magento\Framework\Image\Adapter\AbstractAdapter * @return $this */ @@ -488,6 +520,8 @@ public function resize() } /** + * Rotate image + * * @param int $angle * @return $this */ @@ -514,6 +548,7 @@ public function setAngle($angle) /** * Add watermark to image + * * size param in format 100x200 * * @param string $file @@ -573,6 +608,8 @@ public function setWatermark( } /** + * Save file + * * @return $this */ public function saveFile() @@ -587,6 +624,8 @@ public function saveFile() } /** + * Get url + * * @return string */ public function getUrl() @@ -595,6 +634,8 @@ public function getUrl() } /** + * Set destination subdir + * * @param string $dir * @return $this */ @@ -605,6 +646,8 @@ public function setDestinationSubdir($dir) } /** + * Get destination subdir + * * @return string */ public function getDestinationSubdir() @@ -613,6 +656,8 @@ public function getDestinationSubdir() } /** + * Check is image cached + * * @return bool */ public function isCached() @@ -780,7 +825,10 @@ public function getWatermarkHeight() } /** + * Clear cache + * * @return void + * @throws \Magento\Framework\Exception\FileSystemException */ public function clearCache() { @@ -793,6 +841,7 @@ public function clearCache() /** * First check this file on FS + * * If it doesn't exist - try to download it from DB * * @param string $filename @@ -811,6 +860,7 @@ protected function _fileExists($filename) /** * Return resized product image information + * * @return array * @throws NotLoadInfoImageException */ diff --git a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php index 21fde84931fa6..f6be7f7392b5e 100644 --- a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php +++ b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php @@ -65,6 +65,8 @@ public function __construct( } /** + * Build image params + * * @param array $imageArguments * @return array * @SuppressWarnings(PHPMD.NPathComplexity) @@ -85,6 +87,8 @@ public function build(array $imageArguments): array } /** + * Overwrite default values + * * @param array $imageArguments * @return array */ @@ -110,6 +114,8 @@ private function overwriteDefaultValues(array $imageArguments): array } /** + * Get watermark + * * @param string $type * @return array */ @@ -150,6 +156,7 @@ private function getWatermark(string $type): array /** * Get frame from product_image_white_borders + * * @return bool */ private function hasDefaultFrame(): bool From 38abe3d4558df5ffaa91e3ba5ed7fa73c5172a42 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 9 Oct 2018 16:50:25 +0300 Subject: [PATCH 080/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- app/code/Magento/Catalog/Helper/Image.php | 4 ++-- app/code/Magento/Catalog/Model/Product/Image.php | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index 4a12bf7093943..170f1209ad9e6 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -408,7 +408,7 @@ public function rotate($angle) /** * Add watermark to image * - * size param in format 100x200 + * Size param in format 100x200 * * @param string $fileName * @param string $position @@ -662,7 +662,7 @@ protected function getWatermarkPosition() /** * Set watermark size * - * param size in format 100x200 + * Param size in format 100x200 * * @param string $size * @return $this diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index 8ad4e89d7b480..adde1263e1bf9 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -549,7 +549,7 @@ public function setAngle($angle) /** * Add watermark to image * - * size param in format 100x200 + * Size param in format 100x200 * * @param string $file * @param string $position @@ -690,7 +690,8 @@ public function getWatermarkFile() /** * Get relative watermark file path - * or false if file not found + * + * Return false if file not found * * @return string | bool */ From 6e03f0ff8d21259526d6948de51ed29266241d61 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 9 Oct 2018 17:00:58 +0300 Subject: [PATCH 081/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../view/adminhtml/templates/browser/content/uploader.phtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml index c6810f774238c..54f42bb4ecc1f 100644 --- a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml +++ b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml @@ -19,9 +19,9 @@ foreach ($filters as $media_type) { $resizeConfig = ($block->getImageUploadConfigData()->getIsResizeEnabled()) ? "{action: 'resize', maxWidth: " - . $block->escapeHtml($block->getImageUploadMaxWidth()) + . $block->getImageUploadMaxWidth() . ", maxHeight: " - . $block->escapeHtml($block->getImageUploadMaxHeight()) + . $block->getImageUploadMaxHeight() . "}" : "{action: 'resize'}"; ?> @@ -153,7 +153,7 @@ require([ fileTypes: /^image\/(gif|jpeg|png)$/, maxFileSize: <?= (int) $block->getFileSizeService()->getMaxFileSize() ?> * 10 }, - <?= /* @noEscape*/ $resizeConfig ?>, + <?= $block->escapeHtml($resizeConfig) ?>, { action: 'save' }] From 65670eacceaa1d812ff251c2304540859e9b4142 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 9 Oct 2018 18:18:57 +0300 Subject: [PATCH 082/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Magento/Backend/view/adminhtml/web/js/media-uploader.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js index 2a15f5262bda1..6789dfa3f2b8e 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js @@ -44,7 +44,9 @@ define([ }; if (isResizeEnabled === 0) { - resizeConfiguration = {action: 'resize'}; + resizeConfiguration = { + action: 'resize' + }; } this.element.find('input[type=file]').fileupload({ From 38dadb1f19dfa304083c019ba619ccda4bb44426 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 10 Oct 2018 16:11:33 +0300 Subject: [PATCH 083/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Magento/Backend/view/adminhtml/web/js/media-uploader.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js index 6789dfa3f2b8e..e1d5a0a9debe5 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js @@ -135,8 +135,7 @@ define([ this.element.find('input[type=file]').fileupload('option', { process: [{ action: 'load', - fileTypes: /^image\/(gif|jpeg|png)$/, - maxFileSize: this.options.maxFileSize + fileTypes: /^image\/(gif|jpeg|png)$/ }, resizeConfiguration, { From 9c19879740e5493eebf6b8e308cc57a26ac07eb1 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Wed, 10 Oct 2018 17:35:40 +0300 Subject: [PATCH 084/310] MAGETWO-91725: Reward Points Balance Update Emails are not being sent when balance change initiated by store front - Bug fix - Integration test fix --- .../Model/Metadata/CustomerMetadata.php | 27 +++++++++---------- app/code/Magento/Customer/etc/di.xml | 7 +++++ .../Customer/Model/CustomerMetadataTest.php | 19 ++++++++----- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Customer/Model/Metadata/CustomerMetadata.php b/app/code/Magento/Customer/Model/Metadata/CustomerMetadata.php index 7e8f0b9236fe9..38f3fbcbdbded 100644 --- a/app/code/Magento/Customer/Model/Metadata/CustomerMetadata.php +++ b/app/code/Magento/Customer/Model/Metadata/CustomerMetadata.php @@ -33,16 +33,26 @@ class CustomerMetadata implements CustomerMetadataInterface */ private $attributeMetadataDataProvider; + /** + * List of system attributes which should be available to the clients. + * + * @var string[] + */ + private $systemAttributes; + /** * @param AttributeMetadataConverter $attributeMetadataConverter * @param AttributeMetadataDataProvider $attributeMetadataDataProvider + * @param string[] $systemAttributes */ public function __construct( AttributeMetadataConverter $attributeMetadataConverter, - AttributeMetadataDataProvider $attributeMetadataDataProvider + AttributeMetadataDataProvider $attributeMetadataDataProvider, + array $systemAttributes = [] ) { $this->attributeMetadataConverter = $attributeMetadataConverter; $this->attributeMetadataDataProvider = $attributeMetadataDataProvider; + $this->systemAttributes = $systemAttributes; } /** @@ -136,7 +146,7 @@ public function getCustomAttributesMetadata($dataObjectClassName = self::DATA_IN if (!$isDataObjectMethod && (!$attributeMetadata->isSystem() - || in_array($attributeCode, $this->getAllowedSystemAttributesList()) + || in_array($attributeCode, $this->systemAttributes) ) ) { $customAttributes[] = $attributeMetadata; @@ -144,17 +154,4 @@ public function getCustomAttributesMetadata($dataObjectClassName = self::DATA_IN } return $customAttributes; } - - /** - * Get list of system attributes which should be available to the clients - * - * @return array - */ - private function getAllowedSystemAttributesList() - { - return [ - 'disable_auto_group_change', - 'reward_update_notification' - ]; - } } diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index 6e8c3dc68ed28..209f4794c6fcf 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -127,6 +127,13 @@ <argument name="groupManagement" xsi:type="object">Magento\Customer\Api\GroupManagementInterface\Proxy</argument> </arguments> </type> + <type name="Magento\Customer\Model\Metadata\CustomerMetadata"> + <arguments> + <argument name="systemAttributes" xsi:type="array"> + <item name="disable_auto_group_change" xsi:type="string">disable_auto_group_change</item> + </argument> + </arguments> + </type> <virtualType name="SectionInvalidationConfigReader" type="Magento\Framework\Config\Reader\Filesystem"> <arguments> <argument name="idAttributes" xsi:type="array"> diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php index 336b438661705..794fce17480fa 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php @@ -51,16 +51,23 @@ protected function setUp() public function testGetCustomAttributesMetadata() { - $customAttributesMetadata = $this->service->getCustomAttributesMetadata(); - $this->assertCount(0, $customAttributesMetadata, "Invalid number of attributes returned."); + $customAttributesMetadataQty = count($this->service->getCustomAttributesMetadata()) ; // Verify the consistency of getCustomerAttributeMetadata() function from the 2nd call of the same service - $customAttributesMetadata1 = $this->service->getCustomAttributesMetadata(); - $this->assertCount(0, $customAttributesMetadata1, "Invalid number of attributes returned."); + $customAttributesMetadata1Qty = count($this->service->getCustomAttributesMetadata()); + $this->assertEquals( + $customAttributesMetadataQty, + $customAttributesMetadata1Qty, + "Invalid number of attributes returned." + ); // Verify the consistency of getCustomAttributesMetadata() function from the 2nd service - $customAttributesMetadata2 = $this->serviceTwo->getCustomAttributesMetadata(); - $this->assertCount(0, $customAttributesMetadata2, "Invalid number of attributes returned."); + $customAttributesMetadata2Qty = count($this->serviceTwo->getCustomAttributesMetadata()); + $this->assertEquals( + $customAttributesMetadataQty, + $customAttributesMetadata2Qty, + "Invalid number of attributes returned." + ); } public function testGetNestedOptionsCustomerAttributesMetadata() From d2e66ba244ce394c8eaa2abd2992a9d1d8ded558 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 10 Oct 2018 12:19:58 -0500 Subject: [PATCH 085/310] MAGETWO-95588: Swatches on category page does not work - Added configuration for listing pages - Added tests --- .../AdminCreateProductAttributeSection.xml | 1 + .../Mftf/Test/AdminCreateImageSwatchTest.xml | 28 +++++++++++++++++++ .../templates/product/listing/renderer.phtml | 3 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 17778c76da9b9..b3ecf5bcedba9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -72,5 +72,6 @@ <element name="Scope" type="select" selector="#is_global"/> <element name="AddToColumnOptions" type="select" selector="#is_used_in_grid"/> <element name="UseInFilterOptions" type="select" selector="#is_filterable_in_grid"/> + <element name="UseInProductListing" type="select" selector="#used_in_product_listing"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml index a763bda2e494f..dd497cca2c2de 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml @@ -19,6 +19,7 @@ <group value="Swatches"/> </annotations> <before> + <createData entity="ApiCategory" stepKey="createCategory"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> @@ -67,6 +68,11 @@ <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> + <scrollToTopOfPage stepKey="scrollToTabs"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInProductListing}}" stepKey="waitForTabSwitch"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.UseInProductListing}}" userInput="Yes" stepKey="useInProductListing"/> + <!-- Save the new product attribute --> <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSaveAndEdit1"/> <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccess"/> @@ -97,6 +103,7 @@ <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> <!-- Create configurations based off the Image Swatch we created earlier --> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> @@ -134,5 +141,26 @@ <expectedResult type="string">adobe-base</expectedResult> <actualResult type="string">{$grabSwatch6}</actualResult> </assertContains> + + <!-- Go to the product listing page and see text swatch options --> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPageStorefront"/> + <waitForPageLoad stepKey="waitForProductListingPage"/> + + <!-- Verify the storefront --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption('1')}}" userInput="style" stepKey="grabSwatch7"/> + <assertContains stepKey="assertSwatch7"> + <expectedResult type="string">adobe-thumb</expectedResult> + <actualResult type="string">{$grabSwatch7}</actualResult> + </assertContains> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption('2')}}" userInput="style" stepKey="grabSwatch8"/> + <assertContains stepKey="assertSwatch8"> + <expectedResult type="string">adobe-small</expectedResult> + <actualResult type="string">{$grabSwatch8}</actualResult> + </assertContains> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption('3')}}" userInput="style" stepKey="grabSwatch9"/> + <assertContains stepKey="assertSwatch9"> + <expectedResult type="string">adobe-base</expectedResult> + <actualResult type="string">{$grabSwatch9}</actualResult> + </assertContains> </test> </tests> diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml index e65345b38d9b2..410d4046ae17f 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml @@ -21,7 +21,8 @@ $productId = $block->getProduct()->getId(); "numberToShow": <?= /* @escapeNotVerified */ $block->getNumberSwatchesPerProduct(); ?>, "jsonConfig": <?= /* @escapeNotVerified */ $block->getJsonConfig(); ?>, "jsonSwatchConfig": <?= /* @escapeNotVerified */ $block->getJsonSwatchConfig(); ?>, - "mediaCallback": "<?= /* @escapeNotVerified */ $block->getMediaCallback() ?>" + "mediaCallback": "<?= /* @escapeNotVerified */ $block->getMediaCallback() ?>", + "jsonSwatchImageSizeConfig": <?php /* @noEscape */ echo $block->getJsonSwatchSizeConfig() ?> } } } From 43b3cae9c610984b29c976394ca0ab292d61d0a6 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 10 Oct 2018 13:47:31 -0500 Subject: [PATCH 086/310] MAGETWO-95588: Swatches on category page does not work - Fixed static issue --- .../view/frontend/templates/product/listing/renderer.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml index 410d4046ae17f..c30c96fc890f7 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml @@ -22,7 +22,7 @@ $productId = $block->getProduct()->getId(); "jsonConfig": <?= /* @escapeNotVerified */ $block->getJsonConfig(); ?>, "jsonSwatchConfig": <?= /* @escapeNotVerified */ $block->getJsonSwatchConfig(); ?>, "mediaCallback": "<?= /* @escapeNotVerified */ $block->getMediaCallback() ?>", - "jsonSwatchImageSizeConfig": <?php /* @noEscape */ echo $block->getJsonSwatchSizeConfig() ?> + "jsonSwatchImageSizeConfig": <?= /* @noEscape */ $block->getJsonSwatchSizeConfig() ?> } } } From a0085a80984f0d0c26ef6d1bd93eae6bfa03731c Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Thu, 11 Oct 2018 13:58:35 +0400 Subject: [PATCH 087/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Add automated test --- ...AdminProductFormGroupedProductsSection.xml | 4 + .../AdminSortingAssociatedProductsTest.xml | 207 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml index 64dcd9566d890..0739c4e601b62 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml @@ -11,5 +11,9 @@ <section name="AdminProductFormGroupedProductsSection"> <element name="toggleGroupedProduct" type="button" selector="div[data-index=grouped] .admin__collapsible-title"/> <element name="addProductsToGroup" type="button" selector="button[data-index='grouped_products_button']" timeout="30"/> + <element name="nextActionButton" type="button" selector="//button[@class='action-next']"/> + <element name="previousActionButton" type="button" selector="//button[@class='action-previous']"/> + <element name="positionProduct" type="input" selector="//tbody/tr[{{arg}}][contains(@class,'data-row')]/td[10]//input[@class='position-widget-input']" parameterized="true"/> + <element name="nameProductFromGrid" type="text" selector="//tbody/tr[{{arg}}][contains(@class,'data-row')]/td[4]//*[@class='admin__field-control']//span" parameterized="true"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml new file mode 100644 index 0000000000000..ad5fbbb30edeb --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSortingAssociatedProductsTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages"/> + <title value="Grouped Products: Sorting Associated Products Between Pages"/> + <description value="Make sure that products in grid were recalculated when sorting associated products between pages"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-95085"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <createData entity="_defaultCategory" stepKey="category"/> + <!-- Create 23 products so that grid can have more than one page --> + <createData entity="ApiSimpleProduct" stepKey="product1"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product2"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product3"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product4"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product5"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product6"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product7"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product8"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product9"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product10"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product11"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product12"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product13"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product14"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product15"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product16"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product17"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product18"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product19"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product20"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product21"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product22"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product23"> + <requiredEntity createDataKey="category"/> + </createData> + </before> + <after> + <!--Delete created grouped product--> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" + dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <deleteData createDataKey="product1" stepKey="deleteProduct1"/> + <deleteData createDataKey="product2" stepKey="deleteProduct2"/> + <deleteData createDataKey="product3" stepKey="deleteProduct3"/> + <deleteData createDataKey="product4" stepKey="deleteProduct4"/> + <deleteData createDataKey="product5" stepKey="deleteProduct5"/> + <deleteData createDataKey="product6" stepKey="deleteProduct6"/> + <deleteData createDataKey="product7" stepKey="deleteProduct7"/> + <deleteData createDataKey="product8" stepKey="deleteProduct8"/> + <deleteData createDataKey="product9" stepKey="deleteProduct9"/> + <deleteData createDataKey="product10" stepKey="deleteProduct10"/> + <deleteData createDataKey="product11" stepKey="deleteProduct11"/> + <deleteData createDataKey="product12" stepKey="deleteProduct12"/> + <deleteData createDataKey="product13" stepKey="deleteProduct13"/> + <deleteData createDataKey="product14" stepKey="deleteProduct14"/> + <deleteData createDataKey="product15" stepKey="deleteProduct15"/> + <deleteData createDataKey="product16" stepKey="deleteProduct16"/> + <deleteData createDataKey="product17" stepKey="deleteProduct17"/> + <deleteData createDataKey="product18" stepKey="deleteProduct18"/> + <deleteData createDataKey="product19" stepKey="deleteProduct19"/> + <deleteData createDataKey="product20" stepKey="deleteProduct20"/> + <deleteData createDataKey="product21" stepKey="deleteProduct21"/> + <deleteData createDataKey="product22" stepKey="deleteProduct22"/> + <deleteData createDataKey="product23" stepKey="deleteProduct23"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create grouped Product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <actionGroup ref="fillGroupedProductForm" stepKey="fillProductForm"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToGroupedSection"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductsSection"/> + <click selector="body" stepKey="clickBodyToCorrectFocusGrouped"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> + <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForGroupedProductModal"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterGroupedProducts"> + <argument name="sku" value="api-simple-product"/> + </actionGroup> + + <!-- Select all, then start the bulk update attributes flow --> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + + <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> + <waitForPageLoad stepKey="waitForProductsAdded"/> + + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!--Open created Product group--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfExist"/> + <actionGroup ref="searchProductGridByKeyword" stepKey="searchProductGridForm"> + <argument name="keyword" value="GroupedProduct.name"/> + </actionGroup> + <click selector="{{AdminProductGridSection.selectRowBasedOnName(GroupedProduct.name)}}" stepKey="openGroupedProduct"/> + <waitForPageLoad stepKey="waitForProductEditPageLoad"/> + + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToGroupedSection2"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductsSection2"/> + + <!--Change position value for the Product Position 0--> + <grabTextFrom selector="{{AdminProductFormGroupedProductsSection.nameProductFromGrid('1')}}" stepKey="grabNameProductPosition0"/> + <grabTextFrom selector="{{AdminProductFormGroupedProductsSection.nameProductFromGrid('2')}}" stepKey="grabNameProductPositionFirst"/> + <fillField selector="{{AdminProductFormGroupedProductsSection.positionProduct('1')}}" userInput="21" stepKey="fillFieldProductPosition0"/> + <doubleClick selector="{{AdminProductFormGroupedProductsSection.nextActionButton}}" stepKey="clickButton"/> + <waitForAjaxLoad stepKey="waitForAjax1"/> + + <!--Go to next page and verify that Products in grid were recalculated--> + <doubleClick selector="{{AdminProductFormGroupedProductsSection.nextActionButton}}" stepKey="clickNextActionButton"/> + <waitForAjaxLoad stepKey="waitForAjax2"/> + + <grabTextFrom selector="{{AdminProductFormGroupedProductsSection.nameProductFromGrid('2')}}" stepKey="grabNameProductPosition21"/> + <assertEquals stepKey="assertProductsRecalculated"> + <actualResult type="string">$grabNameProductPosition0</actualResult> + <expectedResult type="string">$grabNameProductPosition21</expectedResult> + </assertEquals> + + <!--Change position value for the product to 1--> + <fillField selector="{{AdminProductFormGroupedProductsSection.positionProduct('2')}}" userInput="1" stepKey="fillFieldProductPosition1"/> + <doubleClick selector="{{AdminProductFormGroupedProductsSection.previousActionButton}}" stepKey="clickButton2"/> + <waitForAjaxLoad stepKey="waitForAjax3"/> + + <!--Go to previous page and verify that Products in grid were recalculated--> + <click selector="{{AdminProductFormGroupedProductsSection.previousActionButton}}" stepKey="clickPreviousActionButton"/> + <waitForAjaxLoad stepKey="waitForAjax4"/> + <grabTextFrom selector="{{AdminProductFormGroupedProductsSection.nameProductFromGrid('2')}}" stepKey="grabNameProductPosition2"/> + <grabTextFrom selector="{{AdminProductFormGroupedProductsSection.nameProductFromGrid('1')}}" stepKey="grabNameProductPositionZero"/> + <assertEquals stepKey="assertProductsRecalculated2"> + <actualResult type="string">$grabNameProductPosition2</actualResult> + <expectedResult type="string">$grabNameProductPosition0</expectedResult> + </assertEquals> + <assertEquals stepKey="assertProductsRecalculated3"> + <actualResult type="string">$grabNameProductPositionFirst</actualResult> + <expectedResult type="string">$grabNameProductPositionZero</expectedResult> + </assertEquals> + </test> +</tests> From 66198844e9a8653b3f53b63588d7b071daf65589 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 11 Oct 2018 17:20:25 +0300 Subject: [PATCH 088/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Block/DataProviders/ImageUploadConfig.php | 8 ++++---- app/code/Magento/Backend/Block/Media/Uploader.php | 12 ++++++------ ...{ImageUploadConfig.php => UploadResizeConfig.php} | 2 +- ...Interface.php => UploadResizeConfigInterface.php} | 4 ++-- app/code/Magento/Backend/etc/adminhtml/di.xml | 2 +- app/code/Magento/Backend/etc/adminhtml/system.xml | 4 ++-- .../Backend/view/adminhtml/web/js/media-uploader.js | 8 +++----- app/code/Magento/Catalog/etc/adminhtml/system.xml | 2 +- .../Magento/Framework/Image/Adapter/Config.php | 4 ++-- .../Image/Adapter/UploadConfigInterface.php | 4 ++-- 10 files changed, 24 insertions(+), 26 deletions(-) rename app/code/Magento/Backend/Model/Image/{ImageUploadConfig.php => UploadResizeConfig.php} (96%) rename app/code/Magento/Backend/Model/Image/{ImageUploadConfigInterface.php => UploadResizeConfigInterface.php} (89%) diff --git a/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php b/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php index 6ec40cfa0b2d4..9c17b0e5538f4 100644 --- a/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php +++ b/app/code/Magento/Backend/Block/DataProviders/ImageUploadConfig.php @@ -8,7 +8,7 @@ namespace Magento\Backend\Block\DataProviders; use Magento\Framework\View\Element\Block\ArgumentInterface; -use Magento\Backend\Model\Image\ImageUploadConfigInterface; +use Magento\Backend\Model\Image\UploadResizeConfigInterface; /** * Provides additional data for image uploader @@ -16,14 +16,14 @@ class ImageUploadConfig implements ArgumentInterface { /** - * @var ImageUploadConfigInterface + * @var UploadResizeConfigInterface */ private $imageUploadConfig; /** - * @param ImageUploadConfigInterface $imageUploadConfig + * @param UploadResizeConfigInterface $imageUploadConfig */ - public function __construct(ImageUploadConfigInterface $imageUploadConfig) + public function __construct(UploadResizeConfigInterface $imageUploadConfig) { $this->imageUploadConfig = $imageUploadConfig; } diff --git a/app/code/Magento/Backend/Block/Media/Uploader.php b/app/code/Magento/Backend/Block/Media/Uploader.php index 941d6f37737fe..84fa487281ac8 100644 --- a/app/code/Magento/Backend/Block/Media/Uploader.php +++ b/app/code/Magento/Backend/Block/Media/Uploader.php @@ -10,7 +10,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Image\Adapter\UploadConfigInterface; -use Magento\Backend\Model\Image\ImageUploadConfigInterface; +use Magento\Backend\Model\Image\UploadResizeConfigInterface; /** * Adminhtml media library uploader @@ -40,14 +40,14 @@ class Uploader extends \Magento\Backend\Block\Widget private $jsonEncoder; /** - * @var ImageUploadConfigInterface + * @var UploadResizeConfigInterface */ private $imageUploadConfig; /** * @var UploadConfigInterface * @deprecated - * @see \Magento\Backend\Model\Image\ImageUploadConfigInterface + * @see \Magento\Backend\Model\Image\UploadResizeConfigInterface */ private $imageConfig; @@ -57,7 +57,7 @@ class Uploader extends \Magento\Backend\Block\Widget * @param array $data * @param Json $jsonEncoder * @param UploadConfigInterface $imageConfig - * @param ImageUploadConfigInterface $imageUploadConfig + * @param UploadResizeConfigInterface $imageUploadConfig */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -65,14 +65,14 @@ public function __construct( array $data = [], Json $jsonEncoder = null, UploadConfigInterface $imageConfig = null, - ImageUploadConfigInterface $imageUploadConfig = null + UploadResizeConfigInterface $imageUploadConfig = null ) { $this->_fileSizeService = $fileSize; $this->jsonEncoder = $jsonEncoder ?: ObjectManager::getInstance()->get(Json::class); $this->imageConfig = $imageConfig ?: ObjectManager::getInstance()->get(UploadConfigInterface::class); $this->imageUploadConfig = $imageUploadConfig - ?: ObjectManager::getInstance()->get(ImageUploadConfigInterface::class); + ?: ObjectManager::getInstance()->get(UploadResizeConfigInterface::class); parent::__construct($context, $data); } diff --git a/app/code/Magento/Backend/Model/Image/ImageUploadConfig.php b/app/code/Magento/Backend/Model/Image/UploadResizeConfig.php similarity index 96% rename from app/code/Magento/Backend/Model/Image/ImageUploadConfig.php rename to app/code/Magento/Backend/Model/Image/UploadResizeConfig.php index b7e13b1e8274c..8155aa5e2fe2d 100644 --- a/app/code/Magento/Backend/Model/Image/ImageUploadConfig.php +++ b/app/code/Magento/Backend/Model/Image/UploadResizeConfig.php @@ -10,7 +10,7 @@ /** * Image uploader config provider. */ -class ImageUploadConfig implements ImageUploadConfigInterface +class UploadResizeConfig implements UploadResizeConfigInterface { /** * Config path for the maximal image width value diff --git a/app/code/Magento/Backend/Model/Image/ImageUploadConfigInterface.php b/app/code/Magento/Backend/Model/Image/UploadResizeConfigInterface.php similarity index 89% rename from app/code/Magento/Backend/Model/Image/ImageUploadConfigInterface.php rename to app/code/Magento/Backend/Model/Image/UploadResizeConfigInterface.php index 254a13407b2c1..50582dfafbcd1 100644 --- a/app/code/Magento/Backend/Model/Image/ImageUploadConfigInterface.php +++ b/app/code/Magento/Backend/Model/Image/UploadResizeConfigInterface.php @@ -8,11 +8,11 @@ namespace Magento\Backend\Model\Image; /** - * Interface ImageUploadConfigInterface + * Interface UploadResizeConfigInterface * * Used to retrieve configuration for frontend image uploader */ -interface ImageUploadConfigInterface +interface UploadResizeConfigInterface { /** * Get maximal width value for resized image diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index dcfc99cfed310..4abea272c5495 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -168,5 +168,5 @@ </arguments> </type> <preference for="CsrfRequestValidator" type="Magento\Backend\App\Request\BackendValidator" /> - <preference for="Magento\Backend\Model\Image\ImageUploadConfigInterface" type="Magento\Backend\Model\Image\ImageUploadConfig" /> + <preference for="Magento\Backend\Model\Image\UploadResizeConfigInterface" type="Magento\Backend\Model\Image\UploadResizeConfig" /> </config> diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 3a0d3e50acc5a..2915d8d671a01 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -339,9 +339,9 @@ <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Images Upload Configuration</label> <field id="enable_resize" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> - <label>Enable Front-end Resize</label> + <label>Enable Frontend Resize</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment>Resize performed by javascript during file upload.</comment> + <comment>Resize performed via javascript before file upload.</comment> </field> <field id="max_width" translate="label comment" type="text" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Maximum Width</label> diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js index e1d5a0a9debe5..119e7a35747cb 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/media-uploader.js @@ -33,8 +33,7 @@ define([ * @private */ _create: function () { - var - self = this, + var self = this, progressTmpl = mageTemplate('[data-template="uploader"]'), isResizeEnabled = this.options.isResizeEnabled, resizeConfiguration = { @@ -43,7 +42,7 @@ define([ maxHeight: this.options.maxHeight }; - if (isResizeEnabled === 0) { + if (!isResizeEnabled) { resizeConfiguration = { action: 'resize' }; @@ -64,8 +63,7 @@ define([ * @param {Object} data */ add: function (e, data) { - var - fileSize, + var fileSize, tmpl; $.each(data.files, function (index, file) { diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 2d872fc06b416..74661acb8f136 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -207,7 +207,7 @@ <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Quality</label> <validate>validate-greater-than-zero validate-number required-entry digits-range-1-100</validate> - <comment>Jpeg quality for images 1-100%.</comment> + <comment>Jpeg quality for resized images 1-100%.</comment> </field> </group> </section> diff --git a/lib/internal/Magento/Framework/Image/Adapter/Config.php b/lib/internal/Magento/Framework/Image/Adapter/Config.php index 84b6b6dcfbdd1..636bbcdcbdb41 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Config.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Config.php @@ -66,7 +66,7 @@ public function getAdapters() * * @return int * @deprecated - * @see \Magento\Backend\Model\Image\ImageUploadConfigInterface::getMaxHeight() + * @see \Magento\Backend\Model\Image\UploadResizeConfigInterface::getMaxHeight() */ public function getMaxWidth(): int { @@ -78,7 +78,7 @@ public function getMaxWidth(): int * * @return int * @deprecated - * @see \Magento\Backend\Model\Image\ImageUploadConfigInterface::getMaxHeight() + * @see \Magento\Backend\Model\Image\UploadResizeConfigInterface::getMaxHeight() */ public function getMaxHeight(): int { diff --git a/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php b/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php index 990062b83ef6f..0a2dbefff8ee0 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php +++ b/lib/internal/Magento/Framework/Image/Adapter/UploadConfigInterface.php @@ -9,8 +9,8 @@ /** * Interface UploadConfigInterface - * @deprecated because new interface was introduced - * @see \Magento\Backend\Model\Image\ImageUploadConfigInterface; + * @deprecated moved to proper namespace and extended + * @see \Magento\Backend\Model\Image\UploadResizeConfigInterface; */ interface UploadConfigInterface { From 2eb844f239c68591a6998d279103246609ef4a3d Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 11 Oct 2018 18:19:10 +0300 Subject: [PATCH 089/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- app/code/Magento/Catalog/Model/Product/Image.php | 6 +++--- .../view/adminhtml/templates/browser/content/uploader.phtml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Image.php b/app/code/Magento/Catalog/Model/Product/Image.php index adde1263e1bf9..a0be36c5a327c 100644 --- a/app/code/Magento/Catalog/Model/Product/Image.php +++ b/app/code/Magento/Catalog/Model/Product/Image.php @@ -45,7 +45,7 @@ class Image extends \Magento\Framework\Model\AbstractModel * Default quality value (for JPEG images only). * * @var int - * @deprecated + * @deprecated use config setting with path self::XML_PATH_JPEG_QUALITY */ protected $_quality = null; @@ -220,7 +220,7 @@ class Image extends \Magento\Framework\Model\AbstractModel * @param SerializerInterface $serializer * @param ParamsBuilder $paramsBuilder * @SuppressWarnings(PHPMD.ExcessiveParameterList) - * @SuppressWarnings(PHPMD.UnusedLocalVariable)1 + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function __construct( \Magento\Framework\Model\Context $context, @@ -305,7 +305,7 @@ public function getHeight() * * @param int $quality * @return $this - * @deprecated + * @deprecated use config setting with path self::XML_PATH_JPEG_QUALITY */ public function setQuality($quality) { diff --git a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml index 54f42bb4ecc1f..df0a74c48ac23 100644 --- a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml +++ b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml @@ -17,7 +17,7 @@ foreach ($filters as $media_type) { }, $media_type['files'])); } -$resizeConfig = ($block->getImageUploadConfigData()->getIsResizeEnabled()) +$resizeConfig = $block->getImageUploadConfigData()->getIsResizeEnabled() ? "{action: 'resize', maxWidth: " . $block->getImageUploadMaxWidth() . ", maxHeight: " From 0ec20f5a77fd11fc61d6a0d297bbd6ab09296a78 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Thu, 11 Oct 2018 11:16:23 -0500 Subject: [PATCH 090/310] MAGETWO-95588: Swatches on category page does not work - CR feedback --- .../Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml index dd497cca2c2de..0e294153e881e 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml @@ -23,7 +23,8 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Begin creating a new product attribute of type "Image Swatch" --> From 9c01411858079ff9c12b968ea8d681f6edad39b7 Mon Sep 17 00:00:00 2001 From: Vinai Kopp <vinai@netzarbeiter.com> Date: Thu, 14 Jun 2018 09:48:26 +0200 Subject: [PATCH 091/310] Fix typehint of customer-data updateSectionId parameters The typehint wrongly indicates the parameter should be a number, where in fact it is a boolean. It is passed through to the server side to the controller action \Magento\Customer\Controller\Section\Load::execute() where it is cast to a boolean and passed as the $updateIds parameter to \Magento\Customer\CustomerData\SectionPool::getSectionsData() and from there as the boolean parameter $forceUpdate to the \Magento\Customer\CustomerData\Section\Identifier::initMark() method. This PR introduces no backward incompatibilities, it only fixes the type hint to make the code more readable and improve compatibility with static code analysis tools. --- .../Magento/Customer/view/frontend/web/js/customer-data.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js index cfa06c6e12003..8cbc779a99ab0 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js +++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js @@ -75,7 +75,7 @@ define([ /** * @param {Object} sectionNames - * @param {Number} updateSectionId + * @param {Boolean} updateSectionId * @return {*} */ getFromServer: function (sectionNames, updateSectionId) { @@ -326,7 +326,7 @@ define([ /** * @param {Array} sectionNames - * @param {Number} updateSectionId + * @param {Boolean} updateSectionId * @return {*} */ reload: function (sectionNames, updateSectionId) { From 696a0c22dac2b426c0685d9b626c323fd62709b1 Mon Sep 17 00:00:00 2001 From: Vinai Kopp <vinai@netzarbeiter.com> Date: Sat, 16 Jun 2018 20:51:24 +0200 Subject: [PATCH 092/310] Rename parameter variable to make intent more easy to understand. --- .../Customer/view/frontend/web/js/customer-data.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js index 8cbc779a99ab0..07d08847aa4ea 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js +++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js @@ -75,17 +75,17 @@ define([ /** * @param {Object} sectionNames - * @param {Boolean} updateSectionId + * @param {Boolean} forceNewSectionTimestamp * @return {*} */ - getFromServer: function (sectionNames, updateSectionId) { + getFromServer: function (sectionNames, forceNewSectionTimestamp) { var parameters; sectionNames = sectionConfig.filterClientSideSections(sectionNames); parameters = _.isArray(sectionNames) ? { sections: sectionNames.join(',') } : []; - parameters['update_section_id'] = updateSectionId; + parameters['update_section_id'] = forceNewSectionTimestamp; return $.getJSON(options.sectionLoadUrl, parameters).fail(function (jqXHR) { throw new Error(jqXHR); @@ -326,11 +326,11 @@ define([ /** * @param {Array} sectionNames - * @param {Boolean} updateSectionId + * @param {Boolean} forceNewSectionTimestamp * @return {*} */ - reload: function (sectionNames, updateSectionId) { - return dataProvider.getFromServer(sectionNames, updateSectionId).done(function (sections) { + reload: function (sectionNames, forceNewSectionTimestamp) { + return dataProvider.getFromServer(sectionNames, forceNewSectionTimestamp).done(function (sections) { $(document).trigger('customer-data-reload', [sectionNames]); buffer.update(sections); }); From 3621c3c9b72278c8fd0b95c88eb9b9475dd4b51e Mon Sep 17 00:00:00 2001 From: Vinai Kopp <vinai@netzarbeiter.com> Date: Fri, 22 Jun 2018 08:47:36 -0400 Subject: [PATCH 093/310] Rename request parameter and variables to make them more expressive --- .../Customer/Controller/Section/Load.php | 8 +++---- .../CustomerData/Section/Identifier.php | 14 +++++------ .../Customer/CustomerData/SectionPool.php | 4 ++-- .../CustomerData/SectionPoolInterface.php | 4 ++-- .../Test/Unit/Controller/Section/LoadTest.php | 24 +++++++++---------- .../Unit/CustomerData/SectionPoolTest.php | 2 +- .../view/frontend/web/js/customer-data.js | 2 +- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Section/Load.php b/app/code/Magento/Customer/Controller/Section/Load.php index e55b1d0df26c7..89d4417c79c19 100644 --- a/app/code/Magento/Customer/Controller/Section/Load.php +++ b/app/code/Magento/Customer/Controller/Section/Load.php @@ -71,11 +71,11 @@ public function execute() $sectionNames = $this->getRequest()->getParam('sections'); $sectionNames = $sectionNames ? array_unique(\explode(',', $sectionNames)) : null; - $updateSectionId = $this->getRequest()->getParam('update_section_id'); - if ('false' === $updateSectionId) { - $updateSectionId = false; + $forceNewSectionTimestamp = $this->getRequest()->getParam('force_new_section_timestamp'); + if ('false' === $forceNewSectionTimestamp) { + $forceNewSectionTimestamp = false; } - $response = $this->sectionPool->getSectionsData($sectionNames, (bool)$updateSectionId); + $response = $this->sectionPool->getSectionsData($sectionNames, (bool)$forceNewSectionTimestamp); } catch (\Exception $e) { $resultJson->setStatusHeader( \Zend\Http\Response::STATUS_CODE_400, diff --git a/app/code/Magento/Customer/CustomerData/Section/Identifier.php b/app/code/Magento/Customer/CustomerData/Section/Identifier.php index 2a770925d1c37..54d7cee2d90bd 100644 --- a/app/code/Magento/Customer/CustomerData/Section/Identifier.php +++ b/app/code/Magento/Customer/CustomerData/Section/Identifier.php @@ -43,12 +43,12 @@ public function __construct( /** * Init mark(identifier) for sections * - * @param bool $forceUpdate + * @param bool $forceNewTimestamp * @return int */ - public function initMark($forceUpdate) + public function initMark($forceNewTimestamp) { - if ($forceUpdate) { + if ($forceNewTimestamp) { $this->markId = time(); return $this->markId; } @@ -68,18 +68,18 @@ public function initMark($forceUpdate) * * @param array $sectionsData * @param null $sectionNames - * @param bool $updateIds + * @param bool $forceNewTimestamp * @return array */ - public function markSections(array $sectionsData, $sectionNames = null, $updateIds = false) + public function markSections(array $sectionsData, $sectionNames = null, $forceNewTimestamp = false) { if (!$sectionNames) { $sectionNames = array_keys($sectionsData); } - $markId = $this->initMark($updateIds); + $markId = $this->initMark($forceNewTimestamp); foreach ($sectionNames as $name) { - if ($updateIds || !array_key_exists(self::SECTION_KEY, $sectionsData[$name])) { + if ($forceNewTimestamp || !array_key_exists(self::SECTION_KEY, $sectionsData[$name])) { $sectionsData[$name][self::SECTION_KEY] = $markId; } } diff --git a/app/code/Magento/Customer/CustomerData/SectionPool.php b/app/code/Magento/Customer/CustomerData/SectionPool.php index 0e0d7b992e33a..618d52079973d 100644 --- a/app/code/Magento/Customer/CustomerData/SectionPool.php +++ b/app/code/Magento/Customer/CustomerData/SectionPool.php @@ -55,10 +55,10 @@ public function __construct( /** * {@inheritdoc} */ - public function getSectionsData(array $sectionNames = null, $updateIds = false) + public function getSectionsData(array $sectionNames = null, $forceNewTimestamp = false) { $sectionsData = $sectionNames ? $this->getSectionDataByNames($sectionNames) : $this->getAllSectionData(); - $sectionsData = $this->identifier->markSections($sectionsData, $sectionNames, $updateIds); + $sectionsData = $this->identifier->markSections($sectionsData, $sectionNames, $forceNewTimestamp); return $sectionsData; } diff --git a/app/code/Magento/Customer/CustomerData/SectionPoolInterface.php b/app/code/Magento/Customer/CustomerData/SectionPoolInterface.php index c308804fd0f8d..ad73b9722b133 100644 --- a/app/code/Magento/Customer/CustomerData/SectionPoolInterface.php +++ b/app/code/Magento/Customer/CustomerData/SectionPoolInterface.php @@ -14,8 +14,8 @@ interface SectionPoolInterface * Get section data by section names. If $sectionNames is null then return all sections data * * @param array $sectionNames - * @param bool $updateIds + * @param bool $forceNewTimestamp * @return array */ - public function getSectionsData(array $sectionNames = null, $updateIds = false); + public function getSectionsData(array $sectionNames = null, $forceNewTimestamp = false); } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php index f4bf184f9ebf2..5a7cf42be2c7e 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php @@ -83,13 +83,13 @@ protected function setUp() } /** - * @param $sectionNames - * @param $updateSectionID - * @param $sectionNamesAsArray - * @param $updateIds + * @param string $sectionNames + * @param bool $forceNewSectionTimestamp + * @param string[] $sectionNamesAsArray + * @param bool $forceNewTimestamp * @dataProvider executeDataProvider */ - public function testExecute($sectionNames, $updateSectionID, $sectionNamesAsArray, $updateIds) + public function testExecute($sectionNames, $forceNewSectionTimestamp, $sectionNamesAsArray, $forceNewTimestamp) { $this->resultJsonFactoryMock->expects($this->once()) ->method('create') @@ -103,12 +103,12 @@ public function testExecute($sectionNames, $updateSectionID, $sectionNamesAsArra $this->httpRequestMock->expects($this->exactly(2)) ->method('getParam') - ->withConsecutive(['sections'], ['update_section_id']) - ->willReturnOnConsecutiveCalls($sectionNames, $updateSectionID); + ->withConsecutive(['sections'], ['force_new_section_timestamp']) + ->willReturnOnConsecutiveCalls($sectionNames, $forceNewSectionTimestamp); $this->sectionPoolMock->expects($this->once()) ->method('getSectionsData') - ->with($sectionNamesAsArray, $updateIds) + ->with($sectionNamesAsArray, $forceNewTimestamp) ->willReturn([ 'message' => 'some message', 'someKey' => 'someValue' @@ -133,15 +133,15 @@ public function executeDataProvider() return [ [ 'sectionNames' => 'sectionName1,sectionName2,sectionName3', - 'updateSectionID' => 'updateSectionID', + 'forceNewSectionTimestamp' => 'forceNewSectionTimestamp', 'sectionNamesAsArray' => ['sectionName1', 'sectionName2', 'sectionName3'], - 'updateIds' => true + 'forceNewTimestamp' => true ], [ 'sectionNames' => null, - 'updateSectionID' => null, + 'forceNewSectionTimestamp' => null, 'sectionNamesAsArray' => null, - 'updateIds' => false + 'forceNewTimestamp' => false ], ]; } diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/SectionPoolTest.php b/app/code/Magento/Customer/Test/Unit/CustomerData/SectionPoolTest.php index 98fee70e335f7..2b67df1aee292 100644 --- a/app/code/Magento/Customer/Test/Unit/CustomerData/SectionPoolTest.php +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/SectionPoolTest.php @@ -63,7 +63,7 @@ public function testGetSectionsDataAllSections() $this->identifierMock->expects($this->once()) ->method('markSections') - //check also default value for $updateIds = false + //check also default value for $forceTimestamp = false ->with($allSectionsData, $sectionNames, false) ->willReturn($identifierResult); $modelResult = $this->model->getSectionsData($sectionNames); diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js index 07d08847aa4ea..39e3f8d95ee3b 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js +++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js @@ -85,7 +85,7 @@ define([ parameters = _.isArray(sectionNames) ? { sections: sectionNames.join(',') } : []; - parameters['update_section_id'] = forceNewSectionTimestamp; + parameters['force_new_section_timestamp'] = forceNewSectionTimestamp; return $.getJSON(options.sectionLoadUrl, parameters).fail(function (jqXHR) { throw new Error(jqXHR); From c404de46a22640bfe844d9bbc63c28fe0ea0bffc Mon Sep 17 00:00:00 2001 From: Vinai Kopp <vinai@netzarbeiter.com> Date: Sat, 23 Jun 2018 17:40:12 +0200 Subject: [PATCH 094/310] Update query parameter name in performance toolkit to match new name in code --- setup/performance-toolkit/benchmark.jmx | 76 ++++++++++---------- setup/performance-toolkit/benchmark_2015.jmx | 36 +++++----- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 8b295c9f5fc46..e8e033fe59024 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -2881,12 +2881,12 @@ vars.put("customer_email", customerUser); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -4906,12 +4906,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -5272,12 +5272,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -5566,12 +5566,12 @@ vars.put("customer_email", customerUser); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -5739,12 +5739,12 @@ vars.put("product_sku", product.get("sku")); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -6171,12 +6171,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -6349,12 +6349,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -6787,12 +6787,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -7153,12 +7153,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -7869,12 +7869,12 @@ vars.put("customer_email", customerUser); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -8105,12 +8105,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -8471,12 +8471,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -9146,12 +9146,12 @@ vars.put("customer_email", customerUser); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -9330,12 +9330,12 @@ vars.put("product_sku", product.get("sku")); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -9662,12 +9662,12 @@ vars.put("customer_email", customerUser); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -9929,12 +9929,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -10295,12 +10295,12 @@ vars.put("totalProductsAdded", String.valueOf(productsAdded)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -10492,12 +10492,12 @@ vars.put("item_id", vars.get("cart_items_qty_inputs_" + id)); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> @@ -10813,12 +10813,12 @@ vars.put("customer_email", customerUser); <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">false</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> diff --git a/setup/performance-toolkit/benchmark_2015.jmx b/setup/performance-toolkit/benchmark_2015.jmx index 8be7749a08bc4..e58e610304199 100644 --- a/setup/performance-toolkit/benchmark_2015.jmx +++ b/setup/performance-toolkit/benchmark_2015.jmx @@ -2981,12 +2981,12 @@ vars.put("loadType", "Guest");</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> @@ -3217,12 +3217,12 @@ vars.put("loadType", "Guest");</stringProp> <stringProp name="Argument.name">sections</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> @@ -3565,12 +3565,12 @@ vars.put("loadType", "Guest");</stringProp> <stringProp name="Argument.name">sections</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> @@ -4039,12 +4039,12 @@ vars.put("loadType", "Guest");</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> @@ -4275,12 +4275,12 @@ vars.put("loadType", "Guest");</stringProp> <stringProp name="Argument.name">sections</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> @@ -4623,12 +4623,12 @@ vars.put("loadType", "Guest");</stringProp> <stringProp name="Argument.name">sections</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> @@ -5637,12 +5637,12 @@ vars.put("loadType", "Customer");</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> <stringProp name="Argument.name">sections</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">false</boolProp> @@ -5873,12 +5873,12 @@ vars.put("loadType", "Customer");</stringProp> <stringProp name="Argument.name">sections</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> @@ -6221,12 +6221,12 @@ vars.put("loadType", "Customer");</stringProp> <stringProp name="Argument.name">sections</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> - <elementProp name="update_section_id" elementType="HTTPArgument"> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> <boolProp name="HTTPArgument.always_encode">true</boolProp> <stringProp name="Argument.value">true</stringProp> <stringProp name="Argument.metadata">=</stringProp> <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">update_section_id</stringProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> <stringProp name="Argument.desc">true</stringProp> </elementProp> <elementProp name="_" elementType="HTTPArgument"> From b68802f2a07b342a56a36a74ff7383975f42332c Mon Sep 17 00:00:00 2001 From: pganapat <prabhuramgr28493@gmail.com> Date: Fri, 12 Oct 2018 15:08:56 -0500 Subject: [PATCH 095/310] MAGETWO-95213: Updating Product Status With Mass Action By Scope Updates The Default Value - Modified store_id validation to take in store_id from filters, when store is null in params. --- .../Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php index b7655f7ee2862..83278ca78c738 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php @@ -87,7 +87,7 @@ public function execute() $filterRequest = $this->getRequest()->getParam('filters', null); $status = (int) $this->getRequest()->getParam('status'); - if (null !== $storeId && null !== $filterRequest) { + if (null === $storeId && null !== $filterRequest) { $storeId = (isset($filterRequest['store_id'])) ? (int) $filterRequest['store_id'] : 0; } From ad85c9546bd06adddd908aef9cc0bb3b2346463e Mon Sep 17 00:00:00 2001 From: Matei Purcaru <matei.purcaru@gmail.com> Date: Sun, 14 Oct 2018 17:59:50 +0300 Subject: [PATCH 096/310] magento/graphql-ce#41: [Query] My Account > My Orders --- .../SalesGraphQl/Model/Resolver/Orders.php | 87 +++++++++++++ app/code/Magento/SalesGraphQl/README.md | 6 + app/code/Magento/SalesGraphQl/composer.json | 27 ++++ app/code/Magento/SalesGraphQl/etc/module.xml | 10 ++ .../Magento/SalesGraphQl/etc/schema.graphqls | 19 +++ .../Magento/SalesGraphQl/registration.php | 10 ++ dev/tests/api-functional/phpunit.xml.dist | 56 +++++++++ .../Magento/GraphQl/Sales/OrdersTest.php | 115 ++++++++++++++++++ .../Sales/_files/order_list_rollback.php | 2 - .../Sales/_files/orders_with_customer.php | 101 +++++++++++++++ .../_files/orders_with_customer_rollback.php | 7 ++ 11 files changed, 438 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php create mode 100644 app/code/Magento/SalesGraphQl/README.md create mode 100644 app/code/Magento/SalesGraphQl/composer.json create mode 100644 app/code/Magento/SalesGraphQl/etc/module.xml create mode 100644 app/code/Magento/SalesGraphQl/etc/schema.graphqls create mode 100644 app/code/Magento/SalesGraphQl/registration.php create mode 100644 dev/tests/api-functional/phpunit.xml.dist create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer_rollback.php diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php new file mode 100644 index 0000000000000..c6c6ac6203789 --- /dev/null +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesGraphQl\Model\Resolver; + +use Magento\Authorization\Model\UserContextInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface; +use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccountInterface; + +/** + * {@inheritdoc} + */ +class Orders implements ResolverInterface +{ + /** + * @var UserContextInterface + */ + private $userContext; + + /** + * @var CollectionFactoryInterface + */ + private $collectionFactory; + + /** + * @var CheckCustomerAccountInterface + */ + private $checkCustomerAccount; + + /** + * Orders constructor. + * @param UserContextInterface $userContext + * @param CollectionFactoryInterface $collectionFactory + */ + public function __construct( + UserContextInterface $userContext, + CollectionFactoryInterface $collectionFactory, + CheckCustomerAccountInterface $checkCustomerAccount + ) { + $this->userContext = $userContext; + $this->collectionFactory = $collectionFactory; + $this->checkCustomerAccount = $checkCustomerAccount; + + } + + /** + * {@inheritdoc} + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + + $customerId = $this->userContext->getUserId(); + + $this->checkCustomerAccount->execute($customerId, $this->userContext->getUserType()); + + $orders = $this->collectionFactory->create($customerId); + $items = []; + + // @TODO Add shipping & billing address in response + // @TODO Add order currency object in response + /** @var \Magento\Sales\Model\Order $order */ + foreach ($orders as $order) { + $items[] = [ + 'id' => $order->getId(), + 'increment_id' => $order->getIncrementId(), + 'created_at' => $order->getCreatedAt(), + 'grant_total' => $order->getGrandTotal(), + 'state' => $order->getState(), + 'status' => $order->getStatus() + ]; + } + + return ['items' => $items]; + } +} \ No newline at end of file diff --git a/app/code/Magento/SalesGraphQl/README.md b/app/code/Magento/SalesGraphQl/README.md new file mode 100644 index 0000000000000..d827613570bad --- /dev/null +++ b/app/code/Magento/SalesGraphQl/README.md @@ -0,0 +1,6 @@ +# SalesGraphQl + +**SalesGraphQl** provides type and resolver information for the GraphQl module +to generate sales orders information endpoints. + +Also will provides endpoints for modifying an order. diff --git a/app/code/Magento/SalesGraphQl/composer.json b/app/code/Magento/SalesGraphQl/composer.json new file mode 100644 index 0000000000000..b75e18c7b7a97 --- /dev/null +++ b/app/code/Magento/SalesGraphQl/composer.json @@ -0,0 +1,27 @@ +{ + "name": "magento/module-sales-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-customer": "*", + "magento/module-catalog": "*", + "magento/module-store": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\SalesGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/SalesGraphQl/etc/module.xml b/app/code/Magento/SalesGraphQl/etc/module.xml new file mode 100644 index 0000000000000..70a55db67a506 --- /dev/null +++ b/app/code/Magento/SalesGraphQl/etc/module.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_SalesGraphQl"/> +</config> diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..c137484cd2f65 --- /dev/null +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -0,0 +1,19 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +type Query { + customerOrders: Orders @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Orders") @doc(description: "List of customer orders") +} + +type Order @doc(description: "Order mapping fields") { + id: Int + increment_id: String + created_at: String + grant_total: Float + state: String + status: String +} + +type Orders { + items: [Order] @doc(description: "Array of orders") +} diff --git a/app/code/Magento/SalesGraphQl/registration.php b/app/code/Magento/SalesGraphQl/registration.php new file mode 100644 index 0000000000000..afb4091bfa32f --- /dev/null +++ b/app/code/Magento/SalesGraphQl/registration.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SalesGraphQl', __DIR__); diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist new file mode 100644 index 0000000000000..c869608462d08 --- /dev/null +++ b/dev/tests/api-functional/phpunit.xml.dist @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * PHPUnit configuration for GraphQL web API functional tests. + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.2/phpunit.xsd" + colors="true" + columns="max" + beStrictAboutTestsThatDoNotTestAnything="false" + bootstrap="./framework/bootstrap.php" +> + <!-- Test suites definition --> + <testsuites> + <testsuite name="Magento GraphQL web API functional tests"> + <directory suffix="Test.php">testsuite/Magento/GraphQl</directory> + </testsuite> + </testsuites> + + <!-- PHP INI settings and constants definition --> + <php> + <includePath>./testsuite</includePath> + <const name="TESTS_INSTALL_CONFIG_FILE" value="config/install-config-mysql.php"/> + <const name="TESTS_GLOBAL_CONFIG_FILE" value="config/config-global.php"/> + <!-- Webserver URL --> + <const name="TESTS_BASE_URL" value="http://magento.inno"/> + <!-- Webserver API user --> + <const name="TESTS_WEBSERVICE_USER" value="admin"/> + <!-- Webserver API key --> + <const name="TESTS_WEBSERVICE_APIKEY" value="123123q"/> + <!-- Define if debugger should be started using XDEBUG_SESSION cookie --> + <const name="TESTS_XDEBUG_ENABLED" value="true"/> + <!-- Define XDEBUG_SESSION cookie value--> + <const name="TESTS_XDEBUG_SESSION" value="MEETMAGENTO" /> + + <ini name="date.timezone" value="America/Los_Angeles"/> + + <!-- Semicolon-separated 'glob' patterns, that match global XML configuration files --> + <const name="TESTS_GLOBAL_CONFIG_DIR" value="../../../app/etc"/> + <!-- Whether to cleanup the application before running tests or not --> + <const name="TESTS_CLEANUP" value="enabled"/> + <!--Defines if Magento should be installed before tests execution--> + <const name="TESTS_MAGENTO_INSTALLATION" value="disabled"/> + <!-- Magento mode for tests execution. Possible values are "default", "developer" and "production". --> + <const name="TESTS_MAGENTO_MODE" value="default"/> + </php> + + <!-- Test listeners --> + <listeners> + <listener class="Magento\TestFramework\Event\PhpUnit"/> + </listeners> +</phpunit> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php new file mode 100644 index 0000000000000..3a85c044f16a9 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php @@ -0,0 +1,115 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Sales; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Class OrdersTest + */ +class OrdersTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + parent::setUp(); + $objectManager = Bootstrap::getObjectManager(); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Sales/_files/orders_with_customer.php + */ + public function testOrdersQuery() + { + $query = + <<<QUERY +query { + customerOrders { + items { + id + increment_id + created_at + grant_total + state + status + } + } +} +QUERY; + + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + + $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + + $expectedData = [ + [ + 'increment_id' => '100000002', + 'state' => \Magento\Sales\Model\Order::STATE_NEW, + 'status' => 'processing', + 'grant_total' => 120.00 + ], + [ + 'increment_id' => '100000003', + 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, + 'status' => 'processing', + 'grant_total' => 130.00 + ], + [ + 'increment_id' => '100000004', + 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, + 'status' => 'closed', + 'grant_total' => 140.00 + ], + [ + 'increment_id' => '100000005', + 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, + 'status' => 'complete', + 'grant_total' => 150.00 + ], + [ + 'increment_id' => '100000006', + 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, + 'status' => 'complete', + 'grant_total' => 160.00 + ] + ]; + + $actualData = $response['customerOrders']['items']; + + foreach ($expectedData as $key => $data) { + $this->assertEquals($data['increment_id'], $actualData[$key]['increment_id']); + $this->assertEquals($data['grant_total'], $actualData[$key]['grant_total']); + $this->assertEquals($data['state'], $actualData[$key]['state']); + $this->assertEquals($data['status'], $actualData[$key]['status']); + } + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_rollback.php index aae4a557ba1ed..6e24cee501f51 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list_rollback.php @@ -4,6 +4,4 @@ * See COPYING.txt for license details. */ -use Magento\Sales\Model\Order; - require 'default_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php new file mode 100644 index 0000000000000..e649396391ca9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Model\Order; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order\Address as OrderAddress; + +require 'order.php'; +/** @var Order $order */ +/** @var Order\Payment $payment */ +/** @var Order\Item $orderItem */ +/** @var array $addressData Data for creating addresses for the orders. */ +$orders = [ + [ + 'increment_id' => '100000002', + 'state' => \Magento\Sales\Model\Order::STATE_NEW, + 'status' => 'processing', + 'grand_total' => 120.00, + 'subtotal' => 120.00, + 'base_grand_total' => 120.00, + 'store_id' => 1, + 'website_id' => 1, + 'payment' => $payment + ], + [ + 'increment_id' => '100000003', + 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, + 'status' => 'processing', + 'grand_total' => 130.00, + 'base_grand_total' => 130.00, + 'subtotal' => 130.00, + 'store_id' => 0, + 'website_id' => 0, + 'payment' => $payment + ], + [ + 'increment_id' => '100000004', + 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, + 'status' => 'closed', + 'grand_total' => 140.00, + 'base_grand_total' => 140.00, + 'subtotal' => 140.00, + 'store_id' => 1, + 'website_id' => 1, + 'payment' => $payment + ], + [ + 'increment_id' => '100000005', + 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, + 'status' => 'complete', + 'grand_total' => 150.00, + 'base_grand_total' => 150.00, + 'subtotal' => 150.00, + 'store_id' => 1, + 'website_id' => 1, + 'payment' => $payment + ], + [ + 'increment_id' => '100000006', + 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, + 'status' => 'complete', + 'grand_total' => 160.00, + 'base_grand_total' => 160.00, + 'subtotal' => 160.00, + 'store_id' => 1, + 'website_id' => 1, + 'payment' => $payment + ], +]; + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +/** @var array $orderData */ +foreach ($orders as $orderData) { + /** @var $order \Magento\Sales\Model\Order */ + $order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Sales\Model\Order::class + ); + + // Reset addresses + /** @var Order\Address $billingAddress */ + $billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); + $billingAddress->setAddressType('billing'); + + $shippingAddress = clone $billingAddress; + $shippingAddress->setId(null)->setAddressType('shipping'); + + $order + ->setData($orderData) + ->addItem($orderItem) + ->setCustomerIsGuest(false) + ->setCustomerId(1) + ->setCustomerEmail('customer@example.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress); + + $orderRepository->save($order); +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer_rollback.php new file mode 100644 index 0000000000000..6e24cee501f51 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer_rollback.php @@ -0,0 +1,7 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require 'default_rollback.php'; From 584eb7d6c876ea8c20e8a032ebddb43f5f8d3553 Mon Sep 17 00:00:00 2001 From: Matei Purcaru <matei.purcaru@gmail.com> Date: Mon, 15 Oct 2018 19:46:32 +0300 Subject: [PATCH 097/310] magento/graphql-ce#41: Fixed typos --- .../SalesGraphQl/Model/Resolver/Orders.php | 4 +- app/code/Magento/SalesGraphQl/README.md | 4 +- .../Magento/SalesGraphQl/etc/schema.graphqls | 11 ++-- dev/tests/api-functional/phpunit.xml.dist | 56 ------------------- .../Magento/GraphQl/Sales/OrdersTest.php | 12 ++-- 5 files changed, 13 insertions(+), 74 deletions(-) delete mode 100644 dev/tests/api-functional/phpunit.xml.dist diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php index c6c6ac6203789..91078c1580634 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php @@ -68,15 +68,13 @@ public function resolve( $orders = $this->collectionFactory->create($customerId); $items = []; - // @TODO Add shipping & billing address in response - // @TODO Add order currency object in response /** @var \Magento\Sales\Model\Order $order */ foreach ($orders as $order) { $items[] = [ 'id' => $order->getId(), 'increment_id' => $order->getIncrementId(), 'created_at' => $order->getCreatedAt(), - 'grant_total' => $order->getGrandTotal(), + 'grand_total' => $order->getGrandTotal(), 'state' => $order->getState(), 'status' => $order->getStatus() ]; diff --git a/app/code/Magento/SalesGraphQl/README.md b/app/code/Magento/SalesGraphQl/README.md index d827613570bad..d5717821b164c 100644 --- a/app/code/Magento/SalesGraphQl/README.md +++ b/app/code/Magento/SalesGraphQl/README.md @@ -1,6 +1,4 @@ # SalesGraphQl **SalesGraphQl** provides type and resolver information for the GraphQl module -to generate sales orders information endpoints. - -Also will provides endpoints for modifying an order. +to generate sales orders information. diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index c137484cd2f65..44f106532858f 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -2,18 +2,17 @@ # See COPYING.txt for license details. type Query { - customerOrders: Orders @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Orders") @doc(description: "List of customer orders") + customerOrders: CustomerOrders @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Orders") @doc(description: "List of customer orders") } -type Order @doc(description: "Order mapping fields") { +type CustomerOrder @doc(description: "Order mapping fields") { id: Int increment_id: String created_at: String - grant_total: Float - state: String + grand_total: Float status: String } -type Orders { - items: [Order] @doc(description: "Array of orders") +type CustomerOrders { + items: [CustomerOrder] @doc(description: "Array of orders") } diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist deleted file mode 100644 index c869608462d08..0000000000000 --- a/dev/tests/api-functional/phpunit.xml.dist +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/** - * PHPUnit configuration for GraphQL web API functional tests. - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.2/phpunit.xsd" - colors="true" - columns="max" - beStrictAboutTestsThatDoNotTestAnything="false" - bootstrap="./framework/bootstrap.php" -> - <!-- Test suites definition --> - <testsuites> - <testsuite name="Magento GraphQL web API functional tests"> - <directory suffix="Test.php">testsuite/Magento/GraphQl</directory> - </testsuite> - </testsuites> - - <!-- PHP INI settings and constants definition --> - <php> - <includePath>./testsuite</includePath> - <const name="TESTS_INSTALL_CONFIG_FILE" value="config/install-config-mysql.php"/> - <const name="TESTS_GLOBAL_CONFIG_FILE" value="config/config-global.php"/> - <!-- Webserver URL --> - <const name="TESTS_BASE_URL" value="http://magento.inno"/> - <!-- Webserver API user --> - <const name="TESTS_WEBSERVICE_USER" value="admin"/> - <!-- Webserver API key --> - <const name="TESTS_WEBSERVICE_APIKEY" value="123123q"/> - <!-- Define if debugger should be started using XDEBUG_SESSION cookie --> - <const name="TESTS_XDEBUG_ENABLED" value="true"/> - <!-- Define XDEBUG_SESSION cookie value--> - <const name="TESTS_XDEBUG_SESSION" value="MEETMAGENTO" /> - - <ini name="date.timezone" value="America/Los_Angeles"/> - - <!-- Semicolon-separated 'glob' patterns, that match global XML configuration files --> - <const name="TESTS_GLOBAL_CONFIG_DIR" value="../../../app/etc"/> - <!-- Whether to cleanup the application before running tests or not --> - <const name="TESTS_CLEANUP" value="enabled"/> - <!--Defines if Magento should be installed before tests execution--> - <const name="TESTS_MAGENTO_INSTALLATION" value="disabled"/> - <!-- Magento mode for tests execution. Possible values are "default", "developer" and "production". --> - <const name="TESTS_MAGENTO_MODE" value="default"/> - </php> - - <!-- Test listeners --> - <listeners> - <listener class="Magento\TestFramework\Event\PhpUnit"/> - </listeners> -</phpunit> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php index 3a85c044f16a9..ef7ceebc3a053 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php @@ -63,31 +63,31 @@ public function testOrdersQuery() 'increment_id' => '100000002', 'state' => \Magento\Sales\Model\Order::STATE_NEW, 'status' => 'processing', - 'grant_total' => 120.00 + 'grand_total' => 120.00 ], [ 'increment_id' => '100000003', 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, 'status' => 'processing', - 'grant_total' => 130.00 + 'grand_total' => 130.00 ], [ 'increment_id' => '100000004', 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, 'status' => 'closed', - 'grant_total' => 140.00 + 'grand_total' => 140.00 ], [ 'increment_id' => '100000005', 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, 'status' => 'complete', - 'grant_total' => 150.00 + 'grand_total' => 150.00 ], [ 'increment_id' => '100000006', 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, 'status' => 'complete', - 'grant_total' => 160.00 + 'grand_total' => 160.00 ] ]; @@ -95,7 +95,7 @@ public function testOrdersQuery() foreach ($expectedData as $key => $data) { $this->assertEquals($data['increment_id'], $actualData[$key]['increment_id']); - $this->assertEquals($data['grant_total'], $actualData[$key]['grant_total']); + $this->assertEquals($data['grand_total'], $actualData[$key]['grand_total']); $this->assertEquals($data['state'], $actualData[$key]['state']); $this->assertEquals($data['status'], $actualData[$key]['status']); } From 4a31f4f11972bf69fdbbe125fd262f9dfdfed0de Mon Sep 17 00:00:00 2001 From: Matei Purcaru <matei.purcaru@gmail.com> Date: Mon, 15 Oct 2018 19:55:48 +0300 Subject: [PATCH 098/310] magento/graphql-ce#41: get user info from resolver context --- .../SalesGraphQl/Model/Resolver/Orders.php | 29 +++++++------------ .../Magento/GraphQl/Sales/OrdersTest.php | 23 +++++---------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php index 91078c1580634..05af6829de454 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php @@ -15,15 +15,10 @@ use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccountInterface; /** - * {@inheritdoc} + * Class Orders */ class Orders implements ResolverInterface { - /** - * @var UserContextInterface - */ - private $userContext; - /** * @var CollectionFactoryInterface */ @@ -36,15 +31,13 @@ class Orders implements ResolverInterface /** * Orders constructor. - * @param UserContextInterface $userContext * @param CollectionFactoryInterface $collectionFactory + * @param CheckCustomerAccountInterface $checkCustomerAccount */ public function __construct( - UserContextInterface $userContext, CollectionFactoryInterface $collectionFactory, CheckCustomerAccountInterface $checkCustomerAccount ) { - $this->userContext = $userContext; $this->collectionFactory = $collectionFactory; $this->checkCustomerAccount = $checkCustomerAccount; @@ -55,18 +48,17 @@ public function __construct( */ public function resolve( Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null + $context, + ResolveInfo $info, + array $value = null, + array $args = null ) { + $customerId = $context->getUserId(); - $customerId = $this->userContext->getUserId(); + $this->checkCustomerAccount->execute($customerId, $context->getUserType()); - $this->checkCustomerAccount->execute($customerId, $this->userContext->getUserType()); - - $orders = $this->collectionFactory->create($customerId); $items = []; + $orders = $this->collectionFactory->create($customerId); /** @var \Magento\Sales\Model\Order $order */ foreach ($orders as $order) { @@ -75,11 +67,10 @@ public function resolve( 'increment_id' => $order->getIncrementId(), 'created_at' => $order->getCreatedAt(), 'grand_total' => $order->getGrandTotal(), - 'state' => $order->getState(), 'status' => $order->getStatus() ]; } return ['items' => $items]; } -} \ No newline at end of file +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php index ef7ceebc3a053..33620f6e55386 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php @@ -40,16 +40,15 @@ public function testOrdersQuery() $query = <<<QUERY query { - customerOrders { - items { - id - increment_id - created_at - grant_total - state - status - } + customerOrders { + items { + id + increment_id + created_at + grand_total + status } + } } QUERY; @@ -61,31 +60,26 @@ public function testOrdersQuery() $expectedData = [ [ 'increment_id' => '100000002', - 'state' => \Magento\Sales\Model\Order::STATE_NEW, 'status' => 'processing', 'grand_total' => 120.00 ], [ 'increment_id' => '100000003', - 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, 'status' => 'processing', 'grand_total' => 130.00 ], [ 'increment_id' => '100000004', - 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, 'status' => 'closed', 'grand_total' => 140.00 ], [ 'increment_id' => '100000005', - 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, 'status' => 'complete', 'grand_total' => 150.00 ], [ 'increment_id' => '100000006', - 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, 'status' => 'complete', 'grand_total' => 160.00 ] @@ -96,7 +90,6 @@ public function testOrdersQuery() foreach ($expectedData as $key => $data) { $this->assertEquals($data['increment_id'], $actualData[$key]['increment_id']); $this->assertEquals($data['grand_total'], $actualData[$key]['grand_total']); - $this->assertEquals($data['state'], $actualData[$key]['state']); $this->assertEquals($data['status'], $actualData[$key]['status']); } } From 94f25c3ba8dffb00ab31c3cdc9a6262b42fabee6 Mon Sep 17 00:00:00 2001 From: pganapat <prabhuramgr28493@gmail.com> Date: Mon, 15 Oct 2018 12:17:26 -0500 Subject: [PATCH 099/310] MAGETWO-95213: Updating Product Status With Mass Action By Scope Updates The Default Value - Added short class description. --- .../Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php index 83278ca78c738..9d7273fb3f23c 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php @@ -14,6 +14,7 @@ use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; /** + * Updates status for a batch of products. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MassStatus extends \Magento\Catalog\Controller\Adminhtml\Product implements HttpPostActionInterface From ecb1a7bf306e87eb54a08a7c5a19eac214838ce6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 16 Oct 2018 13:10:15 +0200 Subject: [PATCH 100/310] Another attempt to execute tests --- .travis.yml | 22 ++++---- dev/tests/api-functional/phpunit.xml.dist | 56 ------------------- .../api-functional/phpunit_graphql.xml.dist | 2 +- dev/travis/before_install.sh | 2 +- dev/travis/before_script.sh | 29 ++++++++++ 5 files changed, 43 insertions(+), 68 deletions(-) delete mode 100644 dev/tests/api-functional/phpunit.xml.dist diff --git a/.travis.yml b/.travis.yml index c6077564b923d..930172a9ea6e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,14 +25,14 @@ env: - NODE_JS_VERSION=8 - MAGENTO_HOST_NAME="magento2.travis" matrix: - - TEST_SUITE=unit - - TEST_SUITE=static - - TEST_SUITE=js GRUNT_COMMAND=spec - - TEST_SUITE=js GRUNT_COMMAND=static - - TEST_SUITE=integration INTEGRATION_INDEX=1 - - TEST_SUITE=integration INTEGRATION_INDEX=2 - - TEST_SUITE=integration INTEGRATION_INDEX=3 - - TEST_SUITE=functional +# - TEST_SUITE=unit +# - TEST_SUITE=static +# - TEST_SUITE=js GRUNT_COMMAND=spec +# - TEST_SUITE=js GRUNT_COMMAND=static +# - TEST_SUITE=integration INTEGRATION_INDEX=1 +# - TEST_SUITE=integration INTEGRATION_INDEX=2 +# - TEST_SUITE=integration INTEGRATION_INDEX=3 +# - TEST_SUITE=functional - TEST_SUITE=api-functional matrix: exclude: @@ -44,6 +44,8 @@ matrix: env: TEST_SUITE=js GRUNT_COMMAND=static - php: 7.1 env: TEST_SUITE=functional + - php: 7.1 + env: TEST_SUITE=api-functional cache: apt: true directories: @@ -62,6 +64,6 @@ script: # The scripts for grunt/phpunit type tests - if [ $TEST_SUITE == "functional" ]; then dev/tests/functional/vendor/phpunit/phpunit/phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - #- if [ $TEST_SUITE == "api-functional" ]; then phpunit -c dev/tests/$TEST_SUITE/phpunit_graphql.xml.dist; fi + - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js"] && [ $TEST_SUITE != "api" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - if [ $TEST_SUITE == "js" ]; then grunt $GRUNT_COMMAND; fi + - if [ $TEST_SUITE == "api-functional" ]; then phpunit -c dev/tests/api-functional; fi diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist deleted file mode 100644 index a55531aba87e2..0000000000000 --- a/dev/tests/api-functional/phpunit.xml.dist +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/** - * PHPUnit configuration for GraphQL web API functional tests. - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.2/phpunit.xsd" - colors="true" - columns="max" - beStrictAboutTestsThatDoNotTestAnything="false" - bootstrap="./framework/bootstrap.php" -> - <!-- Test suites definition --> - <testsuites> - <testsuite name="Magento GraphQL web API functional tests"> - <directory suffix="Test.php">testsuite/Magento/GraphQl</directory> - </testsuite> - </testsuites> - - <!-- PHP INI settings and constants definition --> - <php> - <includePath>./testsuite</includePath> - <const name="TESTS_INSTALL_CONFIG_FILE" value="config/install-config-mysql.php"/> - <const name="TESTS_GLOBAL_CONFIG_FILE" value="config/config-global.php"/> - <!-- Webserver URL --> - <const name="TESTS_BASE_URL" value="http://magento2.travis"/> - <!-- Webserver API user --> - <const name="TESTS_WEBSERVICE_USER" value="admin"/> - <!-- Webserver API key --> - <const name="TESTS_WEBSERVICE_APIKEY" value="123123q"/> - <!-- Define if debugger should be started using XDEBUG_SESSION cookie --> - <const name="TESTS_XDEBUG_ENABLED" value="false"/> - <!-- Define XDEBUG_SESSION cookie value--> - <const name="TESTS_XDEBUG_SESSION" value="phpstorm" /> - - <ini name="date.timezone" value="America/Los_Angeles"/> - - <!-- Semicolon-separated 'glob' patterns, that match global XML configuration files --> - <const name="TESTS_GLOBAL_CONFIG_DIR" value="../../../app/etc"/> - <!-- Whether to cleanup the application before running tests or not --> - <const name="TESTS_CLEANUP" value="enabled"/> - <!--Defines if Magento should be installed before tests execution--> - <const name="TESTS_MAGENTO_INSTALLATION" value="disabled"/> - <!-- Magento mode for tests execution. Possible values are "default", "developer" and "production". --> - <const name="TESTS_MAGENTO_MODE" value="production"/> - </php> - - <!-- Test listeners --> - <listeners> - <listener class="Magento\TestFramework\Event\PhpUnit"/> - </listeners> -</phpunit> diff --git a/dev/tests/api-functional/phpunit_graphql.xml.dist b/dev/tests/api-functional/phpunit_graphql.xml.dist index 955a3a8953fa8..4a57c338ca3a2 100644 --- a/dev/tests/api-functional/phpunit_graphql.xml.dist +++ b/dev/tests/api-functional/phpunit_graphql.xml.dist @@ -27,7 +27,7 @@ <const name="TESTS_INSTALL_CONFIG_FILE" value="config/install-config-mysql.php"/> <const name="TESTS_GLOBAL_CONFIG_FILE" value="config/config-global.php"/> <!-- Webserver URL --> - <const name="TESTS_BASE_URL" value="http://magento2.travis"/> + <const name="TESTS_BASE_URL" value="http://magento.url"/> <!-- Webserver API user --> <const name="TESTS_WEBSERVICE_USER" value="admin"/> <!-- Webserver API key --> diff --git a/dev/travis/before_install.sh b/dev/travis/before_install.sh index c9302f3b6672c..22d6c04dabda4 100755 --- a/dev/travis/before_install.sh +++ b/dev/travis/before_install.sh @@ -34,7 +34,7 @@ if [ $TEST_SUITE == "js" ]; then yarn global add grunt-cli fi -if [ $TEST_SUITE = "functional" ]; then +if [ $TEST_SUITE = "functional" or $TEST_SUITE = "api-functional" ]; then # Install apache sudo apt-get update sudo apt-get install apache2 libapache2-mod-fastcgi diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 1dccc310c7a20..c27e65d3897c7 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -132,6 +132,35 @@ case $TEST_SUITE in php -f generate/moduleSequence.php php -f mtf troubleshooting:check-all + cd ../../.. + ;; + + api-functional) + echo "Installing Magento" + mysql -uroot -e 'CREATE DATABASE magento2;' + php bin/magento setup:install -q \ + --language="en_US" \ + --timezone="UTC" \ + --currency="USD" \ + --base-url="http://${MAGENTO_HOST_NAME}/" \ + --admin-firstname="John" \ + --admin-lastname="Doe" \ + --backend-frontname="backend" \ + --admin-email="admin@example.com" \ + --admin-user="admin" \ + --use-rewrites=1 \ + --admin-use-security-key=0 \ + --admin-password="123123q" + + echo "Enabling production mode" + php bin/magento deploy:mode:set production + + echo "Prepare api-functional tests for running" + cd dev/tests/api-functional + + cp ./phpunit_graphql.xml.dist ./phpunit.xml + sed -e "s?magento.url?${MAGENTO_HOST_NAME}?g" --in-place ./phpunit.xml + cd ../../.. ;; esac From 599cf87a2c7a7eabce65e482557dcfa8f649874c Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 16 Oct 2018 13:19:47 +0200 Subject: [PATCH 101/310] Condition fix for before_install script --- dev/travis/before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/travis/before_install.sh b/dev/travis/before_install.sh index 22d6c04dabda4..44ba3fd8b74f1 100755 --- a/dev/travis/before_install.sh +++ b/dev/travis/before_install.sh @@ -34,7 +34,7 @@ if [ $TEST_SUITE == "js" ]; then yarn global add grunt-cli fi -if [ $TEST_SUITE = "functional" or $TEST_SUITE = "api-functional" ]; then +if [ $TEST_SUITE = "functional" ] || [ $TEST_SUITE = "api-functional" ]; then # Install apache sudo apt-get update sudo apt-get install apache2 libapache2-mod-fastcgi From 09e70057d810aa7004f7760b084d16b4833936b1 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 16 Oct 2018 13:27:48 +0200 Subject: [PATCH 102/310] Test api-functional tests alongside with other tests --- .travis.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 930172a9ea6e0..f5a25bc63543d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,14 +25,14 @@ env: - NODE_JS_VERSION=8 - MAGENTO_HOST_NAME="magento2.travis" matrix: -# - TEST_SUITE=unit -# - TEST_SUITE=static -# - TEST_SUITE=js GRUNT_COMMAND=spec -# - TEST_SUITE=js GRUNT_COMMAND=static -# - TEST_SUITE=integration INTEGRATION_INDEX=1 -# - TEST_SUITE=integration INTEGRATION_INDEX=2 -# - TEST_SUITE=integration INTEGRATION_INDEX=3 -# - TEST_SUITE=functional + - TEST_SUITE=unit + - TEST_SUITE=static + - TEST_SUITE=js GRUNT_COMMAND=spec + - TEST_SUITE=js GRUNT_COMMAND=static + - TEST_SUITE=integration INTEGRATION_INDEX=1 + - TEST_SUITE=integration INTEGRATION_INDEX=2 + - TEST_SUITE=integration INTEGRATION_INDEX=3 + - TEST_SUITE=functional - TEST_SUITE=api-functional matrix: exclude: From e3fc117bfa5158b51c875e76461539aba5b2b6a7 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 16 Oct 2018 15:33:43 +0300 Subject: [PATCH 103/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- app/code/Magento/Catalog/etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 74661acb8f136..1b0fe4c85f617 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -206,7 +206,7 @@ <label>Images Upload Configuration</label> <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Quality</label> - <validate>validate-greater-than-zero validate-number required-entry digits-range-1-100</validate> + <validate>validate-greater-than-zero validate-digits validate-range required-entry digits-range-1-100</validate> <comment>Jpeg quality for resized images 1-100%.</comment> </field> </group> From c83a1caa6b1eb7182f235310938c22cfea7826f4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 19 Oct 2018 11:58:59 +0300 Subject: [PATCH 104/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- app/code/Magento/Catalog/etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 1b0fe4c85f617..7478fa2226454 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -206,7 +206,7 @@ <label>Images Upload Configuration</label> <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Quality</label> - <validate>validate-greater-than-zero validate-digits validate-range required-entry digits-range-1-100</validate> + <validate>validate-digits validate-digits-range digits-range-1-100 required-entry</validate> <comment>Jpeg quality for resized images 1-100%.</comment> </field> </group> From 76a7243995ce3d6ba205df8386da6c6d392b71dd Mon Sep 17 00:00:00 2001 From: Ronak Patel <ronak2ram@gmail.com> Date: Sun, 21 Oct 2018 12:45:04 +0530 Subject: [PATCH 105/310] Fixed - Total pages field returns null in category query --- .../CatalogGraphQl/Model/Resolver/Category/Products.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php index 557c7e08ff432..7a41f8fc94e74 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php @@ -92,7 +92,8 @@ public function resolve( 'items' => $searchResult->getProductsSearchResult(), 'page_info' => [ 'page_size' => $searchCriteria->getPageSize(), - 'current_page' => $currentPage + 'current_page' => $currentPage, + 'total_pages' => $maxPages ] ]; return $data; From 4662ca91833b95662b8f7098718026153c290d05 Mon Sep 17 00:00:00 2001 From: vprohorov <vitaliy_prokharau@epam.com> Date: Mon, 22 Oct 2018 14:25:06 +0300 Subject: [PATCH 106/310] MAGETWO-91537: Search synonyms results missing for words including hyphen and numbers - Fixing test --- .../Magento/Catalog/Test/Repository/CatalogProductSimple.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 721b0ff570079..e90ca6bf7868a 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 @@ -41,7 +41,7 @@ <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> </field> - <field name="name" xsi:type="string">Product \'!@#$%^&*()+:;\\|}{][?=-~` %isolation%</field> + <field name="name" xsi:type="string">Product \'!@#$%^&*()+:;\\|}{][?=~` %isolation%</field> <field name="sku" xsi:type="string">sku_simple_product_%isolation%</field> <field name="is_virtual" xsi:type="string">No</field> <field name="product_has_weight" xsi:type="string">This item has weight</field> From 93c6073eca8f6611ad3702b17861dd4aee7a9364 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 22 Oct 2018 15:55:02 +0200 Subject: [PATCH 107/310] Attempt to determine why GraphQl test modules are not present in the env --- dev/travis/before_script.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index c27e65d3897c7..52fd44c2826cc 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -152,6 +152,9 @@ case $TEST_SUITE in --admin-use-security-key=0 \ --admin-password="123123q" + php bin/magento module:status # DEBUG + php bin/magento setup:upgrade # DEBUG + echo "Enabling production mode" php bin/magento deploy:mode:set production From f9a7b81238135a51f964ef861e26c9ec6aa557e9 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 22 Oct 2018 16:17:31 +0200 Subject: [PATCH 108/310] Temporarily disable all other tests except api-fucntional --- .travis.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index f5a25bc63543d..930172a9ea6e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,14 +25,14 @@ env: - NODE_JS_VERSION=8 - MAGENTO_HOST_NAME="magento2.travis" matrix: - - TEST_SUITE=unit - - TEST_SUITE=static - - TEST_SUITE=js GRUNT_COMMAND=spec - - TEST_SUITE=js GRUNT_COMMAND=static - - TEST_SUITE=integration INTEGRATION_INDEX=1 - - TEST_SUITE=integration INTEGRATION_INDEX=2 - - TEST_SUITE=integration INTEGRATION_INDEX=3 - - TEST_SUITE=functional +# - TEST_SUITE=unit +# - TEST_SUITE=static +# - TEST_SUITE=js GRUNT_COMMAND=spec +# - TEST_SUITE=js GRUNT_COMMAND=static +# - TEST_SUITE=integration INTEGRATION_INDEX=1 +# - TEST_SUITE=integration INTEGRATION_INDEX=2 +# - TEST_SUITE=integration INTEGRATION_INDEX=3 +# - TEST_SUITE=functional - TEST_SUITE=api-functional matrix: exclude: From 97b519cafff9638d9455d9dc2e0aaca3704d805b Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 22 Oct 2018 19:19:21 +0200 Subject: [PATCH 109/310] Introduced cart address resolver and data provider --- .../Resolver/Address/AddressDataProvider.php | 70 +++++++++++++++ .../Model/Resolver/CartAddress.php | 87 +++++++++++++++++++ .../SetShippingMethodsOnCart.php | 6 +- .../Magento/QuoteGraphQl/etc/schema.graphqls | 22 ++--- 4 files changed, 173 insertions(+), 12 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php new file mode 100644 index 0000000000000..d401f296cb465 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver\Address; + +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\CartInterface; + +/** + * Class AddressDataProvider + * + * Collect and return information about cart shipping and billing addresses + */ +class AddressDataProvider +{ + /** + * @var ExtensibleDataObjectConverter + */ + private $dataObjectConverter; + + /** + * AddressDataProvider constructor. + * + * @param ExtensibleDataObjectConverter $dataObjectConverter + */ + public function __construct( + ExtensibleDataObjectConverter $dataObjectConverter + ) { + $this->dataObjectConverter = $dataObjectConverter; + } + + /** + * Collect and return information about shipping and billing addresses + * + * @param CartInterface $cart + * @return array + */ + public function getCartAddresses(CartInterface $cart): array + { + $addressData = []; + $shippingAddress = $cart->getShippingAddress(); + $billingAddress = $cart->getBillingAddress(); + + if ($shippingAddress) { + $shippingData = $this->dataObjectConverter->toFlatArray($shippingAddress, [], AddressInterface::class); + $shippingData['address_type'] = 'SHIPPING'; + $shippingData['selected_shipping_method'] = [ + 'code' => $shippingAddress->getShippingMethod(), + 'label' => $shippingAddress->getShippingDescription(), + 'free_shipping' => $shippingAddress->getFreeShipping(), + ]; + $shippingData['items_weight'] = $shippingAddress->getWeight(); + $shippingData['customer_notes'] = $shippingAddress->getCustomerNotes(); + $addressData[] = $shippingData; + } + + if ($billingAddress) { + $billingData = $this->dataObjectConverter->toFlatArray($billingAddress, [], AddressInterface::class); + $billingData['address_type'] = 'BILLING'; + $addressData[] = $billingData; + } + + return $addressData; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php new file mode 100644 index 0000000000000..f83a112ba5ba8 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\MaskedQuoteIdToQuoteId; +use Magento\QuoteGraphQl\Model\Resolver\Address\AddressDataProvider; + +/** + * @inheritdoc + */ +class CartAddress implements ResolverInterface +{ + /** + * @var AddressDataProvider + */ + private $addressDataProvider; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @var MaskedQuoteIdToQuoteId + */ + private $maskedQuoteIdToQuoteId; + + /** + * CartAddress constructor. + * + * @param MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId + * @param CartRepositoryInterface $cartRepository + * @param AddressDataProvider $addressDataProvider + */ + public function __construct( + MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId, + CartRepositoryInterface $cartRepository, + AddressDataProvider $addressDataProvider + ) { + $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->cartRepository = $cartRepository; + $this->addressDataProvider = $addressDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['cart_id'])) { + // TODO: consider the possibility to pass quote model instead od quote ID + throw new LocalizedException(__('"cart_id" value should be specified')); + } + + try { + $quoteId = $this->maskedQuoteIdToQuoteId->execute($value['cart_id']); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $value['cart_id']]) + ); + } + + // TODO: should we check customer permissions here as well? + + try { + $quote = $this->cartRepository->get($quoteId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%quote_id"', ['quote_id' => $quoteId]) + ); + } + + return $this->addressDataProvider->getCartAddresses($quote); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index c2d14668a2e70..1f35f37d9cf23 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -158,6 +158,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new GraphQlInputException(__($exception->getMessage())); } - return 'Success!'; // TODO we should return cart here + return [ + 'cart' => [ + 'cart_id' => $maskedCartId + ] + ]; } } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 730a54377b370..cbc56bfaea66f 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -67,7 +67,7 @@ type SetShippingAddressesOnCartOutput { } type SetShippingMethodsOnCartOutput { - cart: String #TODO: temp placeholder, should be Cart! + cart: Cart! } # If no address is provided, the system get address assigned to a quote @@ -92,28 +92,28 @@ type ApplyCouponToCartOutput { } type Cart { + cart_id: String items: [CartItemInterface] applied_coupon: AppliedCoupon - addresses: [CartAddress]! + addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddress") } type CartAddress { - firstname: String! - lastname: String! + firstname: String + lastname: String company: String - street: [String!]! - city: String! + street: [String] + city: String region: CartAddressRegion postcode: String - country: CartAddressCountry! - telephone: String! - address_type: AdressTypeEnum! + country: CartAddressCountry + telephone: String + address_type: AdressTypeEnum selected_shipping_method: CheckoutShippingMethod - available_shipping_methods: [CheckoutShippingMethod]! + available_shipping_methods: [CheckoutShippingMethod] items_weight: Float customer_notes: String cart_items: [CartItemQuantity] - applied_coupon: AppliedCoupon } type CartItemQuantity { From f66d26afbbe9e44afb800a6255213e11ae8fab72 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 23 Oct 2018 17:01:19 +0200 Subject: [PATCH 110/310] Added debug information for checking tests modules are deployed --- dev/tests/integration/framework/deployTestModules.php | 4 +++- dev/travis/before_script.sh | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/framework/deployTestModules.php b/dev/tests/integration/framework/deployTestModules.php index 4c894d80f9800..f7be2b8414d23 100644 --- a/dev/tests/integration/framework/deployTestModules.php +++ b/dev/tests/integration/framework/deployTestModules.php @@ -1,4 +1,4 @@ -<?php +x<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -38,3 +38,5 @@ foreach ($files as $file) { include $file; } + +echo "\n\n TEST MODULES WERE DEPLOYED \n\n"; // DEBUG diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 52fd44c2826cc..c27e65d3897c7 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -152,9 +152,6 @@ case $TEST_SUITE in --admin-use-security-key=0 \ --admin-password="123123q" - php bin/magento module:status # DEBUG - php bin/magento setup:upgrade # DEBUG - echo "Enabling production mode" php bin/magento deploy:mode:set production From 2736ff910b56784ffe8513dec151d31b854dba65 Mon Sep 17 00:00:00 2001 From: pganapat <prabhuramgr28493@gmail.com> Date: Tue, 23 Oct 2018 10:11:20 -0500 Subject: [PATCH 111/310] MAGETWO-95213: Updating Product Status With Mass Action By Scope Updates The Default Value - Added MFTF case to cover the scenario MAGETWO-95213. Associated with Zephyr test MAGETWO-59361 --- .../ActionGroup/AdminProductActionGroup.xml | 21 +++ .../Catalog/Test/Mftf/Data/ProductData.xml | 30 ++++ .../Section/AdminProductFiltersSection.xml | 2 + .../Mftf/Section/AdminProductGridSection.xml | 1 + ...sUpdateProductStatusStoreViewScopeTest.xml | 151 ++++++++++++++++++ .../AdminFilterStoreViewActionGroup.xml | 21 +++ 6 files changed, 226 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml create mode 100644 app/code/Magento/Store/Test/Mftf/ActionGroup/AdminFilterStoreViewActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 9f8d827b20849..f06cc816e8595 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -277,4 +277,25 @@ <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> + <!--Create a Simple Product--> + <actionGroup name="createSimpleProductAndAddToWebsite"> + <arguments> + <argument name="product"/> + <argument name="website" type="string"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> + <waitForPageLoad stepKey="waitForProductGrid"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillProductName"/> + <fillField userInput="{{product.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillProductSKU"/> + <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillProductPrice"/> + <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillProductQuantity"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsites"/> + <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForLoadingMaskToDisappear stepKey="waitForProductPageSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + </actionGroup> + </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 7c0cc03186c6e..494335dd56f13 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -420,4 +420,34 @@ <data key="status">1</data> <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> </entity> + <entity name="simpleProductForMassUpdate" type="product"> + <data key="sku" unique="suffix">testSku</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">massUpdateProductName</data> + <data key="keyword">massUpdateProductName</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">masstesturlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> + <entity name="simpleProductForMassUpdate2" type="product"> + <data key="sku" unique="suffix">testSku</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">massUpdateProductName</data> + <data key="keyword">massUpdateProductName</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">masstesturlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml index 7a9de9670f216..06ff54b2a3997 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml @@ -28,5 +28,7 @@ <element name="priceOfFirstRow" type="text" selector="//tr[@data-repeat-index='0']//div[contains(., '{{var1}}')]" parameterized="true"/> <element name="AllProductsNotOfBundleType" type="text" selector="//td[5]/div[text() != 'Bundle Product']"/> <element name="attributeSetOfFirstRow" type="text" selector="//tr[@data-repeat-index='0']//div[contains(., '{{var1}}')]" parameterized="true"/> + <element name="storeViewDropDown" type="multiselect" selector="//select[@name='store_id']" timeout="30"/> + <element name="storeViewOption" type="multiselect" selector="//select[@name='store_id']/option[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml index a7e20e22f1ddc..d12233206ce41 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml @@ -28,6 +28,7 @@ <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> <element name="productGridCheckboxOnRow" type="checkbox" selector="//*[@id='container']//tr[{{row}}]/td[1]//input" parameterized="true"/> <element name="productGridNameProduct" type="input" selector="//tbody//tr//td//div[contains(., '{{var1}}')]" parameterized="true" timeout="30"/> + <element name="productGridContentsOnRow" type="checkbox" selector="//*[@id='container']//tr[{{row}}]/td" parameterized="true"/> <element name="selectRowBasedOnName" type="input" selector="//td/div[text()='{{var1}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml new file mode 100644 index 0000000000000..6d1f266f4c66b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassUpdateProductStatusStoreViewScopeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product status"/> + <title value="Admin should be able to mass update product statuses in store view scope"/> + <description value="Admin should be able to mass update product statuses in store view scope"/> + <severity value="AVERAGE"/> + <testCaseId value="MAGETWO-59361"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + </annotations> + <before> + <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Create Website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> + <argument name="newWebsiteName" value="Second Website"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + + <!--Create Store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> + <argument name="website" value="Second Website"/> + <argument name="storeGroupName" value="Second Store"/> + <argument name="storeGroupCode" value="second_store"/> + </actionGroup> + + <!--Create Store view --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> + <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> + <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> + <waitForPageLoad stepKey="waitForPageLoad2" time="180" /> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage" /> + + <!--Create a Simple Product 1 --> + <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct1"> + <argument name="product" value="simpleProductForMassUpdate"/> + <argument name="website" value="Second Website"/> + </actionGroup> + + <!--Create a Simple Product 2 --> + <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct2"> + <argument name="product" value="simpleProductForMassUpdate2"/> + <argument name="website" value="Second Website"/> + </actionGroup> + </before> + <after> + <!--Delete website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + + <!--Delete Products --> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="simpleProductForMassUpdate.name"/> + </actionGroup> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> + <argument name="productName" value="simpleProductForMassUpdate2.name"/> + </actionGroup> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> + </actionGroup> + <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + + <!-- Filter to Second Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView" > + <argument name="customStore" value="'Second Store View'" /> + </actionGroup> + + <!-- Select Product 2 --> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOption"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabled"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabled"/> + + <!-- Filter to Default Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> + <argument name="customStore" value="'Default'" /> + </actionGroup> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="{{simpleProductForMassUpdate.keyword}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Enable the product in Default store view --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckboxDefaultStoreView2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdownDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOptionDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabledDefaultStoreView"/> + <waitForPageLoad stepKey="waitForBulkUpdatePageDefaultStoreView"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabledDefaultStoreView"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault2"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault2"> + <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> + <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> + </test> +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminFilterStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminFilterStoreViewActionGroup.xml new file mode 100644 index 0000000000000..e4cb26ea6ff7a --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminFilterStoreViewActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<!-- Test XML Example --> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFilterStoreViewActionGroup"> + <arguments> + <argument name="StoreGroup" defaultValue="_defaultStoreGroup"/> + <argument name="customStore" defaultValue="customStore.name"/> + </arguments> + <click selector="{{AdminProductFiltersSection.filter}}" stepKey="ClickOnFilter"/> + <click selector="{{AdminProductFiltersSection.storeViewDropDown}}" stepKey="ClickOnStoreViewDropDown"/> + <click selector="{{AdminProductFiltersSection.storeViewOption(customStore)}}" stepKey="ClickOnStoreViewOption"/> + <click selector="{{AdminProductFiltersSection.applyFilters}}" stepKey="ClickOnApplyFilters"/> + </actionGroup> +</actionGroups> From 66329bbd41a6aac1d454c73c4e826e547dc81a02 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 23 Oct 2018 17:43:50 +0200 Subject: [PATCH 112/310] More details in debug information about deployed modules --- dev/tests/integration/framework/deployTestModules.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/tests/integration/framework/deployTestModules.php b/dev/tests/integration/framework/deployTestModules.php index f7be2b8414d23..6e57d748a55eb 100644 --- a/dev/tests/integration/framework/deployTestModules.php +++ b/dev/tests/integration/framework/deployTestModules.php @@ -25,6 +25,7 @@ mkdir($targetDir, 0755, true); } copy($source, $destination); + echo "\n\nDestination $destination\n\n"; // DEBUG } } unset($iterator, $file); @@ -38,5 +39,3 @@ foreach ($files as $file) { include $file; } - -echo "\n\n TEST MODULES WERE DEPLOYED \n\n"; // DEBUG From 29cb28b41987ae4199fbc8ac14c3640d2224ec23 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 23 Oct 2018 17:58:54 +0200 Subject: [PATCH 113/310] Remove debug info --- dev/tests/integration/framework/deployTestModules.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/integration/framework/deployTestModules.php b/dev/tests/integration/framework/deployTestModules.php index 6e57d748a55eb..f4fb881d3ed70 100644 --- a/dev/tests/integration/framework/deployTestModules.php +++ b/dev/tests/integration/framework/deployTestModules.php @@ -25,7 +25,6 @@ mkdir($targetDir, 0755, true); } copy($source, $destination); - echo "\n\nDestination $destination\n\n"; // DEBUG } } unset($iterator, $file); From 4fdc63807fff721244e7ccff31064b634904075b Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 23 Oct 2018 18:04:52 +0200 Subject: [PATCH 114/310] removed accidentally added char --- dev/tests/integration/framework/deployTestModules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/framework/deployTestModules.php b/dev/tests/integration/framework/deployTestModules.php index f4fb881d3ed70..4c894d80f9800 100644 --- a/dev/tests/integration/framework/deployTestModules.php +++ b/dev/tests/integration/framework/deployTestModules.php @@ -1,4 +1,4 @@ -x<?php +<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. From 6e1710aec76825c8fc03e1e159f518dfba8d18e4 Mon Sep 17 00:00:00 2001 From: pganapat <prabhuramgr28493@gmail.com> Date: Tue, 23 Oct 2018 17:29:09 -0500 Subject: [PATCH 115/310] MAGETWO-95213: Updating Product Status With Mass Action By Scope Updates The Default Value - Review comments fix. Using action group for logout. --- .../Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml index 6d1f266f4c66b..e9b54e3f1a3dc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml @@ -79,7 +79,7 @@ <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> <argument name="productName" value="simpleProductForMassUpdate2.name"/> </actionGroup> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <actionGroup ref="logout" stepKey="amOnLogoutPage"/> </after> <!-- Search and select products --> From 1a745a5db170b77ed92b6d71444bc8c8e0f0aa3e Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Wed, 24 Oct 2018 15:47:58 -0500 Subject: [PATCH 116/310] MAGETWO-95798: All country state's are shown for USA when shipping form has custom address attributes. --- .../Ui/view/base/web/js/form/element/region.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/region.js b/app/code/Magento/Ui/view/base/web/js/form/element/region.js index 45d38b339b50b..f6eafcf49284d 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/region.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/region.js @@ -78,13 +78,12 @@ define([ * @param {String} field */ filter: function (value, field) { - var country = registry.get(this.parentName + '.' + 'country_id'), - option; + var superFn = this._super; - if (country) { - option = country.indexedOptions[value]; + registry.get(this.parentName + '.' + 'country_id', function (country) { + var option = country.indexedOptions[value]; - this._super(value, field); + superFn.call(this, value, field); if (option && option['is_region_visible'] === false) { // hide select and corresponding text input field if region must not be shown for selected country @@ -94,7 +93,7 @@ define([ this.toggleInput(false); } } - } + }.bind(this)); } }); }); From 4349d022166c905e4bd77f3bfb36118c7310ff19 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 25 Oct 2018 11:08:56 +0200 Subject: [PATCH 117/310] Run tests without magento installation --- dev/travis/before_script.sh | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index c27e65d3897c7..135152928539c 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -138,25 +138,27 @@ case $TEST_SUITE in api-functional) echo "Installing Magento" mysql -uroot -e 'CREATE DATABASE magento2;' - php bin/magento setup:install -q \ - --language="en_US" \ - --timezone="UTC" \ - --currency="USD" \ - --base-url="http://${MAGENTO_HOST_NAME}/" \ - --admin-firstname="John" \ - --admin-lastname="Doe" \ - --backend-frontname="backend" \ - --admin-email="admin@example.com" \ - --admin-user="admin" \ - --use-rewrites=1 \ - --admin-use-security-key=0 \ - --admin-password="123123q" - - echo "Enabling production mode" - php bin/magento deploy:mode:set production +# php bin/magento setup:install -q \ +# --language="en_US" \ +# --timezone="UTC" \ +# --currency="USD" \ +# --base-url="http://${MAGENTO_HOST_NAME}/" \ +# --admin-firstname="John" \ +# --admin-lastname="Doe" \ +# --backend-frontname="backend" \ +# --admin-email="admin@example.com" \ +# --admin-user="admin" \ +# --use-rewrites=1 \ +# --admin-use-security-key=0 \ +# --admin-password="123123q" + +# echo "Enabling production mode" +# php bin/magento deploy:mode:set production echo "Prepare api-functional tests for running" cd dev/tests/api-functional + cp config/install-config-mysql.php.dist config/install-config-mysql.php + sed -e "s?http://localhost/?http://${MAGENTO_HOST_NAME}/?g" --in-place ./config/install-config-mysql.php cp ./phpunit_graphql.xml.dist ./phpunit.xml sed -e "s?magento.url?${MAGENTO_HOST_NAME}?g" --in-place ./phpunit.xml From ccfffbc78a4e2c0204c0f16e8b1ff32316f5f7c1 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 25 Oct 2018 14:27:13 +0200 Subject: [PATCH 118/310] Deploy test modules before api-functional tests --- .travis.yml | 4 ++-- dev/travis/before_install.sh | 2 +- dev/travis/before_script.sh | 39 ++++++++++++++++++------------------ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index 930172a9ea6e0..25b18949f0386 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ env: # - TEST_SUITE=integration INTEGRATION_INDEX=2 # - TEST_SUITE=integration INTEGRATION_INDEX=3 # - TEST_SUITE=functional - - TEST_SUITE=api-functional + - TEST_SUITE=graphql-api-functional matrix: exclude: - php: 7.1 @@ -66,4 +66,4 @@ script: - if [ $TEST_SUITE == "functional" ]; then dev/tests/functional/vendor/phpunit/phpunit/phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js"] && [ $TEST_SUITE != "api" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - if [ $TEST_SUITE == "js" ]; then grunt $GRUNT_COMMAND; fi - - if [ $TEST_SUITE == "api-functional" ]; then phpunit -c dev/tests/api-functional; fi + - if [ $TEST_SUITE == "graphql-api-functional" ]; then phpunit -c dev/tests/api-functional; fi diff --git a/dev/travis/before_install.sh b/dev/travis/before_install.sh index 44ba3fd8b74f1..845d70e4e79fd 100755 --- a/dev/travis/before_install.sh +++ b/dev/travis/before_install.sh @@ -34,7 +34,7 @@ if [ $TEST_SUITE == "js" ]; then yarn global add grunt-cli fi -if [ $TEST_SUITE = "functional" ] || [ $TEST_SUITE = "api-functional" ]; then +if [ $TEST_SUITE = "functional" ] || [ $TEST_SUITE = "graphql-api-functional" ]; then # Install apache sudo apt-get update sudo apt-get install apache2 libapache2-mod-fastcgi diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 135152928539c..64591e8112cef 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -135,34 +135,35 @@ case $TEST_SUITE in cd ../../.. ;; - api-functional) + graphql-api-functional) echo "Installing Magento" mysql -uroot -e 'CREATE DATABASE magento2;' -# php bin/magento setup:install -q \ -# --language="en_US" \ -# --timezone="UTC" \ -# --currency="USD" \ -# --base-url="http://${MAGENTO_HOST_NAME}/" \ -# --admin-firstname="John" \ -# --admin-lastname="Doe" \ -# --backend-frontname="backend" \ -# --admin-email="admin@example.com" \ -# --admin-user="admin" \ -# --use-rewrites=1 \ -# --admin-use-security-key=0 \ -# --admin-password="123123q" - -# echo "Enabling production mode" -# php bin/magento deploy:mode:set production + php bin/magento setup:install -q \ + --language="en_US" \ + --timezone="UTC" \ + --currency="USD" \ + --base-url="http://${MAGENTO_HOST_NAME}/" \ + --admin-firstname="John" \ + --admin-lastname="Doe" \ + --backend-frontname="backend" \ + --admin-email="admin@example.com" \ + --admin-user="admin" \ + --use-rewrites=1 \ + --admin-use-security-key=0 \ + --admin-password="123123q" + + echo "Enabling production mode" + php bin/magento deploy:mode:set production echo "Prepare api-functional tests for running" cd dev/tests/api-functional - cp config/install-config-mysql.php.dist config/install-config-mysql.php - sed -e "s?http://localhost/?http://${MAGENTO_HOST_NAME}/?g" --in-place ./config/install-config-mysql.php + cp -r _files/Magento/* ../../../app/code/Magento # Deploy and enable test modules before running tests + php ../../../bin/magento setup:upgrade cp ./phpunit_graphql.xml.dist ./phpunit.xml sed -e "s?magento.url?${MAGENTO_HOST_NAME}?g" --in-place ./phpunit.xml cd ../../.. + php bin/magento setup:upgrade ;; esac From 33f40d63ee1d102e81822341fadeb4cd5af68997 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 25 Oct 2018 14:50:06 +0200 Subject: [PATCH 119/310] Remove redundant setup:upgrade --- .travis.yml | 2 +- dev/travis/before_script.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 25b18949f0386..db42be9f1e217 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,7 @@ matrix: - php: 7.1 env: TEST_SUITE=functional - php: 7.1 - env: TEST_SUITE=api-functional + env: TEST_SUITE=graphql-api-functional cache: apt: true directories: diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 64591e8112cef..8b5dcab310418 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -158,7 +158,6 @@ case $TEST_SUITE in echo "Prepare api-functional tests for running" cd dev/tests/api-functional cp -r _files/Magento/* ../../../app/code/Magento # Deploy and enable test modules before running tests - php ../../../bin/magento setup:upgrade cp ./phpunit_graphql.xml.dist ./phpunit.xml sed -e "s?magento.url?${MAGENTO_HOST_NAME}?g" --in-place ./phpunit.xml From 41bf2a1485ead39c75d97cccc819c9cc2091f0ca Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 25 Oct 2018 15:11:04 +0200 Subject: [PATCH 120/310] Enable back other tests --- .travis.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index db42be9f1e217..78b088c9d848d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,14 +25,14 @@ env: - NODE_JS_VERSION=8 - MAGENTO_HOST_NAME="magento2.travis" matrix: -# - TEST_SUITE=unit -# - TEST_SUITE=static -# - TEST_SUITE=js GRUNT_COMMAND=spec -# - TEST_SUITE=js GRUNT_COMMAND=static -# - TEST_SUITE=integration INTEGRATION_INDEX=1 -# - TEST_SUITE=integration INTEGRATION_INDEX=2 -# - TEST_SUITE=integration INTEGRATION_INDEX=3 -# - TEST_SUITE=functional + - TEST_SUITE=unit + - TEST_SUITE=static + - TEST_SUITE=js GRUNT_COMMAND=spec + - TEST_SUITE=js GRUNT_COMMAND=static + - TEST_SUITE=integration INTEGRATION_INDEX=1 + - TEST_SUITE=integration INTEGRATION_INDEX=2 + - TEST_SUITE=integration INTEGRATION_INDEX=3 + - TEST_SUITE=functional - TEST_SUITE=graphql-api-functional matrix: exclude: From 63cc7536ae1d0a070c047b3b06ad3eb1889b61a7 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 26 Oct 2018 13:22:57 +0300 Subject: [PATCH 121/310] MAGETWO-95753: [2.3] Cannot save product with Tier Prices --- app/code/Magento/Catalog/Helper/Data.php | 23 ++-- .../Backend/TierPrice/UpdateHandler.php | 17 ++- .../Controller/Adminhtml/ProductTest.php | 103 ++++++++++++++++++ 3 files changed, 125 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Data.php b/app/code/Magento/Catalog/Helper/Data.php index ae20cda460796..c83eb70486c43 100644 --- a/app/code/Magento/Catalog/Helper/Data.php +++ b/app/code/Magento/Catalog/Helper/Data.php @@ -7,6 +7,7 @@ use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Store\Model\ScopeInterface; use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Pricing\PriceCurrencyInterface; @@ -273,7 +274,8 @@ public function setStoreId($store) /** * Return current category path or get it from current category - * and creating array of categories|product paths for breadcrumbs + * + * Creating array of categories|product paths for breadcrumbs * * @return array */ @@ -382,6 +384,7 @@ public function getLastViewedUrl() /** * Split SKU of an item by dashes and spaces + * * Words will not be broken, unless this length is greater than $length * * @param string $sku @@ -410,14 +413,15 @@ public function getAttributeHiddenFields() /** * Retrieve Catalog Price Scope * - * @return int + * @return int/null */ - public function getPriceScope() + public function getPriceScope(): ?int { - return $this->scopeConfig->getValue( + $priceScope = $this->scopeConfig->getValue( self::XML_PATH_PRICE_SCOPE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE ); + return isset($priceScope) ? (int)$priceScope : null; } /** @@ -439,7 +443,7 @@ public function isUsingStaticUrlsAllowed() { return $this->scopeConfig->isSetFlag( self::CONFIG_USE_STATIC_URLS, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE ); } @@ -454,7 +458,7 @@ public function isUrlDirectivesParsingAllowed() { return $this->scopeConfig->isSetFlag( self::CONFIG_PARSE_URL_DIRECTIVES, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $this->_storeId ); } @@ -472,6 +476,7 @@ public function getPageTemplateProcessor() /** * Whether to display items count for each filter option + * * @param int $storeId Store view ID * @return bool */ @@ -479,12 +484,14 @@ public function shouldDisplayProductCountOnLayer($storeId = null) { return $this->scopeConfig->isSetFlag( self::XML_PATH_DISPLAY_PRODUCT_COUNT, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); } /** + * Convert tax address array to address data object with country id and postcode + * * @param array $taxAddress * @return \Magento\Customer\Api\Data\AddressInterface|null */ diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php index b4d6dc2c19e51..aef3e87586015 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php @@ -86,10 +86,10 @@ public function execute($entity, $arguments = []) __('Tier prices data should be array, but actually other type is received') ); } - $websiteId = $this->storeManager->getStore($entity->getStoreId())->getWebsiteId(); + $websiteId = (int)$this->storeManager->getStore($entity->getStoreId())->getWebsiteId(); $isGlobal = $attribute->isScopeGlobal() || $websiteId === 0; $identifierField = $this->metadataPoll->getMetadata(ProductInterface::class)->getLinkField(); - $productId = (int) $entity->getData($identifierField); + $productId = (int)$entity->getData($identifierField); // prepare original data to compare $origPrices = []; @@ -98,7 +98,7 @@ public function execute($entity, $arguments = []) $origPrices = $entity->getOrigData($attribute->getName()); } - $old = $this->prepareOriginalDataToCompare($origPrices, $isGlobal); + $old = $this->prepareOldTierPriceToCompare($origPrices); // prepare data for save $new = $this->prepareNewDataForSave($priceRows, $isGlobal); @@ -271,21 +271,18 @@ private function isWebsiteGlobal(int $websiteId): bool } /** - * Prepare original data to compare. + * Prepare old data to compare. * * @param array|null $origPrices - * @param bool $isGlobal * @return array */ - private function prepareOriginalDataToCompare(?array $origPrices, bool $isGlobal = true): array + private function prepareOldTierPriceToCompare(?array $origPrices): array { $old = []; if (is_array($origPrices)) { foreach ($origPrices as $data) { - if ($isGlobal === $this->isWebsiteGlobal((int)$data['website_id'])) { - $key = $this->getPriceKey($data); - $old[$key] = $data; - } + $key = $this->getPriceKey($data); + $old[$key] = $data; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index 06bbc43e36e8d..d949993e7ea3c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -252,4 +252,107 @@ public function saveActionWithAlreadyExistingUrlKeyDataProvider() ] ]; } + + /** + * Test product save with selected tier price + * + * @dataProvider saveActionTierPriceDataProvider + * @param array $postData + * @param array $tierPrice + * @magentoDataFixture Magento/Catalog/_files/product_has_tier_price_show_as_low_as.php + * @magentoAppIsolation enabled + * @magentoConfigFixture current_store catalog/price/scope 1 + * @magentoDbIsolation disabled + */ + public function testSaveActionTierPrice(array $postData, array $tierPrice) + { + $postData['product'] = $this->getProductData($tierPrice); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($postData); + $this->dispatch('backend/catalog/product/save/id/' . $postData['id']); + $this->assertSessionMessages( + $this->contains('You saved the product.'), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Provide test data for testSaveActionWithAlreadyExistingUrlKey(). + * + * @return array + */ + public function saveActionTierPriceDataProvider() + { + return [ + [ + 'post_data' => [ + 'id' => '1', + 'type' => 'simple', + 'store' => '0', + 'set' => '4', + 'back' => 'edit', + 'product' => [], + 'is_downloadable' => '0', + 'affect_configurable_product_attributes' => '1', + 'new_variation_attribute_set_id' => '4', + 'use_default' => [ + 'gift_message_available' => '0', + 'gift_wrapping_available' => '0' + ], + 'configurable_matrix_serialized' => '[]', + 'associated_product_ids_serialized' => '[]' + ], + 'tier_price_for_request' => [ + [ + 'price_id' => '1', + 'website_id' => '0', + 'cust_group' => '32000', + 'price' => '111.00', + 'price_qty' => '100', + 'website_price' => '111.0000', + 'initialize' => 'true', + 'record_id' => '1', + 'value_type' => 'fixed' + ], + [ + 'price_id' => '2', + 'website_id' => '1', + 'cust_group' => '32000', + 'price' => '222.00', + 'price_qty' => '200', + 'website_price' => '111.0000', + 'initialize' => 'true', + 'record_id' => '2', + 'value_type' => 'fixed' + ], + [ + 'price_id' => '3', + 'website_id' => '1', + 'cust_group' => '32000', + 'price' => '333.00', + 'price_qty' => '300', + 'website_price' => '111.0000', + 'initialize' => 'true', + 'record_id' => '3', + 'value_type' => 'fixed' + ] + ] + ] + ]; + } + + /** + * Return product data for test without entity_id for further save + * + * @param array $tierPrice + * @return array + */ + private function getProductData(array $tierPrice) + { + $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + $product = $repository->get('tier_prices')->getData(); + $product['tier_price'] = $tierPrice; + unset($product['entity_id']); + return $product; + } } From c8eb11567542d5e8002180801153fa19f6724336 Mon Sep 17 00:00:00 2001 From: David Verholen <david@verholen.com> Date: Fri, 26 Oct 2018 12:42:07 +0200 Subject: [PATCH 122/310] GraphQL-43: introduce wishlist graphql module --- .../WishlistItemsProductsResolver.php | 58 ++++++++++++++++++ .../Model/Resolver/WishlistItemsResolver.php | 60 +++++++++++++++++++ .../Model/Resolver/WishlistResolver.php | 55 +++++++++++++++++ .../Model/WishlistDataProvider.php | 39 ++++++++++++ .../Model/WishlistItemsDataProvider.php | 52 ++++++++++++++++ .../WishlistItemsProductDataProvider.php | 32 ++++++++++ app/code/Magento/WishlistGraphQl/README.md | 4 ++ .../Magento/WishlistGraphQl/composer.json | 25 ++++++++ .../Magento/WishlistGraphQl/etc/module.xml | 10 ++++ .../WishlistGraphQl/etc/schema.graphqls | 20 +++++++ .../Magento/WishlistGraphQl/registration.php | 9 +++ composer.json | 1 + 12 files changed, 365 insertions(+) create mode 100644 app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php create mode 100644 app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php create mode 100644 app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php create mode 100644 app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php create mode 100644 app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php create mode 100644 app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php create mode 100644 app/code/Magento/WishlistGraphQl/README.md create mode 100644 app/code/Magento/WishlistGraphQl/composer.json create mode 100644 app/code/Magento/WishlistGraphQl/etc/module.xml create mode 100644 app/code/Magento/WishlistGraphQl/etc/schema.graphqls create mode 100644 app/code/Magento/WishlistGraphQl/registration.php diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php new file mode 100644 index 0000000000000..567e85c3c0309 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php @@ -0,0 +1,58 @@ +<?php +declare(strict_types=1); +/** + * WishlistItemsProductsResolver + * + * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. + * @author david.verholen@brandung.de + */ + +namespace Magento\WishlistGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\WishlistGraphQl\Model\WishlistItemsProductDataProvider; + +class WishlistItemsProductsResolver implements ResolverInterface +{ + /** + * @var WishlistItemsProductDataProvider + */ + private $productDataProvider; + + public function __construct(WishlistItemsProductDataProvider $productDataProvider) + { + $this->productDataProvider = $productDataProvider; + } + + + /** + * Fetches the data from persistence models and format it according to the GraphQL schema. + * + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return mixed|Value + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['product_id'])) { + throw new GraphQlInputException( + __('Missing key %1 in wishlist item data', ['product_id']) + ); + } + return $this->productDataProvider->getProductDataById($value['product_id']); + } +} diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php new file mode 100644 index 0000000000000..3f51db6eef3e1 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php @@ -0,0 +1,60 @@ +<?php +declare(strict_types=1); +/** + * WishlistItemTypeResolver + * + * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. + * @author david.verholen@brandung.de + */ + +namespace Magento\WishlistGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Wishlist\Model\Item; +use Magento\WishlistGraphQl\Model\WishlistItemsDataProvider; + +class WishlistItemsResolver implements ResolverInterface +{ + /** + * @var WishlistItemsDataProvider + */ + private $wishlistItemsDataProvider; + + public function __construct(WishlistItemsDataProvider $wishlistItemsDataProvider) + { + $this->wishlistItemsDataProvider = $wishlistItemsDataProvider; + } + + /** + * Fetches the data from persistence models and format it according to the GraphQL schema. + * + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return mixed|Value + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + return array_map(function (Item $wishlistItem) { + return [ + 'id' => $wishlistItem->getId(), + 'qty' => $wishlistItem->getData('qty'), + 'description' => (string)$wishlistItem->getDescription(), + 'added_at' => $wishlistItem->getAddedAt(), + 'product_id' => (int)$wishlistItem->getProductId() + ]; + }, $this->wishlistItemsDataProvider->getWishlistItemsForCustomer($context->getUserId())); + } +} diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php new file mode 100644 index 0000000000000..3aec792b9de54 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php @@ -0,0 +1,55 @@ +<?php +declare(strict_types=1); +/** + * WishlistItemTypeResolver + * + * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. + * @author david.verholen@brandung.de + */ + +namespace Magento\WishlistGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\WishlistGraphQl\Model\WishlistDataProvider; + +class WishlistResolver implements ResolverInterface +{ + /** + * @var WishlistDataProvider + */ + private $wishlistDataProvider; + + public function __construct(WishlistDataProvider $wishlistDataProvider) + { + $this->wishlistDataProvider = $wishlistDataProvider; + } + + /** + * Fetches the data from persistence models and format it according to the GraphQL schema. + * + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return mixed|Value + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $wishlist = $this->wishlistDataProvider->getWishlistForCustomer($context->getUserId()); + return [ + 'sharing_code' => $wishlist->getSharingCode(), + 'updated_at' => $wishlist->getUpdatedAt() + ]; + } +} diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php b/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php new file mode 100644 index 0000000000000..8f472d610ff4c --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php @@ -0,0 +1,39 @@ +<?php +declare(strict_types=1); +/** + * WishlistItemTypeResolver + * + * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. + * @author david.verholen@brandung.de + */ + +namespace Magento\WishlistGraphQl\Model; + +use Magento\Wishlist\Model\ResourceModel\Wishlist; +use Magento\Wishlist\Model\WishlistFactory; + +class WishlistDataProvider +{ + /** + * @var Wishlist + */ + private $wishlistResource; + /** + * @var WishlistFactory + */ + private $wishlistFactory; + + public function __construct(Wishlist $wishlistResource, WishlistFactory $wishlistFactory) + { + $this->wishlistResource = $wishlistResource; + $this->wishlistFactory = $wishlistFactory; + } + + public function getWishlistForCustomer(int $customerId): \Magento\Wishlist\Model\Wishlist + { + /** @var \Magento\Wishlist\Model\Wishlist $wishlist */ + $wishlist = $this->wishlistFactory->create(); + $this->wishlistResource->load($wishlist, $customerId, 'customer_id'); + return $wishlist; + } +} diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php new file mode 100644 index 0000000000000..e339b4886a942 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php @@ -0,0 +1,52 @@ +<?php +declare(strict_types=1); +/** + * WishlistItemTypeResolver + * + * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. + * @author david.verholen@brandung.de + */ + +namespace Magento\WishlistGraphQl\Model; + +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\ResourceModel\Item\CollectionFactory as WishlistItemCollectionFactory; + +class WishlistItemsDataProvider +{ + + /** + * @var WishlistItemCollectionFactory + */ + private $wishlistItemCollectionFactory; + /** + * @var StoreManagerInterface + */ + private $storeManager; + + public function __construct( + WishlistItemCollectionFactory $wishlistItemCollectionFactory, + StoreManagerInterface $storeManager + ) { + $this->wishlistItemCollectionFactory = $wishlistItemCollectionFactory; + $this->storeManager = $storeManager; + } + + /** + * @param int $customerId + * @return Item[] + */ + public function getWishlistItemsForCustomer(int $customerId): array + { + $wishlistItemCollection = $this->wishlistItemCollectionFactory->create(); + $wishlistItemCollection->addCustomerIdFilter($customerId); + $wishlistItemCollection->addStoreFilter(array_map(function (StoreInterface $store) { + return $store->getId(); + }, $this->storeManager->getStores())); + $wishlistItemCollection->setVisibilityFilter(); + $wishlistItemCollection->load(); + return $wishlistItemCollection->getItems(); + } +} diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php new file mode 100644 index 0000000000000..a9aa5dc184397 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php @@ -0,0 +1,32 @@ +<?php +declare(strict_types=1); +/** + * WishlistItemsProductsResolver + * + * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. + * @author david.verholen@brandung.de + */ + +namespace Magento\WishlistGraphQl\Model; + +use Magento\Catalog\Api\ProductRepositoryInterface; + +class WishlistItemsProductDataProvider +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + public function __construct(ProductRepositoryInterface $productRepository) + { + $this->productRepository = $productRepository; + } + + public function getProductDataById(int $productId) { + $product = $this->productRepository->getById($productId); + $productData = $product->toArray(); + $productData['model'] = $product; + return $productData; + } +} diff --git a/app/code/Magento/WishlistGraphQl/README.md b/app/code/Magento/WishlistGraphQl/README.md new file mode 100644 index 0000000000000..9121593e6a759 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/README.md @@ -0,0 +1,4 @@ +# WishlistGraphQl + +**WishlistGraphQl** provides type information for the GraphQl module +to generate wishlist fields. diff --git a/app/code/Magento/WishlistGraphQl/composer.json b/app/code/Magento/WishlistGraphQl/composer.json new file mode 100644 index 0000000000000..2be27d4239f73 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/composer.json @@ -0,0 +1,25 @@ +{ + "name": "magento/module-wishlist-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-catalog": "*" + }, + "suggest": { + "magento/module-wishlist": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\WishlistGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/WishlistGraphQl/etc/module.xml b/app/code/Magento/WishlistGraphQl/etc/module.xml new file mode 100644 index 0000000000000..337623cc85a92 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/etc/module.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_WishlistGraphQl" /> +</config> diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..5c6d701138560 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -0,0 +1,20 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +type Query { + wishlist: WishlistOutput @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistResolver") @doc(description: "todo") +} + +type WishlistOutput { + items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "todo"), + sharing_code: String! @doc(description: "todo"), + updated_at: String! @doc(description: "todo") +} + +type WishlistItem { + id: Int! @doc(description: "todo") + qty: Float! @doc(description: "todo"), + description: String! @doc(description: "todo"), + added_at: String! @doc(description: "todo"), + product: ProductInterface @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsProductsResolver") +} \ No newline at end of file diff --git a/app/code/Magento/WishlistGraphQl/registration.php b/app/code/Magento/WishlistGraphQl/registration.php new file mode 100644 index 0000000000000..f2047f225e5b6 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_WishlistGraphQl', __DIR__); diff --git a/composer.json b/composer.json index 3f8f0a033c893..eede85d273777 100644 --- a/composer.json +++ b/composer.json @@ -237,6 +237,7 @@ "magento/module-weee": "*", "magento/module-widget": "*", "magento/module-wishlist": "*", + "magento/module-wishlist-graph-ql": "*", "magento/module-wishlist-analytics": "*", "magento/theme-adminhtml-backend": "*", "magento/theme-frontend-blank": "*", From 0286f170c6e4c74d2e68a11c3de5f588710143fe Mon Sep 17 00:00:00 2001 From: David Verholen <david@verholen.com> Date: Fri, 26 Oct 2018 13:35:10 +0200 Subject: [PATCH 123/310] GraphQL-43: add missing hard dependencies --- app/code/Magento/WishlistGraphQl/composer.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/WishlistGraphQl/composer.json b/app/code/Magento/WishlistGraphQl/composer.json index 2be27d4239f73..9226ea1754e6a 100644 --- a/app/code/Magento/WishlistGraphQl/composer.json +++ b/app/code/Magento/WishlistGraphQl/composer.json @@ -5,10 +5,9 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-catalog": "*" - }, - "suggest": { - "magento/module-wishlist": "*" + "magento/module-catalog": "*", + "magento/module-wishlist": "*", + "magento/module-store": "*" }, "license": [ "OSL-3.0", From 2e4ea72289c28a223354b18b399e28843267e538 Mon Sep 17 00:00:00 2001 From: David Verholen <david@verholen.com> Date: Fri, 26 Oct 2018 14:14:25 +0200 Subject: [PATCH 124/310] GraphQL-43: add test to retrieve wishlist --- .../Magento/GraphQl/Wishlist/WishlistTest.php | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php new file mode 100644 index 0000000000000..53577d02df50f --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Wishlist; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Wishlist\Model\Item; + +class WishlistTest extends GraphQlAbstract +{ + /** + * @var \Magento\TestFramework\ObjectManager + */ + private $objectManager; + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * Verify the fields of CMS Block selected by identifiers + * + * @magentoApiDataFixture Magento/Wishlist/_files/wishlist.php + * @throws \Magento\Framework\Exception\AuthenticationException + * @throws \Exception + */ + public function testGetCustomersWishlist(): void + { + /** @var \Magento\Wishlist\Model\Wishlist $wishlist */ + $wishlist = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Wishlist\Model\Wishlist::class + ); + $wishlist->loadByCustomerId(1, true); + /** @var Item $wishlistItem */ + $wishlistItem = $wishlist->getItemCollection()->getFirstItem(); + $wishlistItemProduct = $wishlistItem->getProduct(); + $query = + <<<QUERY +{ + wishlist { + items { + id + qty + product { + sku + name + } + description + added_at + } + sharing_code + updated_at + } +} +QUERY; + + $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders('customer@example.com', 'password')); + $this->assertEquals($wishlist->getSharingCode(), $response['wishlist']['sharing_code']); + $this->assertEquals($wishlistItem->getData('qty'), $response['wishlist']['items'][0]['qty']); + $this->assertEquals($wishlistItem->getDescription(), $response['wishlist']['items'][0]['description']); + $this->assertEquals($wishlistItemProduct->getSku(), $response['wishlist']['items'][0]['product']['sku']); + $this->assertEquals($wishlistItemProduct->getName(), $response['wishlist']['items'][0]['product']['name']); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } +} From 89eb2776be3bdacf84a86367d866b8746452f129 Mon Sep 17 00:00:00 2001 From: David Verholen <david@verholen.com> Date: Fri, 26 Oct 2018 14:19:15 +0200 Subject: [PATCH 125/310] GraphQL-43: use correct copyright headers in introduced php classes --- .../Model/Resolver/WishlistItemsProductsResolver.php | 8 +++----- .../Model/Resolver/WishlistItemsResolver.php | 8 +++----- .../WishlistGraphQl/Model/Resolver/WishlistResolver.php | 8 +++----- .../WishlistGraphQl/Model/WishlistDataProvider.php | 8 +++----- .../WishlistGraphQl/Model/WishlistItemsDataProvider.php | 8 +++----- .../Model/WishlistItemsProductDataProvider.php | 8 +++----- 6 files changed, 18 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php index 567e85c3c0309..26ace0e849e3f 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php @@ -1,11 +1,9 @@ <?php -declare(strict_types=1); /** - * WishlistItemsProductsResolver - * - * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. - * @author david.verholen@brandung.de + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\WishlistGraphQl\Model\Resolver; diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php index 3f51db6eef3e1..707618b10602d 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php @@ -1,11 +1,9 @@ <?php -declare(strict_types=1); /** - * WishlistItemTypeResolver - * - * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. - * @author david.verholen@brandung.de + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\WishlistGraphQl\Model\Resolver; diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php index 3aec792b9de54..ba1a6e935c10e 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php @@ -1,11 +1,9 @@ <?php -declare(strict_types=1); /** - * WishlistItemTypeResolver - * - * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. - * @author david.verholen@brandung.de + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\WishlistGraphQl\Model\Resolver; diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php b/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php index 8f472d610ff4c..a62ddebd91120 100644 --- a/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php +++ b/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php @@ -1,11 +1,9 @@ <?php -declare(strict_types=1); /** - * WishlistItemTypeResolver - * - * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. - * @author david.verholen@brandung.de + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\WishlistGraphQl\Model; diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php index e339b4886a942..ca87b23460fc7 100644 --- a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php +++ b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php @@ -1,11 +1,9 @@ <?php -declare(strict_types=1); /** - * WishlistItemTypeResolver - * - * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. - * @author david.verholen@brandung.de + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\WishlistGraphQl\Model; diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php index a9aa5dc184397..76df065a80702 100644 --- a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php +++ b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php @@ -1,11 +1,9 @@ <?php -declare(strict_types=1); /** - * WishlistItemsProductsResolver - * - * @copyright Copyright © 2018 brandung GmbH & Co. KG. All rights reserved. - * @author david.verholen@brandung.de + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\WishlistGraphQl\Model; From 3f5bcdd911b1d7b9604362f078bcfd9b7baa9d3f Mon Sep 17 00:00:00 2001 From: matt <matt@dforce3000.de> Date: Fri, 26 Oct 2018 15:01:27 +0200 Subject: [PATCH 126/310] magento2#222 Apply changes from CR for PR 162 --- .../Model/Customer/UpdateAccountInformation.php | 2 +- .../Model/Resolver/ChangePassword.php | 6 +++--- .../Model/Resolver/GenerateCustomerToken.php | 4 ++-- .../Model/Resolver/RevokeCustomerToken.php | 2 +- app/code/Magento/CustomerGraphQl/etc/schema.graphqls | 12 ++++++++---- .../Exception/GraphQlAlreadyExistsException.php | 2 +- .../Exception/GraphQlAuthenticationException.php | 2 +- .../Exception/GraphQlAuthorizationException.php | 2 +- .../Exception/GraphQlNoSuchEntityException.php | 2 +- 9 files changed, 19 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php index d235d51b5e52d..36365f1910f27 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateAccountInformation.php @@ -73,7 +73,7 @@ public function execute(int $customerId, array $data): void if (isset($data['email']) && $customer->getEmail() !== $data['email']) { if (!isset($data['password']) || empty($data['password'])) { - throw new GraphQlInputException(__('For changing "email" you should provide current "password".')); + throw new GraphQlInputException(__('Provide the current "password" to change "email".')); } $this->checkCustomerPassword->execute($data['password'], $customerId); diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php index 98b0975b37169..78fa852a7ac59 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php @@ -17,7 +17,7 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; /** - * @inheritdoc + * Change customer password resolver */ class ChangePassword implements ResolverInterface { @@ -70,11 +70,11 @@ public function resolve( array $args = null ) { if (!isset($args['currentPassword'])) { - throw new GraphQlInputException(__('"currentPassword" value should be specified')); + throw new GraphQlInputException(__('Specify the "currentPassword" value.')); } if (!isset($args['newPassword'])) { - throw new GraphQlInputException(__('"newPassword" value should be specified')); + throw new GraphQlInputException(__('Specify the "newPassword" value.')); } $currentUserId = $context->getUserId(); diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php index 9719d048f606b..1bd77fe1cde8f 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/GenerateCustomerToken.php @@ -45,11 +45,11 @@ public function resolve( array $args = null ) { if (!isset($args['email'])) { - throw new GraphQlInputException(__('"email" value should be specified')); + throw new GraphQlInputException(__('Specify the "email" value.')); } if (!isset($args['password'])) { - throw new GraphQlInputException(__('"password" value should be specified')); + throw new GraphQlInputException(__('Specify the "password" value.')); } try { diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php index d3b16c05a6492..3301162dc0088 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php @@ -55,6 +55,6 @@ public function resolve( $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - return $this->customerTokenService->revokeCustomerAccessToken((int)$currentUserId); + return ['result' => $this->customerTokenService->revokeCustomerAccessToken((int)$currentUserId)]; } } diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index b8411f00c5cb1..ca4ac6cc245a7 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -6,10 +6,10 @@ type Query { } type Mutation { - generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\GenerateCustomerToken") @doc(description:"Retrieve Customer token") - changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ChangePassword") @doc(description:"Changes password for logged in customer") - updateCustomer (input: UpdateCustomerInput): UpdateCustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomer") @doc(description:"Update customer personal information") - revokeCustomerToken: Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RevokeCustomerToken") @doc(description:"Revoke Customer token") + generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\GenerateCustomerToken") @doc(description:"Retrieve the customer token") + changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ChangePassword") @doc(description:"Changes the password for the logged-in customer") + updateCustomer (input: UpdateCustomerInput!): UpdateCustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomer") @doc(description:"Update the customer's personal information") + revokeCustomerToken: RevokeCustomerTokenOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RevokeCustomerToken") @doc(description:"Revoke the customer token") } type CustomerToken { @@ -28,6 +28,10 @@ type UpdateCustomerOutput { customer: Customer! } +type RevokeCustomerTokenOutput { + result: Boolean! +} + type Customer @doc(description: "Customer defines the customer name and address and other details") { created_at: String @doc(description: "Timestamp indicating when the account was created") group_id: Int @doc(description: "The group assigned to the user. Default values are 0 (Not logged in), 1 (General), 2 (Wholesale), and 3 (Retailer)") diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAlreadyExistsException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAlreadyExistsException.php index d25707b2ca347..8275219e9e554 100644 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAlreadyExistsException.php +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAlreadyExistsException.php @@ -12,7 +12,7 @@ use Magento\Framework\Phrase; /** - * Class GraphQlAlreadyExistsException + * Exception for GraphQL to be thrown when data already exists */ class GraphQlAlreadyExistsException extends AlreadyExistsException implements ClientAware { diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthenticationException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthenticationException.php index 4df74bbf332cd..44c3d07bd186f 100644 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthenticationException.php +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthenticationException.php @@ -12,7 +12,7 @@ use Magento\Framework\Phrase; /** - * Class GraphQlAuthenticationException + * Exception for GraphQL to be thrown when authentication fails */ class GraphQlAuthenticationException extends AuthenticationException implements ClientAware { diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthorizationException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthorizationException.php index 5b76e0ab9f5ae..f71652edc63b3 100644 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthorizationException.php +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthorizationException.php @@ -11,7 +11,7 @@ use Magento\Framework\Exception\AuthorizationException; /** - * Class GraphQlAuthorizationException + * Exception for GraphQL to be thrown when authorization fails */ class GraphQlAuthorizationException extends AuthorizationException implements \GraphQL\Error\ClientAware { diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlNoSuchEntityException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlNoSuchEntityException.php index 05c5925e8d1e5..4bd24d6cfd4a7 100644 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlNoSuchEntityException.php +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlNoSuchEntityException.php @@ -11,7 +11,7 @@ use Magento\Framework\Phrase; /** - * Class GraphQlNoSuchEntityException + * Exception for GraphQL to be thrown when entity does not exists */ class GraphQlNoSuchEntityException extends NoSuchEntityException implements \GraphQL\Error\ClientAware { From dfc9cd0a5ce6d9285b56b40f9cc4c429f8d5f33c Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 26 Oct 2018 17:23:09 +0300 Subject: [PATCH 127/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml index b10c8e5ad54a0..1cd0e15780c11 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml @@ -161,7 +161,7 @@ <!-- Go to the product page and see the uploaded image --> <amOnPage url="$$secondProduct.name$$.html" stepKey="goToStorefront2"/> <waitForPageLoad stepKey="waitForStorefront2"/> - <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('Large')}}" stepKey="seeLarge2"/> + <seeElementInDOM selector="{{StorefrontProductMediaSection.imageFile('large')}}" stepKey="seeLarge2"/> </test> <test name="AdminSimpleProductRemoveImagesTest"> From 505ac52e2a242555873545faced2eef0a275fe4e Mon Sep 17 00:00:00 2001 From: Tobias Maile <tobias.maile@brandung.de> Date: Fri, 26 Oct 2018 16:55:02 +0200 Subject: [PATCH 128/310] GraphQL-202: [Mutations] adds sendEmailFriend Logic --- .../Model/Resolver/SendEmailToFriend.php | 147 ++++++++++++++++++ .../Magento/SendFriendGraphQl/etc/module.xml | 11 ++ .../SendFriendGraphQl/etc/schema.graphqls | 33 ++++ .../SendFriendGraphQl/registration.php | 9 ++ 4 files changed, 200 insertions(+) create mode 100644 app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php create mode 100644 app/code/Magento/SendFriendGraphQl/etc/module.xml create mode 100644 app/code/Magento/SendFriendGraphQl/etc/schema.graphqls create mode 100644 app/code/Magento/SendFriendGraphQl/registration.php diff --git a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php new file mode 100644 index 0000000000000..56f6d20b20f18 --- /dev/null +++ b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriendGraphQl\Model\Resolver; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\DataObjectFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\SendFriend\Model\SendFriend; + +class SendEmailToFriend implements ResolverInterface +{ + /** + * @var SendFriend + */ + private $sendFriend; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** + * @var DataObjectFactory + */ + private $dataObjectFactory; + + public function __construct( + SendFriend $sendFriend, + ProductRepositoryInterface $productRepository, + DataObjectFactory $dataObjectFactory + ) { + $this->sendFriend = $sendFriend; + $this->productRepository = $productRepository; + $this->dataObjectFactory = $dataObjectFactory; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if ($this->sendFriend->getMaxSendsToFriend() && $this->sendFriend->isExceedLimit()) { + throw new GraphQlInputException(__('You can\'t send messages more than %1 times an hour.', + $this->sendFriend->getMaxSendsToFriend() + )); + } + + $product = $this->getProductFromRepository($args['input']['params']['product_id']); + $senderArray = $this->getSenderArrayFromArgs($args); + $recipientsArray = $this->getRecipientsArray($args); + //@todo clarify if event should be dispatched + //$this->_eventManager->dispatch('sendfriend_product', ['product' => $product]); + $this->sendFriend->setSender($senderArray); + $this->sendFriend->setRecipients($recipientsArray); + $this->sendFriend->setProduct($product); + + $this->prepareDataForValidation($args, $recipientsArray); + $validationResult = $this->sendFriend->validate(); + $this->addRecipientNameValidation($args); + if ($validationResult !== true) { + throw new GraphQlInputException(__(implode($validationResult))); + } + + $this->sendFriend->send(); + + return array_merge($senderArray, $recipientsArray); + } + + private function prepareDataForValidation(array $args, array $recipientsArray): void + { + $sender = $this->dataObjectFactory->create()->setData([ + 'name' => $args['input']['sender']['name'], + 'email'=> $args['input']['sender']['email'], + 'message' => $args['input']['sender']['message'], + ]); + $emails = []; + foreach ($recipientsArray['recipients'] as $recipient) { + $emails[] = $recipient['email']; + } + $recipients = $this->dataObjectFactory->create()->setData('emails', $emails); + + $this->sendFriend->setData('_sender', $sender); + $this->sendFriend->setData('_recipients', $recipients); + } + + /** + * @param array $args + * @throws GraphQlInputException + */ + private function addRecipientNameValidation(array $args): void + { + foreach ($args['input']['recipients'] as $recipient) { + if (empty($recipient['name'])) { + throw new GraphQlInputException( + __('Please Provide Name for Recipient with this Email Address: ' . $recipient['email'] + )); + } + } + } + + /** + * @param int $productId + * @return bool|\Magento\Catalog\Api\Data\ProductInterface + */ + private function getProductFromRepository(int $productId) + { + try { + $product = $this->productRepository->getById($productId); + if (!$product->isVisibleInCatalog()) { + return false; + } + } catch (NoSuchEntityException $noEntityException) { + return false; + } + + return $product; + } + + private function getRecipientsArray(array $args): array + { + $recipientsArray = []; + foreach ($args['input']['recipients'] as $recipient) { + $recipientsArray[] = [ + 'name' => $recipient['name'], + 'email' => $recipient['email'], + ]; + } + return ['recipients' => $recipientsArray]; + } + + private function getSenderArrayFromArgs(array $args): array + { + return ['sender' => [ + 'name' => $args['input']['sender']['name'], + 'email' => $args['input']['sender']['email'], + 'message' => $args['input']['sender']['message'], + ] + ]; + } +} diff --git a/app/code/Magento/SendFriendGraphQl/etc/module.xml b/app/code/Magento/SendFriendGraphQl/etc/module.xml new file mode 100644 index 0000000000000..14d792198cd5f --- /dev/null +++ b/app/code/Magento/SendFriendGraphQl/etc/module.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_SendFriendGraphGl"/> +</config> diff --git a/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls b/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..87bf90fd10072 --- /dev/null +++ b/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls @@ -0,0 +1,33 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +type Mutation { + sendEmailToFriend (input: SendEmailToFriendSenderInput): SendEmailToFriendOutput @resolver(class: "\\Magento\\SendFriendGraphQl\\Model\\Resolver\\SendEmailToFriend") @doc(description:"@todo") +} + +input SendEmailToFriendSenderInput { + sender: Sender! + params: Params! + recipients: [Recipient] +} + +type Sender { + name: String! + email: String! + message: String! +} +#@todo Prams can be removed if dispatching of event in app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php not needed +type Params { + product_id: Int! +} + +type Recipient { + name: String! + email: String! +} + + +type SendEmailToFriendOutput { + sender: Sender + recipients: [Recipient] +} diff --git a/app/code/Magento/SendFriendGraphQl/registration.php b/app/code/Magento/SendFriendGraphQl/registration.php new file mode 100644 index 0000000000000..c41607a0dc864 --- /dev/null +++ b/app/code/Magento/SendFriendGraphQl/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SendFriendGraphGl', __DIR__); From e3df0f684ac20c478685cc601590e5231a531eb1 Mon Sep 17 00:00:00 2001 From: Max Almonte <maxalmonte14@hotmail.com> Date: Fri, 26 Oct 2018 13:12:37 -0400 Subject: [PATCH 129/310] Fixed miss called property in getStoreIdByCode method --- .../BundleImportExport/Model/Import/Product/Type/Bundle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index 3ed7e144ddd5a..77d331a8b5696 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -779,7 +779,7 @@ protected function clear() */ private function getStoreIdByCode(string $storeCode): int { - if (!isset($this->storeIdToCode[$storeCode])) { + if (!isset($this->storeCodeToId[$storeCode])) { /** @var $store \Magento\Store\Model\Store */ foreach ($this->storeManager->getStores() as $store) { $this->storeCodeToId[$store->getCode()] = $store->getId(); From 4a283722cdb25ef030d8100bc20c3fa16c2e3d91 Mon Sep 17 00:00:00 2001 From: Artem Klimov <art.klimoff@gmail.com> Date: Sat, 27 Oct 2018 19:36:06 +0300 Subject: [PATCH 130/310] resolve magento 2.3 update conflicts --- lib/internal/Magento/Framework/GraphQl/Config.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/GraphQl/Config.php b/lib/internal/Magento/Framework/GraphQl/Config.php index bfd8f330c6123..481d75ed35893 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config.php +++ b/lib/internal/Magento/Framework/GraphQl/Config.php @@ -10,6 +10,7 @@ use Magento\Framework\Config\DataInterface; use Magento\Framework\GraphQl\Config\ConfigElementFactoryInterface; use Magento\Framework\GraphQl\Config\ConfigElementInterface; +use Magento\Framework\GraphQl\Query\Fields as QueryFields; /** * Provides access to typing information for a configured GraphQL schema. From b8d2663b72438fdda17dcd793c349681a5c784c6 Mon Sep 17 00:00:00 2001 From: Artem Klimov <art.klimoff@gmail.com> Date: Sat, 27 Oct 2018 19:45:50 +0300 Subject: [PATCH 131/310] resolve magento 2.3 update conflicts --- lib/internal/Magento/Framework/GraphQl/Config.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/GraphQl/Config.php b/lib/internal/Magento/Framework/GraphQl/Config.php index 481d75ed35893..75b6c64e9d24f 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config.php +++ b/lib/internal/Magento/Framework/GraphQl/Config.php @@ -65,10 +65,12 @@ public function getConfigElement(string $configElementName) : ConfigElementInter } $fieldsInQuery = $this->queryFields->getFieldsUsedInQuery(); - if (isset($data['fields']) && !empty($fieldsInQuery)) { - foreach ($data['fields'] as $fieldName => $fieldConfig) { - if (!isset($fieldsInQuery[$fieldName])) { - unset($data['fields'][$fieldName]); + if (isset($data['fields'])) { + if (!empty($fieldsInQuery)) { + foreach ($data['fields'] as $fieldName => $fieldConfig) { + if (!isset($fieldsInQuery[$fieldName])) { + unset($data['fields'][$fieldName]); + } } } From dba69444975fab128a0764e4516af29331624e68 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <mitry@atwix.com> Date: Sun, 28 Oct 2018 11:42:31 +0200 Subject: [PATCH 132/310] Add missing unit test for WishlistSettings plugin --- .../Ui/DataProvider/WishlistSettingsTest.php | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 app/code/Magento/Wishlist/Test/Unit/Plugin/Ui/DataProvider/WishlistSettingsTest.php diff --git a/app/code/Magento/Wishlist/Test/Unit/Plugin/Ui/DataProvider/WishlistSettingsTest.php b/app/code/Magento/Wishlist/Test/Unit/Plugin/Ui/DataProvider/WishlistSettingsTest.php new file mode 100644 index 0000000000000..aa3b956e12153 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Unit/Plugin/Ui/DataProvider/WishlistSettingsTest.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Test\Unit\Plugin\Ui\DataProvider; + +use Magento\Catalog\Ui\DataProvider\Product\Listing\DataProvider; +use Magento\Wishlist\Helper\Data; +use Magento\Wishlist\Plugin\Ui\DataProvider\WishlistSettings; + +/** + * Covers \Magento\Wishlist\Plugin\Ui\DataProvider\WishlistSettings + */ +class WishlistSettingsTest extends \PHPUnit\Framework\TestCase +{ + /** + * Testable Object + * + * @var WishlistSettings + */ + private $wishlistSettings; + + /** + * @var Data|\PHPUnit_Framework_MockObject_MockObject + */ + private $helperMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->helperMock = $this->createMock(Data::class); + $this->wishlistSettings = new WishlistSettings($this->helperMock); + } + + /** + * Test afterGetData method + * + * @return void + */ + public function testAfterGetData() + { + /** @var DataProvider|\PHPUnit_Framework_MockObject_MockObject $subjectMock */ + $subjectMock = $this->createMock(DataProvider::class); + $result = []; + $isAllow = true; + $this->helperMock->expects($this->once())->method('isAllow')->willReturn(true); + + $expected = ['allowWishlist' => $isAllow]; + $actual = $this->wishlistSettings->afterGetData($subjectMock, $result); + self::assertEquals($expected, $actual); + } +} From b552406f8e1bd7cc5b33b37bdc4e8b757883e0de Mon Sep 17 00:00:00 2001 From: David Manners <dmanners87@gmail.com> Date: Mon, 29 Oct 2018 16:45:18 +0000 Subject: [PATCH 133/310] magento-engcom/import-export-improvements#134: update file from phpcs feedback --- .../BundleImportExport/Model/Import/Product/Type/Bundle.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php index 77d331a8b5696..81a47d72602b7 100644 --- a/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php +++ b/app/code/Magento/BundleImportExport/Model/Import/Product/Type/Bundle.php @@ -20,6 +20,7 @@ /** * Class Bundle + * * @package Magento\BundleImportExport\Model\Import\Product\Type * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ @@ -349,6 +350,8 @@ protected function populateSelectionTemplate($selection, $optionId, $parentId, $ } /** + * Deprecated method for retrieving mapping between skus and products. + * * @deprecated Misspelled method * @see retrieveProductsByCachedSkus */ @@ -600,6 +603,7 @@ protected function insertOptions() /** * Populate array for insert option values + * * @param array $optionIds * @return array */ From 9043bc9c755268c63db304a499492867d4c91379 Mon Sep 17 00:00:00 2001 From: Matei Purcaru <matei.purcaru@gmail.com> Date: Mon, 29 Oct 2018 19:59:00 +0200 Subject: [PATCH 134/310] magento/graphql-ce#41: Updated global composer.json --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 3f8f0a033c893..ab29199251172 100644 --- a/composer.json +++ b/composer.json @@ -201,6 +201,7 @@ "magento/module-rule": "*", "magento/module-sales": "*", "magento/module-sales-analytics": "*", + "magento/module-sales-graph-ql": "*", "magento/module-sales-inventory": "*", "magento/module-sales-rule": "*", "magento/module-sales-sequence": "*", From 3895af04b1ddfcae81717b1bf5373ded7e76c7bf Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Tue, 30 Oct 2018 09:57:26 +0200 Subject: [PATCH 135/310] MAGETWO-95753: [2.3] Cannot save product with Tier Prices --- .../Controller/Adminhtml/ProductTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index d949993e7ea3c..acec996d0c406 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -8,6 +8,8 @@ use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Message\Manager; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Message\MessageInterface; /** * @magentoAppArea adminhtml @@ -24,7 +26,7 @@ public function testSaveActionWithDangerRequest() $this->dispatch('backend/catalog/product/save'); $this->assertSessionMessages( $this->equalTo(['The product was unable to be saved. Please try again.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR + MessageInterface::TYPE_ERROR ); $this->assertRedirect($this->stringContains('/backend/catalog/product/new')); } @@ -44,7 +46,7 @@ public function testSaveActionAndNew() $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/new/')); $this->assertSessionMessages( $this->contains('You saved the product.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); } @@ -71,11 +73,11 @@ public function testSaveActionAndDuplicate() ); $this->assertSessionMessages( $this->contains('You saved the product.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); $this->assertSessionMessages( $this->contains('You duplicated the product.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); } @@ -260,9 +262,7 @@ public function saveActionWithAlreadyExistingUrlKeyDataProvider() * @param array $postData * @param array $tierPrice * @magentoDataFixture Magento/Catalog/_files/product_has_tier_price_show_as_low_as.php - * @magentoAppIsolation enabled * @magentoConfigFixture current_store catalog/price/scope 1 - * @magentoDbIsolation disabled */ public function testSaveActionTierPrice(array $postData, array $tierPrice) { @@ -272,7 +272,7 @@ public function testSaveActionTierPrice(array $postData, array $tierPrice) $this->dispatch('backend/catalog/product/save/id/' . $postData['id']); $this->assertSessionMessages( $this->contains('You saved the product.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); } @@ -349,8 +349,8 @@ public function saveActionTierPriceDataProvider() */ private function getProductData(array $tierPrice) { - $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); - $product = $repository->get('tier_prices')->getData(); + $productRepositoryInterface = $this->_objectManager->get(ProductRepositoryInterface::class); + $product = $productRepositoryInterface->get('tier_prices')->getData(); $product['tier_price'] = $tierPrice; unset($product['entity_id']); return $product; From 42b235827941fb1713a360edcdbeebb0a95a35d4 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 30 Oct 2018 11:45:50 +0100 Subject: [PATCH 136/310] Consolidated cart address information provider --- .../Resolver/Address/AddressDataProvider.php | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php index d401f296cb465..80fe973c4e449 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -10,6 +10,7 @@ use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Model\Quote\Address as QuoteAddress; /** * Class AddressDataProvider @@ -49,22 +50,45 @@ public function getCartAddresses(CartInterface $cart): array if ($shippingAddress) { $shippingData = $this->dataObjectConverter->toFlatArray($shippingAddress, [], AddressInterface::class); $shippingData['address_type'] = 'SHIPPING'; - $shippingData['selected_shipping_method'] = [ - 'code' => $shippingAddress->getShippingMethod(), - 'label' => $shippingAddress->getShippingDescription(), - 'free_shipping' => $shippingAddress->getFreeShipping(), - ]; - $shippingData['items_weight'] = $shippingAddress->getWeight(); - $shippingData['customer_notes'] = $shippingAddress->getCustomerNotes(); - $addressData[] = $shippingData; + $addressData[] = array_merge($shippingData, $this->extractAddressData($shippingAddress)); } if ($billingAddress) { $billingData = $this->dataObjectConverter->toFlatArray($billingAddress, [], AddressInterface::class); $billingData['address_type'] = 'BILLING'; - $addressData[] = $billingData; + $addressData[] = array_merge($billingData, $this->extractAddressData($billingAddress)); } return $addressData; } + + /** + * Extract the necessary address fields from address model + * + * @param QuoteAddress $address + * @return array + */ + private function extractAddressData(QuoteAddress $address): array + { + $addressData = [ + 'country' => [ + 'code' => $address->getCountryId(), + 'label' => $address->getCountry() + ], + 'region' => [ + 'code' => $address->getRegionCode(), + 'label' => $address->getRegion() + ], + 'street' => $address->getStreet(), + 'selected_shipping_method' => [ + 'code' => $address->getShippingMethod(), + 'label' => $address->getShippingDescription(), + 'free_shipping' => $address->getFreeShipping(), + ], + 'items_weight' => $address->getWeight(), + 'customer_notes' => $address->getCustomerNotes() + ]; + + return $addressData; + } } From 1cfa17f2de57c9a095f248d8e9aa81e5fa3ca35b Mon Sep 17 00:00:00 2001 From: Matei Purcaru <matei.purcaru@gmail.com> Date: Tue, 30 Oct 2018 13:24:01 +0200 Subject: [PATCH 137/310] magento/graphql-ce#41: Updated composer.lock hash --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 1d101c8aaaf15..bc2160a2b18bd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d6640ddfd342feceaec44c406c056f57", + "content-hash": "edf8aa5e66649f1a221f027a3600d41e", "packages": [ { "name": "braintree/braintree_php", From 152b0924d652de96bb49a93959bb3b304e2601ba Mon Sep 17 00:00:00 2001 From: Yevhenii Dumskyi <yevhenii.dumskyi@gmail.com> Date: Tue, 30 Oct 2018 16:34:52 +0200 Subject: [PATCH 138/310] Fix Notice and Exception while adding image to product programmatically --- .../Model/Product/Gallery/Processor.php | 24 +++++++++++++++---- composer.json | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php index c6c7fbda7e9ec..225c502830695 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Model\Product\Gallery; +use Magento\Framework\Api\Data\ImageContentInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem\DriverInterface; @@ -183,6 +184,13 @@ public function addImage( $attrCode = $this->getAttribute()->getAttributeCode(); $mediaGalleryData = $product->getData($attrCode); $position = 0; + + $absoluteFilePath = $this->mediaDirectory->getAbsolutePath($file); + $imageMimeType = mime_content_type($absoluteFilePath); + $imageContent = file_get_contents($absoluteFilePath); + $imageBase64 = base64_encode($imageContent); + $imageName = pathinfo($destinationFile, PATHINFO_FILENAME); + if (!is_array($mediaGalleryData)) { $mediaGalleryData = ['images' => []]; } @@ -195,11 +203,19 @@ public function addImage( $position++; $mediaGalleryData['images'][] = [ - 'file' => $fileName, - 'position' => $position, + 'file' => $fileName, + 'position' => $position, + 'label' => '', + 'disabled' => (int)$exclude, 'media_type' => 'image', - 'label' => '', - 'disabled' => (int)$exclude, + 'types' => $mediaAttribute, + 'content' => [ + 'data' => [ + ImageContentInterface::NAME => $imageName, + ImageContentInterface::BASE64_ENCODED_DATA => $imageBase64, + ImageContentInterface::TYPE => $imageMimeType, + ] + ] ]; $product->setData($attrCode, $mediaGalleryData); diff --git a/composer.json b/composer.json index e2a646275d98b..68bf1f1d8a8b0 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "ext-xsl": "*", "ext-zip": "*", "lib-libxml": "*", + "ext-fileinfo": "*", "braintree/braintree_php": "3.35.0", "colinmollenhour/cache-backend-file": "~1.4.1", "colinmollenhour/cache-backend-redis": "1.10.5", From 898d2a507a283c15af63dedf2c837bbba03ba89c Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Tue, 30 Oct 2018 17:52:51 +0300 Subject: [PATCH 139/310] MAGETWO-91650: Translation not working for product alerts - Replacing name with referenceId --- app/code/Magento/ProductAlert/etc/db_schema.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ProductAlert/etc/db_schema.xml b/app/code/Magento/ProductAlert/etc/db_schema.xml index c6dd8db321b54..820a8029e2d95 100644 --- a/app/code/Magento/ProductAlert/etc/db_schema.xml +++ b/app/code/Magento/ProductAlert/etc/db_schema.xml @@ -52,7 +52,7 @@ <index referenceId="PRODUCT_ALERT_PRICE_WEBSITE_ID" indexType="btree"> <column name="website_id"/> </index> - <index name="PRODUCT_ALERT_PRICE_STORE_ID" indexType="btree"> + <index referenceId="PRODUCT_ALERT_PRICE_STORE_ID" indexType="btree"> <column name="store_id"/> </index> </table> @@ -99,7 +99,7 @@ <index referenceId="PRODUCT_ALERT_STOCK_WEBSITE_ID" indexType="btree"> <column name="website_id"/> </index> - <index name="PRODUCT_ALERT_STOCK_STORE_ID" indexType="btree"> + <index referenceId="PRODUCT_ALERT_STOCK_STORE_ID" indexType="btree"> <column name="store_id"/> </index> </table> From 052d0cc0dda65fa2a014cd0c6a3925b2d14c3f12 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Tue, 30 Oct 2018 13:20:37 -0500 Subject: [PATCH 140/310] MAGETWO-95773: Credit memo is created instead of returning error via invoice refund API for Bundle product - add error message for incorrect product items --- .../Item/Validation/CreationQuantityValidator.php | 4 ++++ app/code/Magento/Sales/Model/Order/ItemRepository.php | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php index 0c2adfff80a2b..fdc5e2658b9ed 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php @@ -53,6 +53,10 @@ public function validate($entity) return [__('The creditmemo contains product item that is not part of the original order.')]; } + if ($orderItem->isDummy()) { + return [__('The creditmemo contains incorrect product items.')]; + } + if (!$this->isQtyAvailable($orderItem, $entity->getQty())) { return [__('The quantity to refund must not be greater than the unrefunded quantity.')]; } diff --git a/app/code/Magento/Sales/Model/Order/ItemRepository.php b/app/code/Magento/Sales/Model/Order/ItemRepository.php index 7916eb9db2b80..2ea9831336cc2 100644 --- a/app/code/Magento/Sales/Model/Order/ItemRepository.php +++ b/app/code/Magento/Sales/Model/Order/ItemRepository.php @@ -228,6 +228,12 @@ private function addParentItem(OrderItemInterface $orderItem) { if ($parentId = $orderItem->getParentItemId()) { $orderItem->setParentItem($this->get($parentId)); + } else { + foreach ($orderItem->getOrder()->getAllItems() as $item) { + if($item->getParentItemId() === $orderItem->getItemId()) { + $item->setParentItem($orderItem); + } + } } } From 54f4802d5a9ef58418e395e8d12ab4931907d6eb Mon Sep 17 00:00:00 2001 From: larsroettig <l.roettig@techdivision.com> Date: Tue, 30 Oct 2018 21:32:40 +0100 Subject: [PATCH 141/310] #18956 Fixes for set root_category_id --- .../Magento/Store/Model/Config/Importer/Processor/Create.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php index 513ba04802985..3142955ac5988 100644 --- a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php +++ b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php @@ -177,8 +177,8 @@ private function createGroups(array $items, array $data) ); $group = $this->groupFactory->create(); - $group->setData($groupData); $group->setRootCategoryId(0); + $group->setData($groupData); $group->getResource()->save($group); $group->getResource()->addCommitCallback(function () use ($data, $group, $website) { From b135ec9c010f6a06649da1a30bf7778a52988a03 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Wed, 31 Oct 2018 10:43:01 +0300 Subject: [PATCH 142/310] MAGETWO-91609: Problems with operator more/less in the "catalog Products List" widget - Fix unit test --- .../Test/Unit/Model/Condition/Sql/BuilderTest.php | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php index 5d18ccef17e54..13bba8a6a28ab 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php @@ -119,13 +119,12 @@ public function testAttachConditionAsHtmlToCollection() \Magento\Eav\Model\Entity\Collection\AbstractCollection::class, [ 'getResource', - 'getSelect', - 'getStoreId', - 'getDefaultStoreId', + 'getSelect' ] ); - $combine = $this->createPartialMock(\Magento\Rule\Model\Condition\Combine::class, + $combine = $this->createPartialMock( + \Magento\Rule\Model\Condition\Combine::class, [ 'getConditions', 'getValue', @@ -153,14 +152,6 @@ public function testAttachConditionAsHtmlToCollection() ->method('getResource') ->will($this->returnValue($resource)); - $collection->expects($this->once()) - ->method('getStoreId') - ->willReturn(1); - - $collection->expects($this->once()) - ->method('getDefaultStoreId') - ->willReturn(1); - $resource->expects($this->once()) ->method('getConnection') ->will($this->returnValue($connection)); From 8641218ebe978623fb1aa731544b76be99003448 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 31 Oct 2018 11:09:20 +0200 Subject: [PATCH 143/310] MAGETWO-95299: Ability to upload PDP images without compression and downsizing --- .../view/adminhtml/templates/browser/content/uploader.phtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml index df0a74c48ac23..414d42cb45382 100644 --- a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml +++ b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/uploader.phtml @@ -19,9 +19,9 @@ foreach ($filters as $media_type) { $resizeConfig = $block->getImageUploadConfigData()->getIsResizeEnabled() ? "{action: 'resize', maxWidth: " - . $block->getImageUploadMaxWidth() + . $block->escapeHtml($block->getImageUploadMaxWidth()) . ", maxHeight: " - . $block->getImageUploadMaxHeight() + . $block->escapeHtml($block->getImageUploadMaxHeight()) . "}" : "{action: 'resize'}"; ?> @@ -153,7 +153,7 @@ require([ fileTypes: /^image\/(gif|jpeg|png)$/, maxFileSize: <?= (int) $block->getFileSizeService()->getMaxFileSize() ?> * 10 }, - <?= $block->escapeHtml($resizeConfig) ?>, + <?= /* @noEscape */ $resizeConfig ?>, { action: 'save' }] From 7fd52e7ea8f7c7f77702d1b7a4665e715eee1ac3 Mon Sep 17 00:00:00 2001 From: Yevhenii Dumskyi <yevhenii.dumskyi@gmail.com> Date: Wed, 31 Oct 2018 11:31:19 +0200 Subject: [PATCH 144/310] Remove all possible native php functions & use framework components --- .../Model/Product/Gallery/Processor.php | 33 ++++++++++++------- composer.json | 1 - 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php index 225c502830695..048ad55985b72 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php @@ -57,25 +57,34 @@ class Processor */ protected $resourceModel; + /** + * @var \Magento\Framework\File\Mime + */ + protected $mime; + /** * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository * @param \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb * @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel + * @param \Magento\Framework\File\Mime $mime + * @throws \Magento\Framework\Exception\FileSystemException */ public function __construct( \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository, \Magento\MediaStorage\Helper\File\Storage\Database $fileStorageDb, \Magento\Catalog\Model\Product\Media\Config $mediaConfig, \Magento\Framework\Filesystem $filesystem, - \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel + \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel, + \Magento\Framework\File\Mime $mime ) { $this->attributeRepository = $attributeRepository; $this->fileStorageDb = $fileStorageDb; $this->mediaConfig = $mediaConfig; $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); $this->resourceModel = $resourceModel; + $this->mime = $mime; } /** @@ -186,10 +195,10 @@ public function addImage( $position = 0; $absoluteFilePath = $this->mediaDirectory->getAbsolutePath($file); - $imageMimeType = mime_content_type($absoluteFilePath); - $imageContent = file_get_contents($absoluteFilePath); + $imageMimeType = $this->mime->getMimeType($absoluteFilePath); + $imageContent = $this->mediaDirectory->readFile($absoluteFilePath); $imageBase64 = base64_encode($imageContent); - $imageName = pathinfo($destinationFile, PATHINFO_FILENAME); + $imageName = $pathinfo['filename']; if (!is_array($mediaGalleryData)) { $mediaGalleryData = ['images' => []]; @@ -203,17 +212,17 @@ public function addImage( $position++; $mediaGalleryData['images'][] = [ - 'file' => $fileName, - 'position' => $position, - 'label' => '', - 'disabled' => (int)$exclude, + 'file' => $fileName, + 'position' => $position, + 'label' => '', + 'disabled' => (int)$exclude, 'media_type' => 'image', - 'types' => $mediaAttribute, - 'content' => [ + 'types' => $mediaAttribute, + 'content' => [ 'data' => [ - ImageContentInterface::NAME => $imageName, + ImageContentInterface::NAME => $imageName, ImageContentInterface::BASE64_ENCODED_DATA => $imageBase64, - ImageContentInterface::TYPE => $imageMimeType, + ImageContentInterface::TYPE => $imageMimeType, ] ] ]; diff --git a/composer.json b/composer.json index 68bf1f1d8a8b0..e2a646275d98b 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,6 @@ "ext-xsl": "*", "ext-zip": "*", "lib-libxml": "*", - "ext-fileinfo": "*", "braintree/braintree_php": "3.35.0", "colinmollenhour/cache-backend-file": "~1.4.1", "colinmollenhour/cache-backend-redis": "1.10.5", From 80da4ea87544caa583ab1c90ed251ea946a8be51 Mon Sep 17 00:00:00 2001 From: prakashpatel07 <prakash.patel@krishtechnolabs.com> Date: Wed, 31 Oct 2018 13:38:55 +0000 Subject: [PATCH 145/310] Fixed Last Logged-in date when customer authenticate via REST API. --- .../Integration/Model/CustomerTokenService.php | 13 ++++++++++++- .../Test/Unit/Model/CustomerTokenServiceTest.php | 8 +++++++- .../Magento/Integration/etc/webapi_rest/events.xml | 12 ++++++++++++ .../Magento/Integration/etc/webapi_soap/events.xml | 12 ++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Integration/etc/webapi_rest/events.xml create mode 100644 app/code/Magento/Integration/etc/webapi_soap/events.xml diff --git a/app/code/Magento/Integration/Model/CustomerTokenService.php b/app/code/Magento/Integration/Model/CustomerTokenService.php index 3c245804a9f6e..35d0424d3b4fa 100644 --- a/app/code/Magento/Integration/Model/CustomerTokenService.php +++ b/app/code/Magento/Integration/Model/CustomerTokenService.php @@ -14,6 +14,7 @@ use Magento\Integration\Model\ResourceModel\Oauth\Token\CollectionFactory as TokenCollectionFactory; use Magento\Integration\Model\Oauth\Token\RequestThrottler; use Magento\Framework\Exception\AuthenticationException; +use Magento\Framework\Event\ManagerInterface; class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServiceInterface { @@ -24,6 +25,11 @@ class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServ */ private $tokenModelFactory; + /** + * @var Magento\Framework\Event\ManagerInterface + */ + private $eventManager; + /** * Customer Account Service * @@ -55,17 +61,21 @@ class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServ * @param AccountManagementInterface $accountManagement * @param TokenCollectionFactory $tokenModelCollectionFactory * @param \Magento\Integration\Model\CredentialsValidator $validatorHelper + * @param \Magento\Framework\Event\ManagerInterface $eventManager */ public function __construct( TokenModelFactory $tokenModelFactory, AccountManagementInterface $accountManagement, TokenCollectionFactory $tokenModelCollectionFactory, - CredentialsValidator $validatorHelper + CredentialsValidator $validatorHelper, + ManagerInterface $eventManager = null ) { $this->tokenModelFactory = $tokenModelFactory; $this->accountManagement = $accountManagement; $this->tokenModelCollectionFactory = $tokenModelCollectionFactory; $this->validatorHelper = $validatorHelper; + $this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(ManagerInterface::class); } /** @@ -86,6 +96,7 @@ public function createCustomerAccessToken($username, $password) ) ); } + $this->eventManager->dispatch('customer_login', ['customer' => $customerDataObject]); $this->getRequestThrottler()->resetAuthenticationFailuresCount($username, RequestThrottler::USER_TYPE_CUSTOMER); return $this->tokenModelFactory->create()->createCustomerToken($customerDataObject->getId())->getToken(); } diff --git a/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php b/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php index 1a7c819343294..1bc7d4247080f 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php @@ -32,6 +32,9 @@ class CustomerTokenServiceTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Integration\Model\Oauth\Token|\PHPUnit_Framework_MockObject_MockObject */ private $_tokenMock; + /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $manager; + protected function setUp() { $this->_tokenFactoryMock = $this->getMockBuilder(\Magento\Integration\Model\Oauth\TokenFactory::class) @@ -67,11 +70,14 @@ protected function setUp() \Magento\Integration\Model\CredentialsValidator::class )->disableOriginalConstructor()->getMock(); + $this->manager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); + $this->_tokenService = new \Magento\Integration\Model\CustomerTokenService( $this->_tokenFactoryMock, $this->_accountManagementMock, $this->_tokenModelCollectionFactoryMock, - $this->validatorHelperMock + $this->validatorHelperMock, + $this->manager ); } diff --git a/app/code/Magento/Integration/etc/webapi_rest/events.xml b/app/code/Magento/Integration/etc/webapi_rest/events.xml new file mode 100644 index 0000000000000..e978698734277 --- /dev/null +++ b/app/code/Magento/Integration/etc/webapi_rest/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="customer_login"> + <observer name="customer_log_login" instance="Magento\Customer\Observer\LogLastLoginAtObserver" /> + </event> +</config> diff --git a/app/code/Magento/Integration/etc/webapi_soap/events.xml b/app/code/Magento/Integration/etc/webapi_soap/events.xml new file mode 100644 index 0000000000000..e978698734277 --- /dev/null +++ b/app/code/Magento/Integration/etc/webapi_soap/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="customer_login"> + <observer name="customer_log_login" instance="Magento\Customer\Observer\LogLastLoginAtObserver" /> + </event> +</config> From 02192264fb25a7bf4d61326650d03a7205b0c940 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Wed, 31 Oct 2018 16:45:01 +0300 Subject: [PATCH 146/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static test --- .../view/adminhtml/web/js/grouped-product-grid.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index c1a35cb0afede..f257cb2df729e 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -9,6 +9,7 @@ define([ 'Magento_Ui/js/dynamic-rows/dynamic-rows-grid' ], function (_, registry, dynamicRowsGrid) { 'use strict'; + return dynamicRowsGrid.extend({ /** @@ -191,15 +192,6 @@ define([ return (~~this.currentPage() - 1) * this.pageSize + this.elems().pluck("name").indexOf(data.name); }, - /** - * Returns start index for current page - * - * @return {number} - */ - getStartIndex() { - return (~~this.currentPage() - 1) * this.pageSize; - }, - /** * Return Page Boundary * @@ -214,7 +206,7 @@ define([ * * @return {number} */ - getGlobalMaxPosition() { + getGlobalMaxPosition: function () { return _.max(this.recordData().map(function (r) { return ~~r.position })); From fff8dd3fae6aa65e19d7a5f539123b71d40c7078 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Wed, 31 Oct 2018 16:54:25 +0300 Subject: [PATCH 147/310] MAGETWO-91609: Problems with operator more/less in the "catalog Products List" widget - Fix static test --- .../Unit/Model/Condition/Sql/BuilderTest.php | 79 ++++--------------- 1 file changed, 17 insertions(+), 62 deletions(-) diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php index 13bba8a6a28ab..9dcbbd18c4c20 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/Sql/BuilderTest.php @@ -91,30 +91,13 @@ public function testAttachConditionAsHtmlToCollection() ['getOperatorForValidate', 'getMappedSqlField', 'getAttribute', 'getBindArgumentValue'] ); - $abstractCondition->expects($this->once()) - ->method('getMappedSqlField') - ->will($this->returnValue('argument')); - - $abstractCondition->expects($this->once()) - ->method('getOperatorForValidate') - ->will($this->returnValue('>')); - - $abstractCondition->expects($this->at(1)) - ->method('getAttribute') - ->will($this->returnValue('attribute')); - - $abstractCondition->expects($this->at(2)) - ->method('getAttribute') - ->will($this->returnValue('attribute')); - - $abstractCondition->expects($this->once()) - ->method('getBindArgumentValue') - ->will($this->returnValue(10)); - - $conditions = [ - $abstractCondition - ]; + $abstractCondition->expects($this->once())->method('getMappedSqlField')->will($this->returnValue('argument')); + $abstractCondition->expects($this->once())->method('getOperatorForValidate')->will($this->returnValue('>')); + $abstractCondition->expects($this->at(1))->method('getAttribute')->will($this->returnValue('attribute')); + $abstractCondition->expects($this->at(2))->method('getAttribute')->will($this->returnValue('attribute')); + $abstractCondition->expects($this->once())->method('getBindArgumentValue')->will($this->returnValue(10)); + $conditions = [$abstractCondition]; $collection = $this->createPartialMock( \Magento\Eav\Model\Entity\Collection\AbstractCollection::class, [ @@ -122,7 +105,6 @@ public function testAttachConditionAsHtmlToCollection() 'getSelect' ] ); - $combine = $this->createPartialMock( \Magento\Rule\Model\Condition\Combine::class, [ @@ -131,10 +113,10 @@ public function testAttachConditionAsHtmlToCollection() 'getAggregator' ] ); + $resource = $this->createPartialMock(\Magento\Framework\DB\Adapter\Pdo\Mysql::class, ['getConnection']); $select = $this->createPartialMock(\Magento\Framework\DB\Select::class, ['where']); - $select->expects($this->never()) - ->method('where'); + $select->expects($this->never())->method('where'); $connection = $this->getMockForAbstractClass( \Magento\Framework\DB\Adapter\AdapterInterface::class, @@ -143,42 +125,15 @@ public function testAttachConditionAsHtmlToCollection() false ); - $connection->expects($this->once()) - ->method('quoteInto') - ->with(' > ?', 10) - ->will($this->returnValue(' > 10')); - - $collection->expects($this->once()) - ->method('getResource') - ->will($this->returnValue($resource)); - - $resource->expects($this->once()) - ->method('getConnection') - ->will($this->returnValue($connection)); - - $combine->expects($this->once()) - ->method('getValue') - ->willReturn('attribute'); - - $combine->expects($this->once()) - ->method('getAggregator') - ->willReturn(' AND '); - - $combine->expects($this->at(0)) - ->method('getConditions') - ->will($this->returnValue($conditions)); - - $combine->expects($this->at(1)) - ->method('getConditions') - ->will($this->returnValue($conditions)); - - $combine->expects($this->at(2)) - ->method('getConditions') - ->will($this->returnValue($conditions)); - - $combine->expects($this->at(3)) - ->method('getConditions') - ->will($this->returnValue($conditions)); + $connection->expects($this->once())->method('quoteInto')->with(' > ?', 10)->will($this->returnValue(' > 10')); + $collection->expects($this->once())->method('getResource')->will($this->returnValue($resource)); + $resource->expects($this->once())->method('getConnection')->will($this->returnValue($connection)); + $combine->expects($this->once())->method('getValue')->willReturn('attribute'); + $combine->expects($this->once())->method('getAggregator')->willReturn(' AND '); + $combine->expects($this->at(0))->method('getConditions')->will($this->returnValue($conditions)); + $combine->expects($this->at(1))->method('getConditions')->will($this->returnValue($conditions)); + $combine->expects($this->at(2))->method('getConditions')->will($this->returnValue($conditions)); + $combine->expects($this->at(3))->method('getConditions')->will($this->returnValue($conditions)); $this->_builder->attachConditionToCollection($collection, $combine); } From 9f568fb21b4c211c85fcf675fb504d2c1971be4d Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 31 Oct 2018 16:15:03 +0200 Subject: [PATCH 148/310] MAGETWO-96007: Customer address issue when creating or updating via API --- .../ResourceModel/CustomerRepository.php | 99 ++++++++++--------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 43ae2db0c2163..a053eee5cd09b 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -8,69 +8,80 @@ use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Customer\Model\Delegation\Data\NewOperation; +use Magento\Customer\Api\Data\CustomerSearchResultsInterfaceFactory; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\Data\CustomerSecureFactory; use Magento\Customer\Model\Customer\NotificationStorage; +use Magento\Customer\Model\Delegation\Data\NewOperation; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\ImageProcessorInterface; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Framework\Api\Search\FilterGroup; +use Magento\Framework\Event\ManagerInterface; use Magento\Customer\Model\Delegation\Storage as DelegatedStorage; use Magento\Framework\App\ObjectManager; +use Magento\Store\Model\StoreManagerInterface; /** * Customer repository. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ -class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInterface +class CustomerRepository implements CustomerRepositoryInterface { /** - * @var \Magento\Customer\Model\CustomerFactory + * @var CustomerFactory */ protected $customerFactory; /** - * @var \Magento\Customer\Model\Data\CustomerSecureFactory + * @var CustomerSecureFactory */ protected $customerSecureFactory; /** - * @var \Magento\Customer\Model\CustomerRegistry + * @var CustomerRegistry */ protected $customerRegistry; /** - * @var \Magento\Customer\Model\ResourceModel\AddressRepository + * @var AddressRepository */ protected $addressRepository; /** - * @var \Magento\Customer\Model\ResourceModel\Customer + * @var Customer */ protected $customerResourceModel; /** - * @var \Magento\Customer\Api\CustomerMetadataInterface + * @var CustomerMetadataInterface */ protected $customerMetadata; /** - * @var \Magento\Customer\Api\Data\CustomerSearchResultsInterfaceFactory + * @var CustomerSearchResultsInterfaceFactory */ protected $searchResultsFactory; /** - * @var \Magento\Framework\Event\ManagerInterface + * @var ManagerInterface */ protected $eventManager; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; /** - * @var \Magento\Framework\Api\ExtensibleDataObjectConverter + * @var ExtensibleDataObjectConverter */ protected $extensibleDataObjectConverter; @@ -85,7 +96,7 @@ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInte protected $imageProcessor; /** - * @var \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface + * @var JoinProcessorInterface */ protected $extensionAttributesJoinProcessor; @@ -105,38 +116,38 @@ class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInte private $delegatedStorage; /** - * @param \Magento\Customer\Model\CustomerFactory $customerFactory - * @param \Magento\Customer\Model\Data\CustomerSecureFactory $customerSecureFactory - * @param \Magento\Customer\Model\CustomerRegistry $customerRegistry - * @param \Magento\Customer\Model\ResourceModel\AddressRepository $addressRepository - * @param \Magento\Customer\Model\ResourceModel\Customer $customerResourceModel - * @param \Magento\Customer\Api\CustomerMetadataInterface $customerMetadata - * @param \Magento\Customer\Api\Data\CustomerSearchResultsInterfaceFactory $searchResultsFactory - * @param \Magento\Framework\Event\ManagerInterface $eventManager - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter + * @param CustomerFactory $customerFactory + * @param CustomerSecureFactory $customerSecureFactory + * @param CustomerRegistry $customerRegistry + * @param AddressRepository $addressRepository + * @param Customer $customerResourceModel + * @param CustomerMetadataInterface $customerMetadata + * @param CustomerSearchResultsInterfaceFactory $searchResultsFactory + * @param ManagerInterface $eventManager + * @param StoreManagerInterface $storeManager + * @param ExtensibleDataObjectConverter $extensibleDataObjectConverter * @param DataObjectHelper $dataObjectHelper * @param ImageProcessorInterface $imageProcessor - * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor + * @param JoinProcessorInterface $extensionAttributesJoinProcessor * @param CollectionProcessorInterface $collectionProcessor * @param NotificationStorage $notificationStorage * @param DelegatedStorage|null $delegatedStorage * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Customer\Model\CustomerFactory $customerFactory, - \Magento\Customer\Model\Data\CustomerSecureFactory $customerSecureFactory, - \Magento\Customer\Model\CustomerRegistry $customerRegistry, - \Magento\Customer\Model\ResourceModel\AddressRepository $addressRepository, - \Magento\Customer\Model\ResourceModel\Customer $customerResourceModel, - \Magento\Customer\Api\CustomerMetadataInterface $customerMetadata, - \Magento\Customer\Api\Data\CustomerSearchResultsInterfaceFactory $searchResultsFactory, - \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, + CustomerFactory $customerFactory, + CustomerSecureFactory $customerSecureFactory, + CustomerRegistry $customerRegistry, + AddressRepository $addressRepository, + Customer $customerResourceModel, + CustomerMetadataInterface $customerMetadata, + CustomerSearchResultsInterfaceFactory $searchResultsFactory, + ManagerInterface $eventManager, + StoreManagerInterface $storeManager, + ExtensibleDataObjectConverter $extensibleDataObjectConverter, DataObjectHelper $dataObjectHelper, ImageProcessorInterface $imageProcessor, - \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor, + JoinProcessorInterface $extensionAttributesJoinProcessor, CollectionProcessorInterface $collectionProcessor, NotificationStorage $notificationStorage, DelegatedStorage $delegatedStorage = null @@ -156,8 +167,7 @@ public function __construct( $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; $this->collectionProcessor = $collectionProcessor; $this->notificationStorage = $notificationStorage; - $this->delegatedStorage = $delegatedStorage - ?? ObjectManager::getInstance()->get(DelegatedStorage::class); + $this->delegatedStorage = $delegatedStorage ?? ObjectManager::getInstance()->get(DelegatedStorage::class); } /** @@ -200,13 +210,13 @@ public function save(CustomerInterface $customer, $passwordHash = null) $customerModel->setRpToken(null); $customerModel->setRpTokenCreatedAt(null); } - if (!array_key_exists('default_billing', $customerArr) + if (!array_key_exists('addresses', $customerArr) && null !== $prevCustomerDataArr && array_key_exists('default_billing', $prevCustomerDataArr) ) { $customerModel->setDefaultBilling($prevCustomerDataArr['default_billing']); } - if (!array_key_exists('default_shipping', $customerArr) + if (!array_key_exists('addresses', $customerArr) && null !== $prevCustomerDataArr && array_key_exists('default_shipping', $prevCustomerDataArr) ) { @@ -371,15 +381,12 @@ public function deleteById($customerId) * Helper function that adds a FilterGroup to the collection. * * @deprecated 100.2.0 - * @param \Magento\Framework\Api\Search\FilterGroup $filterGroup - * @param \Magento\Customer\Model\ResourceModel\Customer\Collection $collection + * @param FilterGroup $filterGroup + * @param Collection $collection * @return void - * @throws \Magento\Framework\Exception\InputException */ - protected function addFilterGroupToCollection( - \Magento\Framework\Api\Search\FilterGroup $filterGroup, - \Magento\Customer\Model\ResourceModel\Customer\Collection $collection - ) { + protected function addFilterGroupToCollection(FilterGroup $filterGroup, Collection $collection) + { $fields = []; foreach ($filterGroup->getFilters() as $filter) { $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq'; From 24b690fbc3610641c674f88893fd01b0aab70118 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Wed, 31 Oct 2018 17:19:46 +0300 Subject: [PATCH 149/310] MAGETWO-91649: Magento ignore store-level url_key of child category in URL rewrite process for global scope - Fix static test --- .../Observer/CategoryUrlPathAutogeneratorObserver.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php index 5c7a8b16a666e..d692918aff6a6 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php @@ -14,6 +14,9 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Store\Model\Store; +/** + * Class for set or update url path. + */ class CategoryUrlPathAutogeneratorObserver implements ObserverInterface { /** @@ -55,6 +58,8 @@ public function __construct( } /** + * Method for update/set url path. + * * @param \Magento\Framework\Event\Observer $observer * @return void * @throws \Magento\Framework\Exception\LocalizedException @@ -81,6 +86,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) } /** + * Update url path for children category. + * * @param Category $category * @return void */ @@ -121,6 +128,8 @@ protected function isGlobalScope($storeId) } /** + * Update url path for category. + * * @param Category $category * @return void */ From 07c2d7a3447e6c8079e02e331777e00ba835e6ed Mon Sep 17 00:00:00 2001 From: Dmytro Drozd <ddrozd@magento.com> Date: Wed, 31 Oct 2018 16:53:51 +0200 Subject: [PATCH 150/310] MAGETWO-96026: Create MFTF test for MAGETWO-93973 --- .../Test/Mftf/Page/AdminProductCreatePage.xml | 1 + ...minProductFormAdvancedInventorySection.xml | 37 +++++++++ .../Mftf/Section/AdminProductFormSection.xml | 1 + ...IncrementsWorkWithDecimalinventoryTest.xml | 75 +++++++++++++++++++ .../Section/StorefrontQuickSearchSection.xml | 2 +- 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml index fc776b49ba213..b3ed3f478f810 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml @@ -17,5 +17,6 @@ <section name="AdminProductMessagesSection"/> <section name="AdminProductFormRelatedUpSellCrossSellSection"/> <section name="AdminProductFormAdvancedPricingSection"/> + <section name="AdminProductFormAdvancedInventorySection"/> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml new file mode 100644 index 0000000000000..e1272899b78ab --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductFormAdvancedInventorySection"> + <!--<element name="customerGroupPriceAddButton" type="button" selector="[data-action='add_new_row']" timeout="30"/>--> + <!--<element name="customerGroupPriceDeleteButton" type="button" selector="[data-action='remove_row']" timeout="30"/>--> + <!--<element name="advancedPricingCloseButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-close" timeout="30"/>--> + <!--<element name="productTierPriceWebsiteSelect" type="select" selector="[name='product[tier_price][{{var1}}][website_id]']" parameterized="true"/>--> + <!--<element name="productTierPriceCustGroupSelect" type="select" selector="[name='product[tier_price][{{var1}}][cust_group]']" parameterized="true"/>--> + <!--<element name="productTierPriceQtyInput" type="input" selector="[name='product[tier_price][{{var1}}][price_qty]']" parameterized="true"/>--> + <!--<element name="productTierPriceValueTypeSelect" type="select" selector="[name='product[tier_price][{{var1}}][value_type]']" parameterized="true"/>--> + <!--<element name="productTierPriceFixedPriceInput" type="input" selector="[name='product[tier_price][{{var1}}][price]']" parameterized="true"/>--> + <!--<element name="productTierPricePercentageValuePriceInput" type="input" selector="[name='product[tier_price][{{var1}}][percentage_value]']" parameterized="true"/>--> + <!--<element name="specialPrice" type="input" selector="input[name='product[special_price]']"/>--> + + <element name="enableQtyIncrements" type="select" selector="//*[@name='product[stock_data][enable_qty_increments]']"/> + <element name="enableQtyIncrementsOptions" type="select" selector="//*[@name='product[stock_data][enable_qty_increments]']//option[contains(@value, '{{var1}}')]" parameterized="true"/> + <element name="enableQtyIncrementsUseConfigSettings" type="checkbox" selector="//input[@name='product[stock_data][use_config_enable_qty_inc]']"/> + <element name="qtyUsesDecimals" type="select" selector="//*[@name='product[stock_data][is_qty_decimal]']"/> + <element name="qtyUsesDecimalsOptions" type="select" selector="//*[@name='product[stock_data][is_qty_decimal]']//option[contains(@value, '{{var1}}')]" parameterized="true"/> + <element name="qtyIncrements" type="input" selector="//input[@name='product[stock_data][qty_increments]']"/> + <element name="qtyIncrementsUseConfigSettings" type="checkbox" selector="//input[@name='product[stock_data][use_config_qty_increments]']"/> + + <element name="doneButton" type="button" selector="//aside[contains(@class,'product_form_product_form_advanced_inventory_modal')]//button[contains(@data-role,'action')]" timeout="5"/> + </section> +</sections> + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 03df9d2a88107..9ec0dbf9f262c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -28,6 +28,7 @@ <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']"/> <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']"/> <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> + <element name="advancedInventoryLink" type="input" selector="//button[contains(@data-index, 'advanced_inventory_button')]"/> <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']"/> <element name="productWeight" type="input" selector=".admin__field[data-index=weight] input"/> <element name="productWeightSelect" type="select" selector="select[name='product[product_has_weight]']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml new file mode 100644 index 0000000000000..7a774c585c9cd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml @@ -0,0 +1,75 @@ +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest"> + <annotations> + <features value="Tiered pricing and quantity increments work with decimal inventory"/> + <stories value="Tiered pricing and quantity increments work with decimal inventory"/> + <title value="Tiered pricing and quantity increments work with decimal inventory"/> + <description value="Tiered pricing and quantity increments work with decimal inventory"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-91178"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="SimpleProduct" stepKey="createPreReqSimpleProduct"> + <requiredEntity createDataKey="createPreReqCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="createPreReqSimpleProduct" stepKey="deletePreReqSimpleProduct"/> + </after> + <!--Step1. Login as admin. Go to Catalog > Products page. Filtering *prod1*. Open *prod1* to edit--> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin" /> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="filterGroupedProductOptions"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <click selector="{{AdminProductGridSection.productGridNameProduct('$$createPreReqSimpleProduct.name$$')}}" + stepKey="clickOpenProductForEdit"/> + <waitForPageLoad time="30" stepKey="waitForProductEditOpen"/> + <!--Step2. Open *Advanced Inventory* pop-up (Click on *Advanced Inventory* link). Set *Qty Uses Decimals* to *Yes*. Click on button *Done* --> + <click selector="{{AdminProductFormSection.advancedInventoryLink}}" stepKey="clickOnAdvancedInventoryLink"/> + <scrollTo selector="{{AdminProductFormAdvancedInventorySection.qtyUsesDecimals}}" stepKey="scrollToQtyUsesDecimalsDropBox"/> + <click selector="{{AdminProductFormAdvancedInventorySection.qtyUsesDecimals}}" stepKey="clickOnQtyUsesDecimalsDropBox"/> + <click selector="{{AdminProductFormAdvancedInventorySection.qtyUsesDecimalsOptions('1')}}" stepKey="chooseYesOnQtyUsesDecimalsDropBox"/> + <click selector="{{AdminProductFormAdvancedInventorySection.doneButton}}" stepKey="clickOnDoneButton"/> + <!--Step3. Open *Advanced Pricing* pop-up (Click on *Advanced Pricing* link). Click on *Add* button. Fill *0.5* in *Quantity*--> + <scrollTo selector="{{AdminProductFormSection.productName}}" stepKey="scrollToProductName"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingLink1"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickOnCustomerGroupPriceAddButton"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="0.5" stepKey="fillProductTierPriceQty"/> + <!--Step4. Close *Advanced Pricing* (Click on button *Done*). Save *prod1* (Click on button *Save*)--> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickOnDoneButton2"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> + + <!--The code should be uncommented after fix MAGETWO-96016--> + <!--<click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingLink2"/>--> + <!--<seeInField userInput="0.5" selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" stepKey="seeInField1"/>--> + <!--<click selector="{{AdminProductFormAdvancedPricingSection.advancedPricingCloseButton}}" stepKey="clickOnCloseButton"/>--> + + <!--Step5. Open *Advanced Inventory* pop-up. Set *Enable Qty Increments* to *Yes*. Fill *.5* in *Qty Increments*--> + <click selector="{{AdminProductFormSection.advancedInventoryLink}}" stepKey="clickOnAdvancedInventoryLink2"/> + <scrollTo selector="{{AdminProductFormAdvancedInventorySection.enableQtyIncrements}}" stepKey="scrollToEnableQtyIncrements"/> + <click selector="{{AdminProductFormAdvancedInventorySection.enableQtyIncrementsUseConfigSettings}}" stepKey="clickOnEnableQtyIncrementsUseConfigSettingsCheckbox"/> + <click selector="{{AdminProductFormAdvancedInventorySection.enableQtyIncrements}}" stepKey="clickOnEnableQtyIncrements"/> + <click selector="{{AdminProductFormAdvancedInventorySection.enableQtyIncrementsOptions(('1'))}}" stepKey="chooseYesOnEnableQtyIncrements"/> + <click selector="{{AdminProductFormAdvancedInventorySection.qtyIncrementsUseConfigSettings}}" stepKey="clickOnQtyIncrementsUseConfigSettings"/> + <fillField selector="{{AdminProductFormAdvancedInventorySection.qtyIncrements}}" userInput=".5" stepKey="fillQtyIncrements"/> + <!--Step6. Close *Advanced Inventory* (Click on button *Done*). Save *prod1* (Click on button *Save*) --> + <click selector="{{AdminProductFormAdvancedInventorySection.doneButton}}" stepKey="clickOnDoneButton3"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <!--Step7. Open *Customer view* (Go to *Store Front*). Open *prod1* page (Find via search and click on product name) --> + <amOnPage url="{{StorefrontHomePage.url}}$$createPreReqSimpleProduct.custom_attributes[url_key]$$.html" stepKey="amOnProductPage"/> + <!--Step8. Fill *1.5* in *Qty*. Click on button *Add to Cart*--> + <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="1.5" stepKey="fillQty"/> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="clickOnAddToCart"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createPreReqSimpleProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> + <!--Step9. Click on *Cart* icon. Click on *View and Edit Cart* link. Change *Qty* value to *5.5*--> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName(('$$createPreReqSimpleProduct.name$$'))}}" userInput="5.5" stepKey="fillQty2"/> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickOnUpdateShoppingCartButton"/> + <seeInField userInput="5.5" selector="{{CheckoutCartProductSection.ProductQuantityByName(('$$createPreReqSimpleProduct.name$$'))}}" stepKey="seeInField2"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml index 2b08e9b4b85ec..6baa3ff54fca0 100644 --- a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml +++ b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontQuickSearchSection"> + <section name="StorefrontQuickSearchSection">/ <element name="searchPhrase" type="input" selector="#search"/> <element name="searchButton" type="button" selector="button.action.search" timeout="30"/> </section> From 3e94424757aa1b8ad2febc89617f5b199057b8ec Mon Sep 17 00:00:00 2001 From: Dmytro Drozd <ddrozd@magento.com> Date: Wed, 31 Oct 2018 17:12:26 +0200 Subject: [PATCH 151/310] MAGETWO-96026: Create MFTF test for MAGETWO-93973 --- .../AdminProductFormAdvancedInventorySection.xml | 12 ------------ .../Mftf/Section/StorefrontQuickSearchSection.xml | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml index e1272899b78ab..0314534dcddfb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml @@ -9,17 +9,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormAdvancedInventorySection"> - <!--<element name="customerGroupPriceAddButton" type="button" selector="[data-action='add_new_row']" timeout="30"/>--> - <!--<element name="customerGroupPriceDeleteButton" type="button" selector="[data-action='remove_row']" timeout="30"/>--> - <!--<element name="advancedPricingCloseButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-close" timeout="30"/>--> - <!--<element name="productTierPriceWebsiteSelect" type="select" selector="[name='product[tier_price][{{var1}}][website_id]']" parameterized="true"/>--> - <!--<element name="productTierPriceCustGroupSelect" type="select" selector="[name='product[tier_price][{{var1}}][cust_group]']" parameterized="true"/>--> - <!--<element name="productTierPriceQtyInput" type="input" selector="[name='product[tier_price][{{var1}}][price_qty]']" parameterized="true"/>--> - <!--<element name="productTierPriceValueTypeSelect" type="select" selector="[name='product[tier_price][{{var1}}][value_type]']" parameterized="true"/>--> - <!--<element name="productTierPriceFixedPriceInput" type="input" selector="[name='product[tier_price][{{var1}}][price]']" parameterized="true"/>--> - <!--<element name="productTierPricePercentageValuePriceInput" type="input" selector="[name='product[tier_price][{{var1}}][percentage_value]']" parameterized="true"/>--> - <!--<element name="specialPrice" type="input" selector="input[name='product[special_price]']"/>--> - <element name="enableQtyIncrements" type="select" selector="//*[@name='product[stock_data][enable_qty_increments]']"/> <element name="enableQtyIncrementsOptions" type="select" selector="//*[@name='product[stock_data][enable_qty_increments]']//option[contains(@value, '{{var1}}')]" parameterized="true"/> <element name="enableQtyIncrementsUseConfigSettings" type="checkbox" selector="//input[@name='product[stock_data][use_config_enable_qty_inc]']"/> @@ -27,7 +16,6 @@ <element name="qtyUsesDecimalsOptions" type="select" selector="//*[@name='product[stock_data][is_qty_decimal]']//option[contains(@value, '{{var1}}')]" parameterized="true"/> <element name="qtyIncrements" type="input" selector="//input[@name='product[stock_data][qty_increments]']"/> <element name="qtyIncrementsUseConfigSettings" type="checkbox" selector="//input[@name='product[stock_data][use_config_qty_increments]']"/> - <element name="doneButton" type="button" selector="//aside[contains(@class,'product_form_product_form_advanced_inventory_modal')]//button[contains(@data-role,'action')]" timeout="5"/> </section> </sections> diff --git a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml index 6baa3ff54fca0..2b08e9b4b85ec 100644 --- a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml +++ b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchSection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontQuickSearchSection">/ + <section name="StorefrontQuickSearchSection"> <element name="searchPhrase" type="input" selector="#search"/> <element name="searchButton" type="button" selector="button.action.search" timeout="30"/> </section> From d70ff4be68f9e084cc4e11de4092a6775e452b92 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Wed, 31 Oct 2018 10:38:14 -0500 Subject: [PATCH 152/310] MAGETWO-95773: Credit memo is created instead of returning error via invoice refund API for Bundle product - fix unit and static tests --- .../Validation/CreationQuantityValidator.php | 5 +++++ .../Sales/Model/Order/ItemRepository.php | 4 ++-- .../Unit/Model/Order/ItemRepositoryTest.php | 22 +++++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php index fdc5e2658b9ed..93a4e701e0322 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Item/Validation/CreationQuantityValidator.php @@ -30,6 +30,7 @@ class CreationQuantityValidator implements ValidatorInterface /** * ItemCreationQuantityValidator constructor. + * * @param OrderItemRepositoryInterface $orderItemRepository * @param mixed $context */ @@ -65,6 +66,8 @@ public function validate($entity) } /** + * Check the quantity to refund is greater than the unrefunded quantity + * * @param Item $orderItem * @param int $qty * @return bool @@ -75,6 +78,8 @@ private function isQtyAvailable(Item $orderItem, $qty) } /** + * Check to see if Item is part of the order + * * @param OrderItemInterface $orderItem * @return bool */ diff --git a/app/code/Magento/Sales/Model/Order/ItemRepository.php b/app/code/Magento/Sales/Model/Order/ItemRepository.php index 2ea9831336cc2..b1ff747d4f487 100644 --- a/app/code/Magento/Sales/Model/Order/ItemRepository.php +++ b/app/code/Magento/Sales/Model/Order/ItemRepository.php @@ -95,7 +95,7 @@ public function __construct( } /** - * load entity + * Load entity * * @param int $id * @return OrderItemInterface @@ -230,7 +230,7 @@ private function addParentItem(OrderItemInterface $orderItem) $orderItem->setParentItem($this->get($parentId)); } else { foreach ($orderItem->getOrder()->getAllItems() as $item) { - if($item->getParentItemId() === $orderItem->getItemId()) { + if ($item->getParentItemId() === $orderItem->getItemId()) { $item->setParentItem($orderItem); } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php index 8be2c3c8612d7..c19b4cbc3cb50 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php @@ -156,18 +156,27 @@ public function testGet() $productOption = $this->getProductOptionMock(); $orderItemMock = $this->getOrderMock($productType, $productOption); + $orderMock = $this->createMock(\Magento\Sales\Model\Order::class); + $orderMock->expects($this->once()) + ->method('getAllItems') + ->willReturn([$orderItemMock]); + $orderItemMock->expects($this->once()) ->method('load') ->with($orderItemId) ->willReturn($orderItemMock); - $orderItemMock->expects($this->once()) + $orderItemMock->expects($this->exactly(2)) ->method('getItemId') ->willReturn($orderItemId); + $orderItemMock->expects($this->once()) + ->method('getOrder') + ->willReturn($orderMock); $this->metadata->expects($this->once()) ->method('getNewInstance') ->willReturn($orderItemMock); + $model = $this->getModel($orderItemMock, $productType); $this->assertSame($orderItemMock, $model->get($orderItemId)); @@ -213,11 +222,17 @@ public function testDeleteById() $orderItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class) ->disableOriginalConstructor() ->getMock(); + + $orderMock = $this->createMock(\Magento\Sales\Model\Order::class); + $orderMock->expects($this->once()) + ->method('getAllItems') + ->willReturn([$orderItemMock]); + $orderItemMock->expects($this->once()) ->method('load') ->with($orderItemId) ->willReturn($orderItemMock); - $orderItemMock->expects($this->once()) + $orderItemMock->expects($this->exactly(2)) ->method('getItemId') ->willReturn($orderItemId); $orderItemMock->expects($this->once()) @@ -226,6 +241,9 @@ public function testDeleteById() $orderItemMock->expects($this->once()) ->method('getBuyRequest') ->willReturn($requestMock); + $orderItemMock->expects($this->once()) + ->method('getOrder') + ->willReturn($orderMock); $orderItemResourceMock = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class) ->disableOriginalConstructor() From 413a03686976ccf06c240783ddfdd2632af6d7cc Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Wed, 31 Oct 2018 13:15:39 -0500 Subject: [PATCH 153/310] MAGETWO-95773: Credit memo is created instead of returning error via invoice refund API for Bundle product - remove empty line --- .../Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php index c19b4cbc3cb50..5e1b49c0013e3 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php @@ -176,7 +176,6 @@ public function testGet() ->method('getNewInstance') ->willReturn($orderItemMock); - $model = $this->getModel($orderItemMock, $productType); $this->assertSame($orderItemMock, $model->get($orderItemId)); From a0bb0fcf965d47f617cc801187f6290d8bdf22a6 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Thu, 1 Nov 2018 09:45:21 +0200 Subject: [PATCH 154/310] graphQl: updated shipping address coverage content --- .../MultiShipping.php | 69 ++++++++++++++++--- .../MultiShipping/ShippingItemsMapper.php | 43 ++++++++++++ .../SingleShipping.php | 47 +++++++++++-- .../SetShippingAddressesOnCart.php | 32 +++------ .../Magento/QuoteGraphQl/etc/schema.graphqls | 4 ++ 5 files changed, 157 insertions(+), 38 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php index 71560a26c03ee..318fd9361af5a 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php @@ -1,23 +1,76 @@ <?php /** - * @author Atwix Team - * @copyright Copyright (c) 2018 Atwix (https://www.atwix.com/) + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart; +use Magento\Authorization\Model\UserContextInterface; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Multishipping\Model\Checkout\Type\Multishipping as MultishippingModel; +use Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart\MultiShipping\ShippingItemsMapper; + class MultiShipping { /** + * @var MultishippingModel + */ + private $multishippingModel; + + /** + * @var ShippingItemsMapper + */ + private $shippingItemsInformationMapper; + + /** + * @param MultishippingModel $multishippingModel + * @param ShippingItemsMapper $shippingItemsInformationMapper + */ + public function __construct( + MultishippingModel $multishippingModel, + ShippingItemsMapper $shippingItemsInformationMapper + ) { + $this->multishippingModel = $multishippingModel; + $this->shippingItemsInformationMapper = $shippingItemsInformationMapper; + } + + /** + * @param ContextInterface $context * @param int $cartId - * @param array $cartItems - * @param int|null $customerAddressId - * @param array|null $address - * @return void + * @param array $shippingAddresses */ - public function setAddress(int $cartId, array $cartItems, ?int $customerAddressId, ?array $address): void + public function setAddresses(ContextInterface $context, int $cartId, array $shippingAddresses): void { - //TODO: implement multi shipping + if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { + throw new GraphQlAuthorizationException( + __( + 'Multishipping allowed only for authorized customers' + ) + ); + } + + $shippingItemsInformation = []; + foreach ($shippingAddresses as $shippingAddress) { + $customerAddressId = $shippingAddress['customer_address_id'] ?? null; + $cartItems = $shippingAddress['cart_items'] ?? null; + if (!$customerAddressId) { + throw new GraphQlInputException(__('Parameter "customer_address_id" is required for multishipping')); + } + if (!$cartItems) { + throw new GraphQlInputException(__('Parameter "cart_items" is required for multishipping')); + } + + $shippingItemsInformation = array_merge( + $shippingItemsInformation, + $this->shippingItemsInformationMapper->map($shippingAddress) + ); + } + + //TODO: multishipping model works with session. Do we need to avoid it? + $this->multishippingModel->setShippingItemsInformation($shippingItemsInformation); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php new file mode 100644 index 0000000000000..c9cefc421a544 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart\MultiShipping; + +/** + * Shipping address to shipping items mapper + */ +class ShippingItemsMapper +{ + + /** + * Converts shipping address input array into shipping items information array + * Array structure: + * array( + * $cartItemId => array( + * 'qty' => $qty, + * 'address' => $customerAddressId + * ) + * ) + * + * @param array $shippingAddress + * @return array + */ + public function map(array $shippingAddress): array + { + $shippingItemsInformation = []; + foreach ($shippingAddress['cart_items'] as $cartItem) { + $shippingItemsInformation[] = [ + $cartItem['cart_item_id'] => [ + 'qty' => $cartItem['quantity'], + 'address' => $shippingAddress['customer_address_id'] + ] + ]; + } + + return $shippingItemsInformation; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php index 536a907541b1a..feeb333683f76 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php @@ -1,12 +1,17 @@ <?php /** - * @author Atwix Team - * @copyright Copyright (c) 2018 Atwix (https://www.atwix.com/) + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart; +use Magento\Authorization\Model\UserContextInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\ShippingAddressManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; @@ -44,18 +49,46 @@ public function __construct( } /** + * @param ContextInterface $context * @param int $cartId - * @param int|null $customerAddressId - * @param array|null $address + * @param array $shippingAddress * @return void */ - public function setAddress(int $cartId, ?int $customerAddressId, ?array $address): void + public function setAddress(ContextInterface $context, int $cartId, array $shippingAddress): void { - if($customerAddressId) { + $customerAddressId = $shippingAddress['customer_address_id'] ?? null; + $addressInput = $shippingAddress['address'] ?? null; + + if (!$customerAddressId && !$addressInput) { + throw new GraphQlInputException( + __('Shipping address should contain either "customer_address_id" or "address" input.') + ); + } + if ($customerAddressId && $addressInput) { + throw new GraphQlInputException( + __('Shipping address can\'t contain "customer_address_id" and "address" input at the same time.') + ); + } + if ($customerAddressId) { + if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { + throw new GraphQlAuthorizationException( + __( + 'Address management allowed only for authorized customers' + ) + ); + } + /** @var AddressInterface $customerAddress */ $customerAddress = $this->addressRepository->getById($customerAddressId); + if ($context->getUserId() !== (int)$customerAddress->getCustomerId()) { + throw new GraphQlInputException( + __( + 'Address is not applicable for current customer' + ) + ); + } $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress); } else { - $shippingAddress = $this->addressModel->addData($address); + $shippingAddress = $this->addressModel->addData($addressInput); } $this->shippingAddressManagement->assign($cartId, $shippingAddress); diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php index f3960f66d2792..7ffa6c4950b3d 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php @@ -7,7 +7,6 @@ namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress; -use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -22,11 +21,6 @@ */ class SetShippingAddressesOnCart implements ResolverInterface { - /** - * @var DataObjectHelper - */ - private $dataObjectHelper; - /** * @var MaskedQuoteIdToQuoteIdInterface */ @@ -48,20 +42,17 @@ class SetShippingAddressesOnCart implements ResolverInterface private $shippingAddressManagement; /** - * @param DataObjectHelper $dataObjectHelper * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param MultiShipping $multiShipping * @param SingleShipping $singleShipping * @param ShippingAddressManagementInterface $shippingAddressManagement */ public function __construct( - DataObjectHelper $dataObjectHelper, MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, MultiShipping $multiShipping, SingleShipping $singleShipping, - ShippingAddressManagementInterface $shippingAddressManagement + ShippingAddressManagementInterface $shippingAddressManagement ) { - $this->dataObjectHelper = $dataObjectHelper; $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->multiShipping = $multiShipping; $this->singleShipping = $singleShipping; @@ -76,23 +67,18 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($args['input']['cart_id'])) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } + if (!isset($args['input']['shipping_addresses'])) { + throw new GraphQlInputException(__('Required parameter "shipping_addresses" is missing')); + } + + $shippingAddressesInput = $args['input']['shipping_addresses']; $maskedCartId = $args['input']['cart_id']; $cartId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); - $customerAddressId = $args['input']['customer_address_id'] ?? null; - $address = $args['input']['address'] ?? null; - $cartItems = $args['input']['cart_items'] ?? []; - - if (!$customerAddressId && !$address) { - throw new GraphQlInputException(__('Query should contain either address id or address input.')); - } - - //TODO: how to determine whether is multi shipping or not - if (!$cartItems) { - //TODO: assign cart items - $this->singleShipping->setAddress($cartId, $customerAddressId, $address); + if (count($shippingAddressesInput) === 1) { + $this->singleShipping->setAddress($context, $cartId, current($shippingAddressesInput)); } else { - $this->multiShipping->setAddress($cartId, $cartItems, $customerAddressId, $address); + $this->multiShipping->setAddresses($context, $cartId, $shippingAddressesInput); } //TODO: implement Cart object in the separate resolver diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index d13240a23140b..d99182ed10988 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -16,6 +16,10 @@ type Mutation { input SetShippingAddressesOnCartInput { cart_id: String! + shipping_addresses: [ShippingAddressInput!]! +} + +input ShippingAddressInput { customer_address_id: Int # Can be provided in one-page checkout and is required for multi-shipping checkout address: CartAddressInput cart_items: [CartItemQuantityInput!] From 71983df468f7d5a9ff35e915c59d56c4d33a414b Mon Sep 17 00:00:00 2001 From: Yevhenii Dumskyi <yevhenii.dumskyi@gmail.com> Date: Thu, 1 Nov 2018 10:50:59 +0200 Subject: [PATCH 155/310] Fix backward incompatibility --- .../Catalog/Model/Product/Gallery/Processor.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php index 048ad55985b72..9cd5073f4357c 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php @@ -9,7 +9,7 @@ use Magento\Framework\Api\Data\ImageContentInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Filesystem\DriverInterface; +use Magento\Framework\App\ObjectManager; /** * Catalog product Media Gallery attribute processor. @@ -60,7 +60,7 @@ class Processor /** * @var \Magento\Framework\File\Mime */ - protected $mime; + private $mime; /** * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository @@ -68,7 +68,7 @@ class Processor * @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel - * @param \Magento\Framework\File\Mime $mime + * @param \Magento\Framework\File\Mime|null $mime * @throws \Magento\Framework\Exception\FileSystemException */ public function __construct( @@ -77,14 +77,14 @@ public function __construct( \Magento\Catalog\Model\Product\Media\Config $mediaConfig, \Magento\Framework\Filesystem $filesystem, \Magento\Catalog\Model\ResourceModel\Product\Gallery $resourceModel, - \Magento\Framework\File\Mime $mime + \Magento\Framework\File\Mime $mime = null ) { $this->attributeRepository = $attributeRepository; $this->fileStorageDb = $fileStorageDb; $this->mediaConfig = $mediaConfig; $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); $this->resourceModel = $resourceModel; - $this->mime = $mime; + $this->mime = $mime ?: ObjectManager::getInstance()->get(\Magento\Framework\File\Mime::class); } /** From a537b009bbece04073700df55bbd1a0abaf31821 Mon Sep 17 00:00:00 2001 From: Dmytro Drozd <ddrozd@magento.com> Date: Thu, 1 Nov 2018 11:32:05 +0200 Subject: [PATCH 156/310] MAGETWO-96026: Create MFTF test for MAGETWO-93973 --- ...gAndQuantityIncrementsWorkWithDecimalinventoryTest.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml index 7a774c585c9cd..e9158e91432fa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml @@ -1,3 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest"> From e794db8b1a9fb3b456b1cdaac5303d1888fc9698 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Thu, 1 Nov 2018 12:59:38 +0300 Subject: [PATCH 157/310] MAGETWO-91649: Magento ignore store-level url_key of child category in URL rewrite process for global scope - Fixed integration test; --- .../Magento/Catalog/Controller/Adminhtml/CategoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index a6d03fcc200e2..dad4d21362a0a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -403,7 +403,7 @@ public function moveActionDataProvider() { return [ [400, 401, 'first_url_key', 402, 'second_url_key', false], - [400, 401, 'duplicated_url_key', 402, 'duplicated_url_key', true], + [400, 401, 'duplicated_url_key', 402, 'duplicated_url_key', false], [0, 401, 'first_url_key', 402, 'second_url_key', true], [400, 401, 'first_url_key', 0, 'second_url_key', true], ]; From 70f1c939381a9ae123770f80a27b3fcaaf6bfc83 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Thu, 1 Nov 2018 12:02:49 +0200 Subject: [PATCH 158/310] MAGETWO-95692: [2.3] Value of Customer Address Attribute is not shown in the Customers grid --- .../Magento/Customer/Model/Indexer/Source.php | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Customer/Model/Indexer/Source.php b/app/code/Magento/Customer/Model/Indexer/Source.php index e4bf03e08a9ad..a8878e2084ea0 100644 --- a/app/code/Magento/Customer/Model/Indexer/Source.php +++ b/app/code/Magento/Customer/Model/Indexer/Source.php @@ -5,6 +5,7 @@ */ namespace Magento\Customer\Model\Indexer; +use Magento\Customer\Model\ResourceModel\Customer\Indexer\CollectionFactory; use Magento\Customer\Model\ResourceModel\Customer\Indexer\Collection; use Magento\Framework\App\ResourceConnection\SourceProviderInterface; use Traversable; @@ -25,11 +26,11 @@ class Source implements \IteratorAggregate, \Countable, SourceProviderInterface private $batchSize; /** - * @param \Magento\Customer\Model\ResourceModel\Customer\Indexer\CollectionFactory $collection + * @param CollectionFactory $collectionFactory * @param int $batchSize */ public function __construct( - \Magento\Customer\Model\ResourceModel\Customer\Indexer\CollectionFactory $collectionFactory, + CollectionFactory $collectionFactory, $batchSize = 10000 ) { $this->customerCollection = $collectionFactory->create(); @@ -37,7 +38,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getMainTable() { @@ -45,7 +46,7 @@ public function getMainTable() } /** - * {@inheritdoc} + * @inheritdoc */ public function getIdFieldName() { @@ -53,7 +54,7 @@ public function getIdFieldName() } /** - * {@inheritdoc} + * @inheritdoc */ public function addFieldToSelect($fieldName, $alias = null) { @@ -62,7 +63,7 @@ public function addFieldToSelect($fieldName, $alias = null) } /** - * {@inheritdoc} + * @inheritdoc */ public function getSelect() { @@ -70,7 +71,7 @@ public function getSelect() } /** - * {@inheritdoc} + * @inheritdoc */ public function addFieldToFilter($attribute, $condition = null) { @@ -79,7 +80,7 @@ public function addFieldToFilter($attribute, $condition = null) } /** - * @return int + * @inheritdoc */ public function count() { @@ -105,4 +106,28 @@ public function getIterator() $pageNumber++; } while ($pageNumber <= $lastPage); } + + /** + * Joins Attribute + * + * @param string $alias alias for the joined attribute + * @param string|\Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute + * @param string $bind attribute of the main entity to link with joined $filter + * @param string|null $filter primary key for the joined entity (entity_id default) + * @param string $joinType inner|left + * @param int|null $storeId + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @see Collection::joinAttribute() + */ + public function joinAttribute( + string $alias, + $attribute, + string $bind, + ?string $filter = null, + string $joinType = 'inner', + ?int $storeId = null + ): void { + $this->customerCollection->joinAttribute($alias, $attribute, $bind, $filter, $joinType, $storeId); + } } From 24b7d98215333a1fa391073420391fdd079034b5 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 1 Nov 2018 15:41:58 +0200 Subject: [PATCH 159/310] MAGETWO-95753: [2.3] Cannot save product with Tier Prices --- app/code/Magento/Catalog/Helper/Data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Helper/Data.php b/app/code/Magento/Catalog/Helper/Data.php index c83eb70486c43..3e96763632830 100644 --- a/app/code/Magento/Catalog/Helper/Data.php +++ b/app/code/Magento/Catalog/Helper/Data.php @@ -413,7 +413,7 @@ public function getAttributeHiddenFields() /** * Retrieve Catalog Price Scope * - * @return int/null + * @return int|null */ public function getPriceScope(): ?int { From d353304c65bd9e8c9eff7841f3a6e4deec403e65 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Thu, 1 Nov 2018 15:50:10 +0200 Subject: [PATCH 160/310] MAGETWO-95798: All country state's are shown for USA when shipping form has custom address attributes. --- .../Checkout/Test/Mftf/Section/CheckoutShippingSection.xml | 1 + app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index 494a365ffd507..a8e0c5508ae06 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -32,5 +32,6 @@ <element name="firstShippingMethod" type="radio" selector="//*[@id='checkout-shipping-method-load']//input[@class='radio']"/> <element name="defaultShipping" type="button" selector=".billing-address-details"/> <element name="stateInput" type="input" selector="input[name=region]"/> + <element name="regionOptions" type="select" selector="select[name=region_id] option"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index d090620145105..2609106b1f19b 100755 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -32,6 +32,7 @@ <data key="vat_id">vatData</data> <data key="default_shipping">true</data> <data key="default_billing">true</data> + <data key="region_qty">66</data> </entity> <entity name="US_Address_TX" type="address"> <data key="firstname">John</data> From 099465aed038cbdf2884326d5915acd84867d6e0 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 1 Nov 2018 16:37:19 +0200 Subject: [PATCH 161/310] ENGCOM-3201: Updated integration test. --- .../testsuite/Magento/Customer/Controller/Section/LoadTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php index 3e086a89f8140..3563087d3722b 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php @@ -13,7 +13,7 @@ public function testLoadInvalidSection() $expected = [ 'message' => 'The "section<invalid" section source isn't supported.', ]; - $this->dispatch('/customer/section/load/?sections=section<invalid&update_section_id=false&_=147066166394'); + $this->dispatch('/customer/section/load/?sections=section<invalid&force_new_section_timestamp=false&_=147066166394'); self::assertEquals(json_encode($expected), $this->getResponse()->getBody()); } } From 0ae58d9b877fffedee4ba7eeb40331c63dfcc7a3 Mon Sep 17 00:00:00 2001 From: Dmytro Drozd <ddrozd@magento.com> Date: Thu, 1 Nov 2018 17:10:46 +0200 Subject: [PATCH 162/310] MAGETWO-96026: Create MFTF test for MAGETWO-93973 --- .../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 2 +- ...PricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 9ec0dbf9f262c..249610568aec7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -28,7 +28,7 @@ <element name="advancedPricingLink" type="button" selector="button[data-index='advanced_pricing_button']"/> <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']"/> <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> - <element name="advancedInventoryLink" type="input" selector="//button[contains(@data-index, 'advanced_inventory_button')]"/> + <element name="advancedInventoryLink" type="button" selector="//button[contains(@data-index, 'advanced_inventory_button')]"/> <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']"/> <element name="productWeight" type="input" selector=".admin__field[data-index=weight] input"/> <element name="productWeightSelect" type="select" selector="select[name='product[product_has_weight]']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml index e9158e91432fa..9dbd2eb0c1fae 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml @@ -45,6 +45,7 @@ <!--Step3. Open *Advanced Pricing* pop-up (Click on *Advanced Pricing* link). Click on *Add* button. Fill *0.5* in *Quantity*--> <scrollTo selector="{{AdminProductFormSection.productName}}" stepKey="scrollToProductName"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingLink1"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForAddButton"/> <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickOnCustomerGroupPriceAddButton"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="0.5" stepKey="fillProductTierPriceQty"/> <!--Step4. Close *Advanced Pricing* (Click on button *Done*). Save *prod1* (Click on button *Save*)--> From a0fddaa2777641013b57a62eac9649efc8d79fe8 Mon Sep 17 00:00:00 2001 From: Dmytro Drozd <ddrozd@magento.com> Date: Thu, 1 Nov 2018 18:45:27 +0200 Subject: [PATCH 163/310] MAGETWO-96026: Create MFTF test for MAGETWO-93973 --- ...ingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml index 9dbd2eb0c1fae..6eb78809b187d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml @@ -10,13 +10,13 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> <test name="TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest"> <annotations> - <features value="Tiered pricing and quantity increments work with decimal inventory"/> + <features value="Catalog"/> <stories value="Tiered pricing and quantity increments work with decimal inventory"/> <title value="Tiered pricing and quantity increments work with decimal inventory"/> <description value="Tiered pricing and quantity increments work with decimal inventory"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-91178"/> - <group value="product"/> + <testCaseId value="MAGETWO-93973"/> + <group value="Catalog"/> </annotations> <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> From 547c6fe2f67312c7ba70a8a91adb684766dae98b Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Thu, 1 Nov 2018 11:58:34 -0500 Subject: [PATCH 164/310] MAGETWO-95773: Credit memo is created instead of returning error via invoice refund API for Bundle product - use filtered collection for loop --- app/code/Magento/Sales/Model/Order/ItemRepository.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order/ItemRepository.php b/app/code/Magento/Sales/Model/Order/ItemRepository.php index b1ff747d4f487..cdf5bdf99786e 100644 --- a/app/code/Magento/Sales/Model/Order/ItemRepository.php +++ b/app/code/Magento/Sales/Model/Order/ItemRepository.php @@ -229,7 +229,9 @@ private function addParentItem(OrderItemInterface $orderItem) if ($parentId = $orderItem->getParentItemId()) { $orderItem->setParentItem($this->get($parentId)); } else { - foreach ($orderItem->getOrder()->getAllItems() as $item) { + $orderCollection = $orderItem->getOrder()->getItemsCollection()->filterByParent($orderItem->getItemId()); + + foreach ($orderCollection->getItems() as $item) { if ($item->getParentItemId() === $orderItem->getItemId()) { $item->setParentItem($orderItem); } From 2533e88aea6e5834f632ff631e5a4cce6e1496b0 Mon Sep 17 00:00:00 2001 From: Tommy Wiebell <twiebell@adobe.com> Date: Thu, 1 Nov 2018 16:27:20 -0500 Subject: [PATCH 165/310] MAGETWO-95773: Credit memo is created instead of returning error via invoice refund API for Bundle product - Update unit test coverage to include changed functionality --- .../Unit/Model/Order/ItemRepositoryTest.php | 94 ++++++++++++------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php index 5e1b49c0013e3..7f3626c25d8df 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ItemRepositoryTest.php @@ -145,7 +145,7 @@ public function testGetEmptyEntity() $model->get($orderItemId); } - public function testGet() + public function testGetAsParentWithChild() { $orderItemId = 1; $productType = 'configurable'; @@ -154,18 +154,27 @@ public function testGet() $this->getProductOptionExtensionMock(); $productOption = $this->getProductOptionMock(); - $orderItemMock = $this->getOrderMock($productType, $productOption); + $orderItemMock = $this->getOrderItemMock($productType, $productOption); + + $orderItemCollectionMock = $this->createMock(\Magento\Sales\Model\ResourceModel\Order\Item\Collection::class); + $orderItemCollectionMock->expects($this->once()) + ->method('filterByParent') + ->with($orderItemId) + ->willReturnSelf(); + $orderItemCollectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$orderItemMock]); $orderMock = $this->createMock(\Magento\Sales\Model\Order::class); $orderMock->expects($this->once()) - ->method('getAllItems') - ->willReturn([$orderItemMock]); + ->method('getItemsCollection') + ->willReturn($orderItemCollectionMock); $orderItemMock->expects($this->once()) ->method('load') ->with($orderItemId) ->willReturn($orderItemMock); - $orderItemMock->expects($this->exactly(2)) + $orderItemMock->expects($this->exactly(3)) ->method('getItemId') ->willReturn($orderItemId); $orderItemMock->expects($this->once()) @@ -183,6 +192,45 @@ public function testGet() $this->assertSame($orderItemMock, $model->get($orderItemId)); } + public function testGetAsChild() + { + $orderItemId = 1; + $parentItemId = 66; + $productType = 'configurable'; + + $this->productOptionData = ['option1' => 'value1']; + + $this->getProductOptionExtensionMock(); + $productOption = $this->getProductOptionMock(); + $orderItemMock = $this->getOrderItemMock($productType, $productOption); + + $orderItemMock->expects($this->once()) + ->method('load') + ->with($orderItemId) + ->willReturn($orderItemMock); + $orderItemMock->expects($this->once()) + ->method('getItemId') + ->willReturn($orderItemId); + $orderItemMock->expects($this->exactly(3)) + ->method('getParentItemId') + ->willReturn($parentItemId); + + $this->metadata->expects($this->once()) + ->method('getNewInstance') + ->willReturn($orderItemMock); + + $parentItemMock = $this->createMock(\Magento\Sales\Model\Order\Item::class); + + $model = $this->getModel($orderItemMock, $productType); + $reflectedRegistryProperty = new \ReflectionProperty($model, 'registry'); + $reflectedRegistryProperty->setAccessible(true); + $reflectedRegistryProperty->setValue($model, [$parentItemId => $parentItemMock]); + $this->assertSame($orderItemMock, $model->get($orderItemId)); + + // Assert already registered + $this->assertSame($orderItemMock, $model->get($orderItemId)); + } + public function testGetList() { $productType = 'configurable'; @@ -192,7 +240,7 @@ public function testGetList() ->getMock(); $this->getProductOptionExtensionMock(); $productOption = $this->getProductOptionMock(); - $orderItemMock = $this->getOrderMock($productType, $productOption); + $orderItemMock = $this->getOrderItemMock($productType, $productOption); $searchResultMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Item\Collection::class) ->disableOriginalConstructor() @@ -214,35 +262,12 @@ public function testDeleteById() $orderItemId = 1; $productType = 'configurable'; - $requestMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) - ->disableOriginalConstructor() - ->getMock(); - $orderItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class) ->disableOriginalConstructor() ->getMock(); - - $orderMock = $this->createMock(\Magento\Sales\Model\Order::class); - $orderMock->expects($this->once()) - ->method('getAllItems') - ->willReturn([$orderItemMock]); - $orderItemMock->expects($this->once()) - ->method('load') - ->with($orderItemId) - ->willReturn($orderItemMock); - $orderItemMock->expects($this->exactly(2)) - ->method('getItemId') + ->method('getEntityId') ->willReturn($orderItemId); - $orderItemMock->expects($this->once()) - ->method('getProductType') - ->willReturn($productType); - $orderItemMock->expects($this->once()) - ->method('getBuyRequest') - ->willReturn($requestMock); - $orderItemMock->expects($this->once()) - ->method('getOrder') - ->willReturn($orderMock); $orderItemResourceMock = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class) ->disableOriginalConstructor() @@ -252,15 +277,16 @@ public function testDeleteById() ->with($orderItemMock) ->willReturnSelf(); - $this->metadata->expects($this->once()) - ->method('getNewInstance') - ->willReturn($orderItemMock); $this->metadata->expects($this->exactly(1)) ->method('getMapper') ->willReturn($orderItemResourceMock); $model = $this->getModel($orderItemMock, $productType); + $reflectedRegistryProperty = new \ReflectionProperty($model, 'registry'); + $reflectedRegistryProperty->setAccessible(true); + $reflectedRegistryProperty->setValue($model, [$orderItemId => $orderItemMock]); $this->assertTrue($model->deleteById($orderItemId)); + $this->assertEmpty($reflectedRegistryProperty->getValue($model)); } /** @@ -318,7 +344,7 @@ protected function getModel( * @param \PHPUnit_Framework_MockObject_MockObject $productOption * @return \PHPUnit_Framework_MockObject_MockObject */ - protected function getOrderMock($productType, $productOption) + protected function getOrderItemMock($productType, $productOption) { $requestMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() From 0cf59de0e706b213e836949700a713fdfa9cf4f4 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 2 Nov 2018 11:33:02 +0200 Subject: [PATCH 166/310] Added user permission check --- .../Model/Resolver/CartAddress.php | 30 +++++++++++++++---- .../SetShippingMethodsOnCart.php | 8 +++-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php index f83a112ba5ba8..d2b502688adbc 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php @@ -10,11 +10,13 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteId; +use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; use Magento\QuoteGraphQl\Model\Resolver\Address\AddressDataProvider; /** @@ -27,6 +29,11 @@ class CartAddress implements ResolverInterface */ private $addressDataProvider; + /** + * @var IsCartMutationAllowedForCurrentUser + */ + private $isCartMutationAllowedForCurrentUser; + /** * @var CartRepositoryInterface */ @@ -43,15 +50,18 @@ class CartAddress implements ResolverInterface * @param MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId * @param CartRepositoryInterface $cartRepository * @param AddressDataProvider $addressDataProvider + * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser */ public function __construct( MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId, CartRepositoryInterface $cartRepository, - AddressDataProvider $addressDataProvider + AddressDataProvider $addressDataProvider, + IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; $this->addressDataProvider = $addressDataProvider; + $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; } /** @@ -59,20 +69,30 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { + /* The cart_id is used instead of the model because some parent resolvers do not work + with cart model */ if (!isset($value['cart_id'])) { - // TODO: consider the possibility to pass quote model instead od quote ID throw new LocalizedException(__('"cart_id" value should be specified')); } + $maskedCartId = $value['cart_id']; + try { - $quoteId = $this->maskedQuoteIdToQuoteId->execute($value['cart_id']); + $quoteId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $value['cart_id']]) + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) ); } - // TODO: should we check customer permissions here as well? + if (false === $this->isCartMutationAllowedForCurrentUser->execute($quoteId)) { + throw new GraphQlAuthorizationException( + __( + 'The current user cannot perform operations on cart "%masked_cart_id"', + ['masked_cart_id' => $maskedCartId] + ) + ); + } try { $quote = $this->cartRepository->get($quoteId); diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index 1f35f37d9cf23..7e35f97d9f711 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -72,13 +72,17 @@ class SetShippingMethodsOnCart implements ResolverInterface * @param ArrayManager $arrayManager * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + * @param ShippingInformationManagementInterface $shippingInformationManagement + * @param QuoteAddressFactory $quoteAddressFactory + * @param QuoteAddressResource $quoteAddressResource + * @param ShippingInformationFactory $shippingInformationFactory */ public function __construct( ArrayManager $arrayManager, MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser, ShippingInformationManagementInterface $shippingInformationManagement, - QuoteAddressFactory $quoteAddressFacrory, + QuoteAddressFactory $quoteAddressFactory, QuoteAddressResource $quoteAddressResource, ShippingInformationFactory $shippingInformationFactory ) { @@ -88,7 +92,7 @@ public function __construct( $this->shippingInformationManagement = $shippingInformationManagement; $this->quoteAddressResource = $quoteAddressResource; - $this->quoteAddressFactory = $quoteAddressFacrory; + $this->quoteAddressFactory = $quoteAddressFactory; $this->shippingInformationFactory = $shippingInformationFactory; } From 5c0bcfd2160d549c082d7f51df0562ab674a9fe6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 2 Nov 2018 11:58:47 +0200 Subject: [PATCH 167/310] Refactored flow for new authorization check logic --- .../Model/Resolver/CartAddress.php | 49 ++--------------- .../SetShippingMethodsOnCart.php | 52 +++++-------------- 2 files changed, 19 insertions(+), 82 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php index d2b502688adbc..54bd8fa2a5717 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php @@ -8,15 +8,11 @@ namespace Magento\QuoteGraphQl\Model\Resolver; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteId; -use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; use Magento\QuoteGraphQl\Model\Resolver\Address\AddressDataProvider; /** @@ -29,11 +25,6 @@ class CartAddress implements ResolverInterface */ private $addressDataProvider; - /** - * @var IsCartMutationAllowedForCurrentUser - */ - private $isCartMutationAllowedForCurrentUser; - /** * @var CartRepositoryInterface */ @@ -50,18 +41,15 @@ class CartAddress implements ResolverInterface * @param MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId * @param CartRepositoryInterface $cartRepository * @param AddressDataProvider $addressDataProvider - * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser */ public function __construct( MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId, CartRepositoryInterface $cartRepository, - AddressDataProvider $addressDataProvider, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + AddressDataProvider $addressDataProvider ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; $this->addressDataProvider = $addressDataProvider; - $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; } /** @@ -69,39 +57,12 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - /* The cart_id is used instead of the model because some parent resolvers do not work - with cart model */ - if (!isset($value['cart_id'])) { - throw new LocalizedException(__('"cart_id" value should be specified')); + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); } - $maskedCartId = $value['cart_id']; - - try { - $quoteId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) - ); - } - - if (false === $this->isCartMutationAllowedForCurrentUser->execute($quoteId)) { - throw new GraphQlAuthorizationException( - __( - 'The current user cannot perform operations on cart "%masked_cart_id"', - ['masked_cart_id' => $maskedCartId] - ) - ); - } - - try { - $quote = $this->cartRepository->get($quoteId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%quote_id"', ['quote_id' => $quoteId]) - ); - } + $cart = $value['model']; - return $this->addressDataProvider->getCartAddresses($quote); + return $this->addressDataProvider->getCartAddresses($cart); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php index 7e35f97d9f711..97a7bcea8aa48 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php @@ -12,18 +12,16 @@ use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; -use Magento\QuoteGraphQl\Model\Authorization\IsCartMutationAllowedForCurrentUser; use Magento\Quote\Model\Quote\AddressFactory as QuoteAddressFactory; use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; use Magento\Checkout\Model\ShippingInformationFactory; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; /** * Class SetShippingMethodsOnCart @@ -47,20 +45,15 @@ class SetShippingMethodsOnCart implements ResolverInterface */ private $quoteAddressResource; - /** - * @var MaskedQuoteIdToQuoteIdInterface - */ - private $maskedQuoteIdToQuoteId; - /** * @var ArrayManager */ private $arrayManager; /** - * @var IsCartMutationAllowedForCurrentUser + * @var GetCartForUser */ - private $isCartMutationAllowedForCurrentUser; + private $getCartForUser; /** * @var ShippingInformationManagementInterface @@ -70,8 +63,7 @@ class SetShippingMethodsOnCart implements ResolverInterface /** * SetShippingMethodsOnCart constructor. * @param ArrayManager $arrayManager - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId - * @param IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser + * @param GetCartForUser $getCartForUser * @param ShippingInformationManagementInterface $shippingInformationManagement * @param QuoteAddressFactory $quoteAddressFactory * @param QuoteAddressResource $quoteAddressResource @@ -79,18 +71,15 @@ class SetShippingMethodsOnCart implements ResolverInterface */ public function __construct( ArrayManager $arrayManager, - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - IsCartMutationAllowedForCurrentUser $isCartMutationAllowedForCurrentUser, + GetCartForUser $getCartForUser, ShippingInformationManagementInterface $shippingInformationManagement, QuoteAddressFactory $quoteAddressFactory, QuoteAddressResource $quoteAddressResource, ShippingInformationFactory $shippingInformationFactory ) { $this->arrayManager = $arrayManager; - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->isCartMutationAllowedForCurrentUser = $isCartMutationAllowedForCurrentUser; + $this->getCartForUser = $getCartForUser; $this->shippingInformationManagement = $shippingInformationManagement; - $this->quoteAddressResource = $quoteAddressResource; $this->quoteAddressFactory = $quoteAddressFactory; $this->shippingInformationFactory = $shippingInformationFactory; @@ -111,34 +100,20 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); } - $shippingMethod = reset($shippingMethods); // TODO: provide implementation for multishipping + $shippingMethod = reset($shippingMethods); if (!$shippingMethod['cart_address_id']) { throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); } - if (!$shippingMethod['shipping_carrier_code']) { // FIXME: check the E_WARNING here + if (!$shippingMethod['shipping_carrier_code']) { throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - if (!$shippingMethod['shipping_method_code']) { // FIXME: check the E_WARNING here + if (!$shippingMethod['shipping_method_code']) { throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); } - try { - $cartId = $this->maskedQuoteIdToQuoteId->execute((string) $maskedCartId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $maskedCartId]) - ); - } - - if (false === $this->isCartMutationAllowedForCurrentUser->execute($cartId)) { - throw new GraphQlAuthorizationException( - __( - 'The current user cannot perform operations on cart "%masked_cart_id"', - ['masked_cart_id' => $maskedCartId] - ) - ); - } + $userId = $context->getUserId(); + $cart = $this->getCartForUser->execute((string) $maskedCartId, $userId); $quoteAddress = $this->quoteAddressFactory->create(); $this->quoteAddressResource->load($quoteAddress, $shippingMethod['cart_address_id']); @@ -153,7 +128,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $shippingInformation->setShippingMethodCode($shippingMethod['shipping_method_code']); try { - $this->shippingInformationManagement->saveAddressInformation($cartId, $shippingInformation); + $this->shippingInformationManagement->saveAddressInformation($cart->getId(), $shippingInformation); } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException(__($exception->getMessage())); } catch (StateException $exception) { @@ -164,7 +139,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return [ 'cart' => [ - 'cart_id' => $maskedCartId + 'cart_id' => $maskedCartId, + 'model' => $cart ] ]; } From c98c3d5142d73789e8233deb4aa90f172d716c21 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 2 Nov 2018 12:29:44 +0200 Subject: [PATCH 168/310] Logic for setting shipping method is moved to a separate class --- .../SetShippingMethodOnCart.php} | 84 ++++------------ .../Resolver/SetShippingMethodsOnCart.php | 99 +++++++++++++++++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 3 files changed, 119 insertions(+), 66 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/{Resolver/ShippingMethod/SetShippingMethodsOnCart.php => Cart/SetShippingMethodOnCart.php} (54%) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php similarity index 54% rename from app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php index 97a7bcea8aa48..7f945dca2fd76 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingMethod/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php @@ -5,30 +5,26 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Resolver\ShippingMethod; +namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Checkout\Api\ShippingInformationManagementInterface; -use Magento\Checkout\Model\ShippingInformation; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\Stdlib\ArrayManager; +use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\AddressFactory as QuoteAddressFactory; use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource; use Magento\Checkout\Model\ShippingInformationFactory; -use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\Checkout\Api\ShippingInformationManagementInterface; +use Magento\Checkout\Model\ShippingInformation; /** * Class SetShippingMethodsOnCart * - * Mutation resolver for setting shipping methods for shopping cart + * Set shipping method for a specified shopping cart address */ -class SetShippingMethodsOnCart implements ResolverInterface +class SetShippingMethodOnCart { /** * @var ShippingInformationFactory @@ -45,40 +41,23 @@ class SetShippingMethodsOnCart implements ResolverInterface */ private $quoteAddressResource; - /** - * @var ArrayManager - */ - private $arrayManager; - - /** - * @var GetCartForUser - */ - private $getCartForUser; - /** * @var ShippingInformationManagementInterface */ private $shippingInformationManagement; /** - * SetShippingMethodsOnCart constructor. - * @param ArrayManager $arrayManager - * @param GetCartForUser $getCartForUser * @param ShippingInformationManagementInterface $shippingInformationManagement * @param QuoteAddressFactory $quoteAddressFactory * @param QuoteAddressResource $quoteAddressResource * @param ShippingInformationFactory $shippingInformationFactory */ public function __construct( - ArrayManager $arrayManager, - GetCartForUser $getCartForUser, ShippingInformationManagementInterface $shippingInformationManagement, QuoteAddressFactory $quoteAddressFactory, QuoteAddressResource $quoteAddressResource, ShippingInformationFactory $shippingInformationFactory ) { - $this->arrayManager = $arrayManager; - $this->getCartForUser = $getCartForUser; $this->shippingInformationManagement = $shippingInformationManagement; $this->quoteAddressResource = $quoteAddressResource; $this->quoteAddressFactory = $quoteAddressFactory; @@ -86,37 +65,19 @@ public function __construct( } /** - * @inheritdoc + * Sets shipping method for a specified shopping cart address + * + * @param Quote $cart + * @param int $cartAddressId + * @param string $carrierCode + * @param string $methodCode + * @throws GraphQlInputException + * @throws GraphQlNoSuchEntityException */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + public function execute(Quote $cart, int $cartAddressId, string $carrierCode, string $methodCode): void { - $shippingMethods = $this->arrayManager->get('input/shipping_methods', $args); - $maskedCartId = $this->arrayManager->get('input/cart_id', $args); - - if (!$maskedCartId) { - throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); - } - if (!$shippingMethods) { - throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); - } - - $shippingMethod = reset($shippingMethods); - - if (!$shippingMethod['cart_address_id']) { - throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); - } - if (!$shippingMethod['shipping_carrier_code']) { - throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); - } - if (!$shippingMethod['shipping_method_code']) { - throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); - } - - $userId = $context->getUserId(); - $cart = $this->getCartForUser->execute((string) $maskedCartId, $userId); - $quoteAddress = $this->quoteAddressFactory->create(); - $this->quoteAddressResource->load($quoteAddress, $shippingMethod['cart_address_id']); + $this->quoteAddressResource->load($quoteAddress, $cartAddressId); /** @var ShippingInformation $shippingInformation */ $shippingInformation = $this->shippingInformationFactory->create(); @@ -124,8 +85,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value /* If the address is not a shipping address (but billing) the system will find the proper shipping address for the selected cart and set the information there (actual for single shipping address) */ $shippingInformation->setShippingAddress($quoteAddress); - $shippingInformation->setShippingCarrierCode($shippingMethod['shipping_carrier_code']); - $shippingInformation->setShippingMethodCode($shippingMethod['shipping_method_code']); + $shippingInformation->setShippingCarrierCode($carrierCode); + $shippingInformation->setShippingMethodCode($methodCode); try { $this->shippingInformationManagement->saveAddressInformation($cart->getId(), $shippingInformation); @@ -136,12 +97,5 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } catch (InputException $exception) { throw new GraphQlInputException(__($exception->getMessage())); } - - return [ - 'cart' => [ - 'cart_id' => $maskedCartId, - 'model' => $cart - ] - ]; } -} +} \ No newline at end of file diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php new file mode 100644 index 0000000000000..920829f5d67b1 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\Stdlib\ArrayManager; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\QuoteGraphQl\Model\Cart\SetShippingMethodOnCart; + +/** + * Class SetShippingMethodsOnCart + * + * Mutation resolver for setting shipping methods for shopping cart + */ +class SetShippingMethodsOnCart implements ResolverInterface +{ + /** + * @var SetShippingMethodOnCart + */ + private $setShippingMethodOnCart; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @var GetCartForUser + */ + private $getCartForUser; + + /** + * @param ArrayManager $arrayManager + * @param GetCartForUser $getCartForUser + * @param SetShippingMethodOnCart $setShippingMethodOnCart + */ + public function __construct( + ArrayManager $arrayManager, + GetCartForUser $getCartForUser, + SetShippingMethodOnCart $setShippingMethodOnCart + ) { + $this->arrayManager = $arrayManager; + $this->getCartForUser = $getCartForUser; + $this->setShippingMethodOnCart = $setShippingMethodOnCart; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $shippingMethods = $this->arrayManager->get('input/shipping_methods', $args); + $maskedCartId = $this->arrayManager->get('input/cart_id', $args); + + if (!$maskedCartId) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + if (!$shippingMethods) { + throw new GraphQlInputException(__('Required parameter "shipping_methods" is missing')); + } + + $shippingMethod = reset($shippingMethods); // This point can be extended for multishipping + + if (!$shippingMethod['cart_address_id']) { + throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); + } + if (!$shippingMethod['shipping_carrier_code']) { + throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); + } + if (!$shippingMethod['shipping_method_code']) { + throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); + } + + $userId = $context->getUserId(); + $cart = $this->getCartForUser->execute((string) $maskedCartId, $userId); + + $this->setShippingMethodOnCart->execute( + $cart, + $shippingMethod['cart_address_id'], + $shippingMethod['shipping_carrier_code'], + $shippingMethod['shipping_method_code'] + ); + + return [ + 'cart' => [ + 'cart_id' => $maskedCartId, + 'model' => $cart + ] + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index cbc56bfaea66f..ce982952f1aee 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -11,7 +11,7 @@ type Mutation { removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput - setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingMethod\\SetShippingMethodsOnCart") + setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart") addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") } From c8dbdf4a1b94c5d3f6b9f3211f270c0168d74396 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Fri, 2 Nov 2018 12:37:27 +0200 Subject: [PATCH 169/310] graphQl: fixed multi shipping model --- .../MultiShipping/ShippingItemsMapper.php | 2 +- .../Magento/QuoteGraphQl/etc/graphql/di.xml | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/QuoteGraphQl/etc/graphql/di.xml diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php index c9cefc421a544..ad5207814db7c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php @@ -14,7 +14,7 @@ class ShippingItemsMapper { /** - * Converts shipping address input array into shipping items information array + * Converts shipping address input array into shipping items information array * Array structure: * array( * $cartItemId => array( diff --git a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml new file mode 100644 index 0000000000000..43941c9740048 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <virtualType name="multishippingPaymentSpecification" type="Magento\Payment\Model\Method\Specification\Composite"> + <arguments> + <argument name="specifications" xsi:type="array"> + <item name="enabled" xsi:type="string">Magento\Multishipping\Model\Payment\Method\Specification\Enabled</item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Multishipping\Block\Checkout\Billing"> + <arguments> + <argument name="paymentSpecification" xsi:type="object">multishippingPaymentSpecification</argument> + </arguments> + </type> + <type name="Magento\Multishipping\Model\Checkout\Type\Multishipping"> + <arguments> + <argument name="paymentSpecification" xsi:type="object">multishippingPaymentSpecification</argument> + </arguments> + </type> +</config> From 51e240203aad14b5a0dcce42caf9041c92fc138d Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Fri, 2 Nov 2018 15:29:33 +0300 Subject: [PATCH 170/310] MAGETWO-91684: Issue with Newsletter subscriptions - Ensure that customer id is presented. --- .../Model/ResourceModel/Subscriber.php | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php index 5bd89c2798ce3..f9e9d57bf4b40 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php @@ -132,28 +132,34 @@ public function loadByCustomerData(\Magento\Customer\Api\Data\CustomerInterface { $storeIds = $this->storeManager->getWebsite($customer->getWebsiteId())->getStoreIds(); - $select = $this->connection - ->select() - ->from($this->getMainTable()) - ->where('customer_id = ?', $customer->getId()) - ->where('store_id IN (?)', $storeIds); - - $result = $this->connection->fetchRow($select); - - if ($result) { - return $result; + if ($customer->getId()) { + $select = $this->connection + ->select() + ->from($this->getMainTable()) + ->where('customer_id = ?', $customer->getId()) + ->where('store_id IN (?)', $storeIds) + ->limit(1); + + $result = $this->connection->fetchRow($select); + + if ($result) { + return $result; + } } - $select = $this->connection - ->select() - ->from($this->getMainTable()) - ->where('subscriber_email = ?', $customer->getEmail()) - ->where('store_id IN (?)', $storeIds); + if ($customer->getEmail()) { + $select = $this->connection + ->select() + ->from($this->getMainTable()) + ->where('subscriber_email = ?', $customer->getEmail()) + ->where('store_id IN (?)', $storeIds) + ->limit(1); - $result = $this->connection->fetchRow($select); + $result = $this->connection->fetchRow($select); - if ($result) { - return $result; + if ($result) { + return $result; + } } return []; From 16731a0812d9a74fb55d4acc8e68af3716e1c728 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 2 Nov 2018 15:00:55 +0200 Subject: [PATCH 171/310] MAGETWO-96007: Customer address issue when creating or updating via API --- .../ResourceModel/CustomerRepositoryTest.php | 161 +++++++++++++----- 1 file changed, 114 insertions(+), 47 deletions(-) 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 1af093bea06cc..15afc87405ffd 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php @@ -8,8 +8,24 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Customer\Api\Data\RegionInterfaceFactory; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\CustomerRegistry; use Magento\Framework\Api\SortOrder; +use Magento\Framework\Config\CacheInterface; +use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SortOrderBuilder; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Customer\Model\Customer; /** * Checks Customer insert, update, search with repository @@ -24,60 +40,65 @@ class CustomerRepositoryTest extends \PHPUnit\Framework\TestCase /** @var CustomerRepositoryInterface */ private $customerRepository; - /** @var \Magento\Framework\ObjectManagerInterface */ + /** @var ObjectManagerInterface */ private $objectManager; - /** @var \Magento\Customer\Api\Data\CustomerInterfaceFactory */ + /** @var CustomerInterfaceFactory */ private $customerFactory; - /** @var \Magento\Customer\Api\Data\AddressInterfaceFactory */ + /** @var AddressInterfaceFactory */ private $addressFactory; - /** @var \Magento\Customer\Api\Data\RegionInterfaceFactory */ + /** @var RegionInterfaceFactory */ private $regionFactory; - /** @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ + /** @var ExtensibleDataObjectConverter */ private $converter; - /** @var \Magento\Framework\Api\DataObjectHelper */ + /** @var DataObjectHelper */ protected $dataObjectHelper; - /** @var \Magento\Framework\Encryption\EncryptorInterface */ + /** @var EncryptorInterface */ protected $encryptor; - /** @var \Magento\Customer\Model\CustomerRegistry */ + /** @var CustomerRegistry */ protected $customerRegistry; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->customerRepository = - $this->objectManager->create(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $this->customerFactory = - $this->objectManager->create(\Magento\Customer\Api\Data\CustomerInterfaceFactory::class); - $this->addressFactory = $this->objectManager->create(\Magento\Customer\Api\Data\AddressInterfaceFactory::class); - $this->regionFactory = $this->objectManager->create(\Magento\Customer\Api\Data\RegionInterfaceFactory::class); - $this->accountManagement = - $this->objectManager->create(\Magento\Customer\Api\AccountManagementInterface::class); - $this->converter = $this->objectManager->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class); - $this->dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class); - $this->encryptor = $this->objectManager->create(\Magento\Framework\Encryption\EncryptorInterface::class); - $this->customerRegistry = $this->objectManager->create(\Magento\Customer\Model\CustomerRegistry::class); - - /** @var \Magento\Framework\Config\CacheInterface $cache */ - $cache = $this->objectManager->create(\Magento\Framework\Config\CacheInterface::class); + $this->customerRepository = $this->objectManager->create(CustomerRepositoryInterface::class); + $this->customerFactory = $this->objectManager->create(CustomerInterfaceFactory::class); + $this->addressFactory = $this->objectManager->create(AddressInterfaceFactory::class); + $this->regionFactory = $this->objectManager->create(RegionInterfaceFactory::class); + $this->accountManagement = $this->objectManager->create(AccountManagementInterface::class); + $this->converter = $this->objectManager->create(ExtensibleDataObjectConverter::class); + $this->dataObjectHelper = $this->objectManager->create(DataObjectHelper::class); + $this->encryptor = $this->objectManager->create(EncryptorInterface::class); + $this->customerRegistry = $this->objectManager->create(CustomerRegistry::class); + + /** @var CacheInterface $cache */ + $cache = $this->objectManager->create(CacheInterface::class); $cache->remove('extension_attributes_config'); } + /** + * @inheritdoc + */ protected function tearDown() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */ - $customerRegistry = $objectManager->get(\Magento\Customer\Model\CustomerRegistry::class); + $customerRegistry = $objectManager->get(CustomerRegistry::class); $customerRegistry->remove(1); } /** + * Check if first name update was successful + * * @magentoDbIsolation enabled */ public function testCreateCustomerNewThenUpdateFirstName() @@ -99,7 +120,7 @@ public function testCreateCustomerNewThenUpdateFirstName() $newCustomerFirstname = 'New First Name'; $updatedCustomer = $this->customerFactory->create(); $this->dataObjectHelper->mergeDataObjects( - \Magento\Customer\Api\Data\CustomerInterface::class, + CustomerInterface::class, $updatedCustomer, $customer ); @@ -112,6 +133,8 @@ public function testCreateCustomerNewThenUpdateFirstName() } /** + * Test create new customer + * * @magentoDbIsolation enabled */ public function testCreateNewCustomer() @@ -139,6 +162,8 @@ public function testCreateNewCustomer() } /** + * Test update customer + * * @dataProvider updateCustomerDataProvider * @magentoAppArea frontend * @magentoDataFixture Magento/Customer/_files/customer.php @@ -168,7 +193,7 @@ public function testUpdateCustomer($defaultBilling, $defaultShipping) $this->dataObjectHelper->populateWithArray( $customerDetails, $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); $this->customerRepository->save($customerDetails, $newPasswordHash); $customerAfter = $this->customerRepository->getById($existingCustomerId); @@ -187,12 +212,12 @@ public function testUpdateCustomer($defaultBilling, $defaultShipping) $attributesBefore = $this->converter->toFlatArray( $customerBefore, [], - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); $attributesAfter = $this->converter->toFlatArray( $customerAfter, [], - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); // ignore 'updated_at' unset($attributesBefore['updated_at']); @@ -215,6 +240,8 @@ public function testUpdateCustomer($defaultBilling, $defaultShipping) } /** + * Test update customer address + * * @magentoAppArea frontend * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php @@ -233,14 +260,14 @@ public function testUpdateCustomerAddress() $this->dataObjectHelper->populateWithArray( $newAddressDataObject, $newAddress, - \Magento\Customer\Api\Data\AddressInterface::class + AddressInterface::class ); $newAddressDataObject->setRegion($addresses[0]->getRegion()); $newCustomerEntity = $this->customerFactory->create(); $this->dataObjectHelper->populateWithArray( $newCustomerEntity, $customerDetails, - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); $newCustomerEntity->setId($customerId) ->setAddresses([$newAddressDataObject, $addresses[1]]); @@ -256,6 +283,8 @@ public function testUpdateCustomerAddress() } /** + * Test preserve all addresses after customer update + * * @magentoAppArea frontend * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php @@ -265,22 +294,37 @@ public function testUpdateCustomerPreserveAllAddresses() $customerId = 1; $customer = $this->customerRepository->getById($customerId); $customerDetails = $customer->__toArray(); + $defaultBilling = $customerDetails['default_billing']; + $defaultShipping = $customerDetails['default_shipping']; $newCustomerEntity = $this->customerFactory->create(); $this->dataObjectHelper->populateWithArray( $newCustomerEntity, $customerDetails, - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); $newCustomerEntity->setId($customer->getId()) ->setAddresses(null); $this->customerRepository->save($newCustomerEntity); $newCustomerDetails = $this->customerRepository->getById($customerId); + $newCustomerArray = $newCustomerDetails->__toArray(); //Verify that old addresses are still present $this->assertEquals(2, count($newCustomerDetails->getAddresses())); + $this->assertEquals( + $defaultBilling, + $newCustomerArray['default_billing'], + "Default billing invalid value" + ); + $this->assertEquals( + $defaultShipping, + $newCustomerArray['default_shipping'], + "Default shipping invalid value" + ); } /** + * Test update delete all addresses with empty arrays + * * @magentoAppArea frontend * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php @@ -294,18 +338,31 @@ public function testUpdateCustomerDeleteAllAddressesWithEmptyArray() $this->dataObjectHelper->populateWithArray( $newCustomerEntity, $customerDetails, - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); $newCustomerEntity->setId($customer->getId()) ->setAddresses([]); $this->customerRepository->save($newCustomerEntity); $newCustomerDetails = $this->customerRepository->getById($customerId); + $newCustomerArray = $newCustomerDetails->__toArray(); //Verify that old addresses are removed $this->assertEquals(0, count($newCustomerDetails->getAddresses())); + $this->assertEquals( + $newCustomerArray['default_billing'], + null, + "Default billing invalid value" + ); + $this->assertEquals( + $newCustomerArray['default_shipping'], + null, + "Default shipping invalid value" + ); } /** + * Test search customers + * * @param \Magento\Framework\Api\Filter[] $filters * @param \Magento\Framework\Api\Filter[] $filterGroup * @param array $expectedResult array of expected results indexed by ID @@ -317,9 +374,8 @@ public function testUpdateCustomerDeleteAllAddressesWithEmptyArray() */ public function testSearchCustomers($filters, $filterGroup, $expectedResult) { - /** @var \Magento\Framework\Api\SearchCriteriaBuilder $searchBuilder */ - $searchBuilder = Bootstrap::getObjectManager() - ->create(\Magento\Framework\Api\SearchCriteriaBuilder::class); + /** @var SearchCriteriaBuilder $searchBuilder */ + $searchBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); foreach ($filters as $filter) { $searchBuilder->addFilters([$filter]); } @@ -346,19 +402,19 @@ public function testSearchCustomers($filters, $filterGroup, $expectedResult) */ public function testSearchCustomersOrder() { - /** @var \Magento\Framework\Api\SearchCriteriaBuilder $searchBuilder */ + /** @var SearchCriteriaBuilder $searchBuilder */ $objectManager = Bootstrap::getObjectManager(); - $searchBuilder = $objectManager->create(\Magento\Framework\Api\SearchCriteriaBuilder::class); + $searchBuilder = $objectManager->create(SearchCriteriaBuilder::class); // Filter for 'firstname' like 'First' - $filterBuilder = $objectManager->create(\Magento\Framework\Api\FilterBuilder::class); + $filterBuilder = $objectManager->create(FilterBuilder::class); $firstnameFilter = $filterBuilder->setField('firstname') ->setConditionType('like') ->setValue('First%') ->create(); $searchBuilder->addFilters([$firstnameFilter]); // Search ascending order - $sortOrderBuilder = $objectManager->create(\Magento\Framework\Api\SortOrderBuilder::class); + $sortOrderBuilder = $objectManager->create(SortOrderBuilder::class); $sortOrder = $sortOrderBuilder ->setField('lastname') ->setDirection(SortOrder::SORT_ASC) @@ -383,6 +439,8 @@ public function testSearchCustomersOrder() } /** + * Test delete + * * @magentoAppArea adminhtml * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoAppIsolation enabled @@ -393,12 +451,14 @@ public function testDelete() $customer = $this->customerRepository->get($fixtureCustomerEmail); $this->customerRepository->delete($customer); /** Ensure that customer was deleted */ - $this->expectException(\Magento\Framework\Exception\NoSuchEntityException::class); + $this->expectException(NoSuchEntityException::class); $this->expectExceptionMessage('No such entity with email = customer@example.com, websiteId = 1'); $this->customerRepository->get($fixtureCustomerEmail); } /** + * Test delete by id + * * @magentoAppArea adminhtml * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoAppIsolation enabled @@ -409,7 +469,7 @@ public function testDeleteById() $fixtureCustomerId = 1; $this->customerRepository->deleteById($fixtureCustomerId); /** Ensure that customer was deleted */ - $this->expectException(\Magento\Framework\Exception\NoSuchEntityException::class); + $this->expectException(NoSuchEntityException::class); $this->expectExceptionMessage('No such entity with email = customer@example.com, websiteId = 1'); $this->customerRepository->get($fixtureCustomerEmail); } @@ -433,9 +493,14 @@ public function updateCustomerDataProvider() ]; } + /** + * Search customer data provider + * + * @return array + */ public function searchCustomersDataProvider() { - $builder = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\FilterBuilder::class); + $builder = Bootstrap::getObjectManager()->create(FilterBuilder::class); return [ 'Customer with specific email' => [ [$builder->setField('email')->setValue('customer@search.example.com')->create()], @@ -485,9 +550,9 @@ protected function expectedDefaultShippingsInCustomerModelAttributes( $defaultShipping ) { /** - * @var \Magento\Customer\Model\Customer $customer + * @var Customer $customer */ - $customer = $this->objectManager->create(\Magento\Customer\Model\Customer::class); + $customer = $this->objectManager->create(Customer::class); /** @var \Magento\Customer\Model\Customer $customer */ $customer->load($customerId); $this->assertEquals( @@ -503,6 +568,8 @@ protected function expectedDefaultShippingsInCustomerModelAttributes( } /** + * Test update default shipping and default billing address + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDbIsolation enabled */ @@ -530,13 +597,13 @@ public function testUpdateDefaultShippingAndDefaultBillingTest() $this->assertEquals( $savedCustomer->getDefaultBilling(), $oldDefaultBilling, - 'Default billing shoud not be overridden' + 'Default billing should not be overridden' ); $this->assertEquals( $savedCustomer->getDefaultShipping(), $oldDefaultShipping, - 'Default shipping shoud not be overridden' + 'Default shipping should not be overridden' ); } } From c720d972108e6991f127f6f71e63485290dabe61 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Fri, 2 Nov 2018 15:05:08 +0200 Subject: [PATCH 172/310] magento/magento2:#18979 - API: Bundle Product Option Repository Delete method removes incorrect option --- app/code/Magento/Bundle/Model/OptionRepository.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Bundle/Model/OptionRepository.php b/app/code/Magento/Bundle/Model/OptionRepository.php index 59e658b08df28..a4d579a44174a 100644 --- a/app/code/Magento/Bundle/Model/OptionRepository.php +++ b/app/code/Magento/Bundle/Model/OptionRepository.php @@ -160,10 +160,9 @@ public function delete(\Magento\Bundle\Api\Data\OptionInterface $option) */ public function deleteById($sku, $optionId) { - $product = $this->getProduct($sku); - $optionCollection = $this->type->getOptionsCollection($product); - $optionCollection->setIdFilter($optionId); - $hasBeenDeleted = $this->delete($optionCollection->getFirstItem()); + /** @var \Magento\Bundle\Api\Data\OptionInterface $option */ + $option = $this->get($sku, $optionId); + $hasBeenDeleted = $this->delete($option); return $hasBeenDeleted; } From 936a22328f429b7e0dd631b6b20831ea91695cb6 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 2 Nov 2018 15:23:16 +0200 Subject: [PATCH 173/310] Added setting shipping method on cart test coverage concept --- .../Quote/SetShippingMethodOnCartTest.php | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php new file mode 100644 index 0000000000000..64c7cbf9fd42e --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for setting shipping methods on cart + */ +class SetShippingMethodOnCartTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->create(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetShippingOnCart() + { + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + + $shippingAddress = $this->quote->getShippingAddress(); + $shippingAddressId = $shippingAddress->getId(); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $query = <<<QUERY +mutation { + setShippingMethodsOnCart(input: + { + cart_id: "$maskedQuoteId", + shipping_methods: [ + { + shipping_method_code: "flatrate" + shipping_carrier_code: "flatrate" + cart_address_id: $shippingAddressId + } + ]}) { + + cart { + cart_id, + addresses { + firstname + lastname + company + address_type + city + street + region { + code + label + } + postcode + country { + code + label + } + + selected_shipping_method { + code + label + } + } + } + } +} + +QUERY; + + $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $response = $this->graphQlQuery($query, [], '', $headerMap); + + self::assertArrayHasKey('setShippingMethodsOnCart', $response); + self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); + self::assertEquals($maskedQuoteId, $response['setShippingMethodsOnCart']['cart']['cart_id']); + + // TODO: check shipping method code and description + } + + // TODO: cover all other cases +} From a30654fb794e4517cdfec07ac431824e2243dec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szyma=C5=84ski?= <bartlomiej.szymanski@bold.net.pl> Date: Fri, 2 Nov 2018 14:29:17 +0100 Subject: [PATCH 174/310] Replace GraphQlInputException on \LogicException --- .../Model/LayerFilterItemTypeResolverComposite.php | 4 +--- .../Model/ProductLinkTypeResolverComposite.php | 4 +--- app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php | 7 +------ 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php b/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php index 20e5590117556..e04b5d1bf67ff 100644 --- a/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php +++ b/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php @@ -43,9 +43,7 @@ public function resolveType(array $data) : string } } if (empty($resolvedType)) { - throw new GraphQlInputException( - __('Concrete type for %1 not implemented', ['ProductLinksInterface']) - ); + throw new \LogicException('Cannot resolve layered filter type'); } } } diff --git a/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php b/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php index 937e3921758dc..5f6d1a65519f8 100644 --- a/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php +++ b/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php @@ -50,9 +50,7 @@ public function resolveType(array $data) : string } if (!$resolvedType) { - throw new GraphQlInputException( - __('Concrete type for %1 not implemented', ['ProductLinksInterface']) - ); + throw new \LogicException('Cannot resolve type'); } } } diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php b/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php index cbea3a86bbddd..b1d7aae5df101 100644 --- a/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php +++ b/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php @@ -71,12 +71,7 @@ public function getType(string $attributeCode, string $entityType) : string try { $type = $this->typeProcessor->translateTypeName($type); } catch (\InvalidArgumentException $exception) { - throw new GraphQlInputException( - __('Type %1 has no internal representation declared.', [$type]), - null, - 0, - false - ); + throw new \LogicException('Cannot resolve EAV type'); } } else { $type = $type === 'double' ? 'float' : $type; From 144ea18392229aef98c5a21165fba106bd97840d Mon Sep 17 00:00:00 2001 From: Lars Roettig <l.roettig@techdivision.com> Date: Fri, 2 Nov 2018 15:34:08 +0100 Subject: [PATCH 175/310] CodeReviewChange --- .../Magento/Store/Model/Config/Importer/Processor/Create.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php index 3142955ac5988..9d1a7a38ede68 100644 --- a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php +++ b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php @@ -177,7 +177,10 @@ private function createGroups(array $items, array $data) ); $group = $this->groupFactory->create(); - $group->setRootCategoryId(0); + if (!isset($groupData['root_categry_id'])) { + $groupData['root_categry_id'] = 0; + } + $group->setData($groupData); $group->getResource()->save($group); From 0a6c0b5a792dbba00295bea9b9b902c7b9598205 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Fri, 2 Nov 2018 17:15:09 +0200 Subject: [PATCH 176/310] graphQl: removed multishipping implementation, fixed namespaces --- .../SetShippingAddressOnCart.php} | 19 ++- .../Resolver/SetShippingAddressesOnCart.php | 101 +++++++++++++ .../MultiShipping.php | 76 ---------- .../MultiShipping/ShippingItemsMapper.php | 43 ------ .../SetShippingAddressesOnCart.php | 133 ------------------ .../Magento/QuoteGraphQl/etc/schema.graphqls | 3 +- 6 files changed, 117 insertions(+), 258 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/{Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php => Cart/SetShippingAddressOnCart.php} (85%) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php similarity index 85% rename from app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php index feeb333683f76..43e58bb807672 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/SingleShipping.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart; +namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Authorization\Model\UserContextInterface; use Magento\Customer\Api\Data\AddressInterface; @@ -16,7 +16,12 @@ use Magento\Quote\Model\ShippingAddressManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; -class SingleShipping +/** + * Class SetShippingAddressOnCart + * + * Set shipping address for a specified shopping cart + */ +class SetShippingAddressOnCart { /** * @var ShippingAddressManagementInterface @@ -51,11 +56,17 @@ public function __construct( /** * @param ContextInterface $context * @param int $cartId - * @param array $shippingAddress + * @param array $shippingAddresses * @return void */ - public function setAddress(ContextInterface $context, int $cartId, array $shippingAddress): void + public function setAddresses(ContextInterface $context, int $cartId, array $shippingAddresses): void { + if (count($shippingAddresses) > 1) { + throw new GraphQlInputException( + __('Multiple address does not allowed here!') + ); + } + $shippingAddress = current($shippingAddresses); $customerAddressId = $shippingAddress['customer_address_id'] ?? null; $addressInput = $shippingAddress['address'] ?? null; diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php new file mode 100644 index 0000000000000..fcb4255a75eb7 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\Stdlib\ArrayManager; +use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\Quote\Model\ShippingAddressManagementInterface; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\QuoteGraphQl\Model\Cart\SetShippingAddressOnCart; + +/** + * Class SetShippingAddressesOnCart + * + * Mutation resolver for setting shipping addresses for shopping cart + */ +class SetShippingAddressesOnCart implements ResolverInterface +{ + /** + * @var MaskedQuoteIdToQuoteIdInterface + */ + private $maskedQuoteIdToQuoteId; + + /** + * @var SetShippingAddressOnCart + */ + private $setShippingAddressOnCart; + + /** + * @var ShippingAddressManagementInterface + */ + private $shippingAddressManagement; + + /** + * @var GetCartForUser + */ + private $getCartForUser; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + * @param SetShippingAddressOnCart $setShippingAddressOnCart + * @param ShippingAddressManagementInterface $shippingAddressManagement + * @param GetCartForUser $getCartForUser + * @param ArrayManager $arrayManager + */ + public function __construct( + MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, + SetShippingAddressOnCart $setShippingAddressOnCart, + ShippingAddressManagementInterface $shippingAddressManagement, + GetCartForUser $getCartForUser, + ArrayManager $arrayManager + ) { + $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->setShippingAddressOnCart = $setShippingAddressOnCart; + $this->shippingAddressManagement = $shippingAddressManagement; + $this->getCartForUser = $getCartForUser; + $this->arrayManager = $arrayManager; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $shippingAddresses = $this->arrayManager->get('input/shipping_addresses', $args); + $maskedCartId = $this->arrayManager->get('input/cart_id', $args); + + if (!$maskedCartId) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + if (!$shippingAddresses) { + throw new GraphQlInputException(__('Required parameter "shipping_addresses" is missing')); + } + + $maskedCartId = $args['input']['cart_id']; + $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); + $cartId = (int)$cart->getEntityId(); + + $this->setShippingAddressOnCart->setAddresses($context, $cartId, $shippingAddresses); + + return [ + 'cart' => [ + 'cart_id' => $maskedCartId, + 'model' => $cart + ] + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php deleted file mode 100644 index 318fd9361af5a..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart; - -use Magento\Authorization\Model\UserContextInterface; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; -use Magento\Multishipping\Model\Checkout\Type\Multishipping as MultishippingModel; -use Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart\MultiShipping\ShippingItemsMapper; - -class MultiShipping -{ - /** - * @var MultishippingModel - */ - private $multishippingModel; - - /** - * @var ShippingItemsMapper - */ - private $shippingItemsInformationMapper; - - /** - * @param MultishippingModel $multishippingModel - * @param ShippingItemsMapper $shippingItemsInformationMapper - */ - public function __construct( - MultishippingModel $multishippingModel, - ShippingItemsMapper $shippingItemsInformationMapper - ) { - $this->multishippingModel = $multishippingModel; - $this->shippingItemsInformationMapper = $shippingItemsInformationMapper; - } - - /** - * @param ContextInterface $context - * @param int $cartId - * @param array $shippingAddresses - */ - public function setAddresses(ContextInterface $context, int $cartId, array $shippingAddresses): void - { - if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { - throw new GraphQlAuthorizationException( - __( - 'Multishipping allowed only for authorized customers' - ) - ); - } - - $shippingItemsInformation = []; - foreach ($shippingAddresses as $shippingAddress) { - $customerAddressId = $shippingAddress['customer_address_id'] ?? null; - $cartItems = $shippingAddress['cart_items'] ?? null; - if (!$customerAddressId) { - throw new GraphQlInputException(__('Parameter "customer_address_id" is required for multishipping')); - } - if (!$cartItems) { - throw new GraphQlInputException(__('Parameter "cart_items" is required for multishipping')); - } - - $shippingItemsInformation = array_merge( - $shippingItemsInformation, - $this->shippingItemsInformationMapper->map($shippingAddress) - ); - } - - //TODO: multishipping model works with session. Do we need to avoid it? - $this->multishippingModel->setShippingItemsInformation($shippingItemsInformation); - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php deleted file mode 100644 index ad5207814db7c..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressOnCart/MultiShipping/ShippingItemsMapper.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart\MultiShipping; - -/** - * Shipping address to shipping items mapper - */ -class ShippingItemsMapper -{ - - /** - * Converts shipping address input array into shipping items information array - * Array structure: - * array( - * $cartItemId => array( - * 'qty' => $qty, - * 'address' => $customerAddressId - * ) - * ) - * - * @param array $shippingAddress - * @return array - */ - public function map(array $shippingAddress): array - { - $shippingItemsInformation = []; - foreach ($shippingAddress['cart_items'] as $cartItem) { - $shippingItemsInformation[] = [ - $cartItem['cart_item_id'] => [ - 'qty' => $cartItem['quantity'], - 'address' => $shippingAddress['customer_address_id'] - ] - ]; - } - - return $shippingItemsInformation; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php deleted file mode 100644 index 7ffa6c4950b3d..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SetShippingAddressesOnCart.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Resolver\ShippingAddress; - -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; -use Magento\Quote\Model\ShippingAddressManagementInterface; -use Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart\MultiShipping; -use Magento\QuoteGraphQl\Model\Resolver\ShippingAddress\SetShippingAddressOnCart\SingleShipping; - -/** - * @inheritdoc - */ -class SetShippingAddressesOnCart implements ResolverInterface -{ - /** - * @var MaskedQuoteIdToQuoteIdInterface - */ - private $maskedQuoteIdToQuoteId; - - /** - * @var MultiShipping - */ - private $multiShipping; - - /** - * @var SingleShipping - */ - private $singleShipping; - - /** - * @var ShippingAddressManagementInterface - */ - private $shippingAddressManagement; - - /** - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId - * @param MultiShipping $multiShipping - * @param SingleShipping $singleShipping - * @param ShippingAddressManagementInterface $shippingAddressManagement - */ - public function __construct( - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - MultiShipping $multiShipping, - SingleShipping $singleShipping, - ShippingAddressManagementInterface $shippingAddressManagement - ) { - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->multiShipping = $multiShipping; - $this->singleShipping = $singleShipping; - $this->shippingAddressManagement = $shippingAddressManagement; - } - - /** - * @inheritdoc - */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) - { - if (!isset($args['input']['cart_id'])) { - throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); - } - if (!isset($args['input']['shipping_addresses'])) { - throw new GraphQlInputException(__('Required parameter "shipping_addresses" is missing')); - } - - $shippingAddressesInput = $args['input']['shipping_addresses']; - $maskedCartId = $args['input']['cart_id']; - $cartId = $this->maskedQuoteIdToQuoteId->execute($maskedCartId); - - if (count($shippingAddressesInput) === 1) { - $this->singleShipping->setAddress($context, $cartId, current($shippingAddressesInput)); - } else { - $this->multiShipping->setAddresses($context, $cartId, $shippingAddressesInput); - } - - //TODO: implement Cart object in the separate resolver - $shippingAddress = $this->shippingAddressManagement->get($cartId); - return [ - 'cart' => [ - 'applied_coupon' => [ - 'code' => '' - ], - 'addresses' => [[ - 'firstname' => $shippingAddress->getFirstname(), - 'lastname' => $shippingAddress->getLastname(), - 'company' => $shippingAddress->getCompany(), - 'street' => $shippingAddress->getStreet(), - 'city' => $shippingAddress->getCity(), - 'region' => [ - 'code' => $shippingAddress->getRegionCode(), - 'label' => $shippingAddress->getRegion() - ], - 'country' => [ - 'code' => $shippingAddress->getCountryId(), - 'label' => '' - ], - 'postcode' => $shippingAddress->getPostcode(), - 'telephone' => $shippingAddress->getTelephone(), - 'address_type' => 'SHIPPING', - 'selected_shipping_method' => [ - 'code' => 'test', - 'label' => 'test', - 'free_shipping' => 'test', - 'error_message' => 'test' - ], - 'available_shipping_methods' => [[ - 'code' => 'test', - 'label' => 'test', - 'free_shipping' => 'test', - 'error_message' => 'test' - ]], - 'items_weight' => [0], - 'customer_notes' => $shippingAddress->getLastname(), - 'cart_items' => [[ - 'cart_item_id' => '', - 'quantity' => 0 - ]], - 'applied_coupon' => [ - 'code' => '' - ] - ]] - ] - ]; - } -} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 7312c15e06580..017b1f8119829 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -9,11 +9,10 @@ type Mutation { createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") - setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddress\\SetShippingAddressesOnCart") + setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart") createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart") - setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart") addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") From 5fa66213fa852793694b4aee831bb93969bff5d0 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Sun, 4 Nov 2018 17:20:03 +0200 Subject: [PATCH 177/310] magento/magento2:#18979 - API: Bundle Product Option Repository Delete method removes incorrect option - Fixed failed unit tests --- .../Magento/Bundle/Model/OptionRepository.php | 11 +-- .../Test/Unit/Model/OptionRepositoryTest.php | 74 +++++++++++++++++-- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Bundle/Model/OptionRepository.php b/app/code/Magento/Bundle/Model/OptionRepository.php index a4d579a44174a..46c44c83b5bb5 100644 --- a/app/code/Magento/Bundle/Model/OptionRepository.php +++ b/app/code/Magento/Bundle/Model/OptionRepository.php @@ -106,17 +106,18 @@ public function get($sku, $optionId) $productLinks = $this->linkManagement->getChildren($product->getSku(), $optionId); - /** @var \Magento\Bundle\Api\Data\OptionInterface $option */ + /** @var \Magento\Bundle\Api\Data\OptionInterface $optionDataObject */ $optionDataObject = $this->optionFactory->create(); $this->dataObjectHelper->populateWithArray( $optionDataObject, $option->getData(), \Magento\Bundle\Api\Data\OptionInterface::class ); - $optionDataObject->setOptionId($option->getId()) - ->setTitle($option->getTitle() === null ? $option->getDefaultTitle() : $option->getTitle()) - ->setSku($product->getSku()) - ->setProductLinks($productLinks); + + $optionDataObject->setOptionId($option->getId()); + $optionDataObject->setTitle($option->getTitle() === null ? $option->getDefaultTitle() : $option->getTitle()); + $optionDataObject->setSku($product->getSku()); + $optionDataObject->setProductLinks($productLinks); return $optionDataObject; } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php index b4a466b413af0..c579d8289d1b6 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php @@ -8,6 +8,7 @@ namespace Magento\Bundle\Test\Unit\Model; use Magento\Bundle\Model\OptionRepository; +use Magento\Framework\Exception\NoSuchEntityException; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -84,7 +85,7 @@ protected function setUp() ->getMock(); $this->optionResourceMock = $this->createPartialMock( \Magento\Bundle\Model\ResourceModel\Option::class, - ['delete', '__wakeup', 'save', 'removeOptionSelections'] + ['get', 'delete', '__wakeup', 'save', 'removeOptionSelections'] ); $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); $this->linkManagementMock = $this->createMock(\Magento\Bundle\Api\ProductLinkManagementInterface::class); @@ -227,32 +228,91 @@ public function testDeleteThrowsExceptionIfCannotDelete() $this->model->delete($optionMock); } + /** + * Test successful delete action for given $optionId + */ public function testDeleteById() { $productSku = 'sku'; $optionId = 100; - $productMock = $this->createMock(\Magento\Catalog\Api\Data\ProductInterface::class); + + $optionMock = $this->createMock(\Magento\Bundle\Model\Option::class); + $optionMock->expects($this->exactly(2)) + ->method('getId') + ->willReturn($optionId); + + $optionMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'title' => 'Option title', + 'option_id' => $optionId + ]); + + $this->optionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($optionMock); + + $productMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + ['getTypeId', 'getTypeInstance', 'getStoreId', 'getPriceType', '__wakeup', 'getSku'] + ); $productMock->expects($this->once()) ->method('getTypeId') ->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE); - $this->productRepositoryMock->expects($this->once()) + $productMock->expects($this->exactly(2))->method('getSku')->willReturn($productSku); + + $this->productRepositoryMock + ->expects($this->once()) ->method('get') ->with($productSku) ->willReturn($productMock); + $optCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optCollectionMock->expects($this->once())->method('getItemById')->with($optionId)->willReturn($optionMock); + $this->typeMock->expects($this->once()) + ->method('getOptionsCollection') + ->with($productMock) + ->willReturn($optCollectionMock); + + $this->assertTrue($this->model->deleteById($productSku, $optionId)); + } + + /** + * Tests if NoSuchEntityException thrown when provided $optionId not found + */ + public function testDeleteByIdException() { + $productSku = 'sku'; + $optionId = null; + $optionMock = $this->createMock(\Magento\Bundle\Model\Option::class); + $optionMock->expects($this->exactly(1)) + ->method('getId') + ->willReturn($optionId); + + $productMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + ['getTypeId', 'getTypeInstance', 'getStoreId', 'getPriceType', '__wakeup', 'getSku'] + ); + $productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE); + + $this->productRepositoryMock + ->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($productMock); $optCollectionMock = $this->createMock(\Magento\Bundle\Model\ResourceModel\Option\Collection::class); + $optCollectionMock->expects($this->once())->method('getItemById')->with($optionId)->willReturn($optionMock); $this->typeMock->expects($this->once()) ->method('getOptionsCollection') ->with($productMock) ->willReturn($optCollectionMock); - $optCollectionMock->expects($this->once())->method('setIdFilter')->with($optionId)->willReturnSelf(); - $optCollectionMock->expects($this->once())->method('getFirstItem')->willReturn($optionMock); + $this->expectException(NoSuchEntityException::class); - $this->optionResourceMock->expects($this->once())->method('delete')->with($optionMock)->willReturnSelf(); - $this->assertTrue($this->model->deleteById($productSku, $optionId)); + $this->model->deleteById($productSku, $optionId); } /** From f06056a040a22d49a580fba331cad11799267272 Mon Sep 17 00:00:00 2001 From: Artem Klimov <art.klimoff@gmail.com> Date: Mon, 5 Nov 2018 00:32:38 +0200 Subject: [PATCH 178/310] Sort schema field for test cases --- .../Framework/GraphQl/Config/GraphQlReaderTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php index 8583dcf3e4cd2..3d3372429123a 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php @@ -191,6 +191,16 @@ enumValues(includeDeprecated: true) { $mergedSchemaResponseFields = array_merge($schemaResponseFieldsFirstHalf, $schemaResponseFieldsSecondHalf); foreach ($expectedOutput as $searchTerm) { + $sortFields = ['inputFields', 'fields']; + foreach ($sortFields as $sortField) { + isset($searchTerm[$sortField]) && is_array($searchTerm[$sortField]) + ? usort($searchTerm[$sortField], function($a, $b) { + $cmpField = 'name'; + return isset($a[$cmpField]) && isset($b[$cmpField]) + ? strcmp($a[$cmpField], $b[$cmpField]) : 0; + }) : null; + } + $this->assertTrue( (in_array($searchTerm, $mergedSchemaResponseFields)), 'Missing type in the response' From 0f575bce67a6dbc4fd9226d7b0ef19eaaebe0fed Mon Sep 17 00:00:00 2001 From: larsroettig <l.roettig@techdivision.com> Date: Mon, 5 Nov 2018 00:19:04 +0100 Subject: [PATCH 179/310] #18956 Fix for phpunit-test case --- .../Magento/Store/Model/Config/Importer/Processor/Create.php | 4 ++-- .../Test/Unit/Model/Config/Importer/Processor/CreateTest.php | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php index 9d1a7a38ede68..71ffa5303293d 100644 --- a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php +++ b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php @@ -177,8 +177,8 @@ private function createGroups(array $items, array $data) ); $group = $this->groupFactory->create(); - if (!isset($groupData['root_categry_id'])) { - $groupData['root_categry_id'] = 0; + if (!isset($groupData['root_category_id'])) { + $groupData['root_category_id'] = 0; } $group->setData($groupData); diff --git a/app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php b/app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php index 2c2b0b00aec43..0901464224399 100644 --- a/app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php @@ -291,9 +291,6 @@ public function testRunGroup() $this->groupMock->expects($this->exactly(3)) ->method('getResource') ->willReturn($this->abstractDbMock); - $this->groupMock->expects($this->once()) - ->method('setRootCategoryId') - ->with(0); $this->groupMock->expects($this->once()) ->method('getDefaultStoreId') ->willReturn($defaultStoreId); From f9b4595a60d7b81775cdd43f996991acb0d50ff2 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Mon, 5 Nov 2018 12:08:27 +0300 Subject: [PATCH 180/310] MAGETWO-91750: Multiselect attribute values is not searchable under Quick Search when more than one value is selected - Fix perfomance --- .../Model/Indexer/Fulltext/Action/DataProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 421746e7761c1..6cd4e227b85c8 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -653,7 +653,8 @@ private function getAttributeOptionValue($attributeId, $valueIds, $storeId) } } foreach ($attributeValueIds as $attrValueId) { - $attributeOptionValue .= $this->attributeOptions[$optionKey][$attrValueId] . ' '; + if (isset($this->attributeOptions[$optionKey][$attrValueId])) + $attributeOptionValue .= $this->attributeOptions[$optionKey][$attrValueId] . ' '; } return empty($attributeOptionValue) ? null : trim($attributeOptionValue); } From da66589239c95d7739b2c01e27628d178ed9c139 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Mon, 5 Nov 2018 12:32:18 +0300 Subject: [PATCH 181/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix functional test --- .../Mftf/Section/AdminProductFormGroupedProductsSection.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml index 0739c4e601b62..f5549f26bfd56 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml @@ -11,8 +11,8 @@ <section name="AdminProductFormGroupedProductsSection"> <element name="toggleGroupedProduct" type="button" selector="div[data-index=grouped] .admin__collapsible-title"/> <element name="addProductsToGroup" type="button" selector="button[data-index='grouped_products_button']" timeout="30"/> - <element name="nextActionButton" type="button" selector="//button[@class='action-next']"/> - <element name="previousActionButton" type="button" selector="//button[@class='action-previous']"/> + <element name="nextActionButton" type="button" selector=".admin__field > .admin__field-control > .admin__control-table-pagination > .admin__data-grid-pager > .action-next"/> + <element name="previousActionButton" type="button" selector=".admin__field > .admin__field-control > .admin__control-table-pagination > .admin__data-grid-pager > .action-previous"/> <element name="positionProduct" type="input" selector="//tbody/tr[{{arg}}][contains(@class,'data-row')]/td[10]//input[@class='position-widget-input']" parameterized="true"/> <element name="nameProductFromGrid" type="text" selector="//tbody/tr[{{arg}}][contains(@class,'data-row')]/td[4]//*[@class='admin__field-control']//span" parameterized="true"/> </section> From 1a703c7054e3efc51cf9eded93602c6d4b93f726 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Mon, 5 Nov 2018 13:11:11 +0300 Subject: [PATCH 182/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../adminhtml/web/js/grouped-product-grid.js | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index f257cb2df729e..c8e5a58c99e78 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -23,6 +23,7 @@ define([ if (position || position === 0) { this.checkMaxPosition(position); this.sort(position, elem); + if (~~position === this.maxPosition && ~~position > this.getDefaultPageBoundary() + 1) { this.shiftNextPagesPositions(position); } @@ -37,17 +38,17 @@ define([ * @param position */ shiftNextPagesPositions: function (position) { - var recordData = this.recordData(); + + var recordData = this.recordData(), + startIndex = ~~this.currentPage() * this.pageSize, + offset = position - startIndex + 1; if (~~this.currentPage() === this.pages()) { return false; - } else { - var startIndex = ~~this.currentPage() * this.pageSize, - offset = position - startIndex + 1; - for (var index = startIndex; index < recordData.length; index++) { - recordData[index].position = index + offset; - } - this.recordData(recordData); } + for (var index = startIndex; index < recordData.length; index++) { + recordData[index].position = index + offset; + } + this.recordData(recordData); }, @@ -58,14 +59,14 @@ define([ * @param event */ updateGridPosition: function (data, event) { - var inputValue = parseInt(event.target.value), + var inputValue = parseInt(event.target.value, 10), recordData = this.recordData(), record, previousValue, updatedRecord; record = this.elems().find(function (obj) { - return obj.dataScope === data.parentScope + return obj.dataScope === data.parentScope; }); previousValue = this.getCalculatedPosition(record); @@ -105,7 +106,7 @@ define([ */ getUpdatedRecordIndex: function (recordData, recordId) { return recordData.map(function (o) { - return ~~o.id + return ~~o.id; }).indexOf(~~recordId); }, @@ -163,7 +164,7 @@ define([ this.elems([]); updatedRecord = this.getUpdatedRecordIndex(recordData, objectToUpdate.data().id); recordData.forEach(function (value, index) { - recordData[index].position = (index === updatedRecord) ? 0 : value.position + 1; + recordData[index].position = index === updatedRecord ? 0 : value.position + 1; }); this.reloadGridData(recordData); } @@ -198,7 +199,7 @@ define([ * @return {number} */ getDefaultPageBoundary: function () { - return (~~this.currentPage() * this.pageSize) - 1; + return ~~this.currentPage() * this.pageSize - 1; }, /** @@ -208,7 +209,7 @@ define([ */ getGlobalMaxPosition: function () { return _.max(this.recordData().map(function (r) { - return ~~r.position + return ~~r.position; })); } }); From 2f995d13c332dd4a2e560373d231c0f06cf25f98 Mon Sep 17 00:00:00 2001 From: Tobias Maile <tobias.maile@brandung.de> Date: Mon, 5 Nov 2018 12:33:25 +0100 Subject: [PATCH 183/310] GraphQL-202: [Mutations] adds description and category_id as parameter --- app/code/Magento/SendFriendGraphQl/etc/schema.graphqls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls b/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls index 87bf90fd10072..4123387650838 100644 --- a/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls @@ -2,7 +2,7 @@ # See COPYING.txt for license details. type Mutation { - sendEmailToFriend (input: SendEmailToFriendSenderInput): SendEmailToFriendOutput @resolver(class: "\\Magento\\SendFriendGraphQl\\Model\\Resolver\\SendEmailToFriend") @doc(description:"@todo") + sendEmailToFriend (input: SendEmailToFriendSenderInput): SendEmailToFriendOutput @resolver(class: "\\Magento\\SendFriendGraphQl\\Model\\Resolver\\SendEmailToFriend") @doc(description:"Recommends Product by Sending Single/Multiple Email") } input SendEmailToFriendSenderInput { @@ -16,9 +16,9 @@ type Sender { email: String! message: String! } -#@todo Prams can be removed if dispatching of event in app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php not needed type Params { product_id: Int! + category_id: Int! } type Recipient { From 1fb624d381e67581a06e66809883e51a4a4b0d75 Mon Sep 17 00:00:00 2001 From: Tobias Maile <tobias.maile@brandung.de> Date: Mon, 5 Nov 2018 12:34:46 +0100 Subject: [PATCH 184/310] GraphQL-202: [Mutations] moves validation logic to separate module --- .../Model/Validation/Validation.php | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 app/code/Magento/SendFriendGraphQl/Model/Validation/Validation.php diff --git a/app/code/Magento/SendFriendGraphQl/Model/Validation/Validation.php b/app/code/Magento/SendFriendGraphQl/Model/Validation/Validation.php new file mode 100644 index 0000000000000..49f7feeaba489 --- /dev/null +++ b/app/code/Magento/SendFriendGraphQl/Model/Validation/Validation.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriendGraphQl\Model\Validation; + +use Magento\Framework\DataObjectFactory; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\SendFriend\Model\SendFriend; + +class Validation +{ + /** + * @var SendFriend + */ + private $sendFriend; + /** + * @var DataObjectFactory + */ + private $dataObjectFactory; + + public function __construct( + DataObjectFactory $dataObjectFactory, + SendFriend $sendFriend + ) { + $this->sendFriend = $sendFriend; + $this->dataObjectFactory = $dataObjectFactory; + } + + /** + * @param $args + * @param array $recipientsArray + * @throws GraphQlInputException + */ + public function validate($args, array $recipientsArray): void + { + $this->prepareDataForSendFriendValidation($args, $recipientsArray); + $validationResult = $this->sendFriend->validate(); + if ($validationResult !== true) { + throw new GraphQlInputException(__(implode($validationResult))); + } + if ($this->sendFriend->getMaxSendsToFriend() && $this->sendFriend->isExceedLimit()) { + throw new GraphQlInputException(__('You can\'t send messages more than %1 times an hour.', + $this->sendFriend->getMaxSendsToFriend() + )); + } + } + + private function prepareDataForSendFriendValidation(array $args, array $recipientsArray): void + { + $sender = $this->dataObjectFactory->create()->setData([ + 'name' => $args['input']['sender']['name'], + 'email'=> $args['input']['sender']['email'], + 'message' => $args['input']['sender']['message'], + ]); + $emails = []; + foreach ($recipientsArray['recipients'] as $recipient) { + $emails[] = $recipient['email']; + } + $recipients = $this->dataObjectFactory->create()->setData('emails', $emails); + + $this->sendFriend->setData('_sender', $sender); + $this->sendFriend->setData('_recipients', $recipients); + } + +} \ No newline at end of file From 2a021c075f52e39b03dc8c30ba418afc57851041 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Mon, 5 Nov 2018 17:12:32 +0300 Subject: [PATCH 185/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../view/adminhtml/web/js/grouped-product-grid.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index c8e5a58c99e78..3fb7e9ecc8b4b 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -35,17 +35,19 @@ define([ /** * Shift positions for next page elements * - * @param position + * @param {Number} position */ shiftNextPagesPositions: function (position) { var recordData = this.recordData(), startIndex = ~~this.currentPage() * this.pageSize, - offset = position - startIndex + 1; + offset = position - startIndex + 1, + index = startIndex; + if (~~this.currentPage() === this.pages()) { return false; } - for (var index = startIndex; index < recordData.length; index++) { + for (index; index < recordData.length; index++) { recordData[index].position = index + offset; } this.recordData(recordData); From e18bcb5f846df4ae8fd84f2b66890984982aee04 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Mon, 5 Nov 2018 17:15:28 +0300 Subject: [PATCH 186/310] MAGETWO-91750: Multiselect attribute values is not searchable under Quick Search when more than one value is selected - Fix static testd --- .../Model/Indexer/Fulltext/Action/DataProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 6cd4e227b85c8..39cb95747c2cf 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -653,8 +653,9 @@ private function getAttributeOptionValue($attributeId, $valueIds, $storeId) } } foreach ($attributeValueIds as $attrValueId) { - if (isset($this->attributeOptions[$optionKey][$attrValueId])) + if (isset($this->attributeOptions[$optionKey][$attrValueId])) { $attributeOptionValue .= $this->attributeOptions[$optionKey][$attrValueId] . ' '; + } } return empty($attributeOptionValue) ? null : trim($attributeOptionValue); } From a40eb487b7d3f57d6241620ff2f49d9b4196f383 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi <mankivsk@adobe.com> Date: Mon, 5 Nov 2018 10:42:57 -0600 Subject: [PATCH 187/310] ENGCOM-3355: [Forwardport] Fix Authenticating a customer via REST API does not update the last logged in data #18973 - Fixed docblocks --- app/code/Magento/Integration/Model/CustomerTokenService.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Integration/Model/CustomerTokenService.php b/app/code/Magento/Integration/Model/CustomerTokenService.php index 35d0424d3b4fa..7c2c444a734eb 100644 --- a/app/code/Magento/Integration/Model/CustomerTokenService.php +++ b/app/code/Magento/Integration/Model/CustomerTokenService.php @@ -16,6 +16,9 @@ use Magento\Framework\Exception\AuthenticationException; use Magento\Framework\Event\ManagerInterface; +/** + * @inheritdoc + */ class CustomerTokenService implements \Magento\Integration\Api\CustomerTokenServiceInterface { /** @@ -79,7 +82,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function createCustomerAccessToken($username, $password) { From 90831f56053bc2af291468e01bead189441eb25c Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Mon, 5 Nov 2018 19:16:24 +0200 Subject: [PATCH 188/310] graphQl: removed not needed exception, added test coverage --- .../Model/Cart/SetShippingAddressOnCart.php | 9 +- .../Quote/SetShippingAddressOnCartTest.php | 424 ++++++++++++++++++ 2 files changed, 425 insertions(+), 8 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php index 43e58bb807672..a2d93ac1753c5 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php @@ -84,19 +84,12 @@ public function setAddresses(ContextInterface $context, int $cartId, array $ship if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { throw new GraphQlAuthorizationException( __( - 'Address management allowed only for authorized customers' + 'Address management allowed only for authorized customers.' ) ); } /** @var AddressInterface $customerAddress */ $customerAddress = $this->addressRepository->getById($customerAddressId); - if ($context->getUserId() !== (int)$customerAddress->getCustomerId()) { - throw new GraphQlInputException( - __( - 'Address is not applicable for current customer' - ) - ); - } $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress); } else { $shippingAddress = $this->addressModel->addData($addressInput); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php new file mode 100644 index 0000000000000..9771dbbf84c48 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -0,0 +1,424 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\TestFramework\ObjectManager; + +/** + * Test for set shipping addresses on cart mutation + */ +class SetShippingAddressOnCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->create(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testSetNewGuestShippingAddressOnCart() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + addresses { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testSetSavedShippingAddressOnCartByGuest() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + customer_address_id: 1 + } + ] + } + ) { + cart { + addresses { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + self::expectExceptionMessage('Address management allowed only for authorized customers.'); + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testSetSavedAndNewShippingAddressOnCartAtTheSameTime() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + customer_address_id: 1, + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + addresses { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + self::expectExceptionMessage( + 'Shipping address can\'t contain "customer_address_id" and "address" input at the same time.' + ); + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testSetShippingAddressOnCartWithNoAddresses() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + {} + ] + } + ) { + cart { + addresses { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + self::expectExceptionMessage( + 'Shipping address should contain either "customer_address_id" or "address" input.' + ); + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testSetNewRegisteredCustomerShippingAddressOnCart() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $this->quote->setCustomerId(1); + $this->quoteResource->save($this->quote); + + $headerMap = $this->getHeaderMap(); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + addresses { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query, [], '', $headerMap); + + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + */ + public function testSetSavedRegisteredCustomerShippingAddressOnCart() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $this->quote->setCustomerId(1); + $this->quoteResource->save($this->quote); + + $headerMap = $this->getHeaderMap(); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + customer_address_id: 1 + } + ] + } + ) { + cart { + addresses { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query, [], '', $headerMap); + + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['addresses']); + $this->assertSavedShippingAddressFields($shippingAddressResponse); + } + + /** + * Verify the all the whitelisted fields for a New Address Object + * + * @param array $shippingAddressResponse + */ + private function assertNewShippingAddressFields(array $shippingAddressResponse): void + { + $assertionMap = [ + ['response_field' => 'firstname', 'expected_value' => 'test firstname'], + ['response_field' => 'lastname', 'expected_value' => 'test lastname'], + ['response_field' => 'company', 'expected_value' => 'test company'], + ['response_field' => 'street', 'expected_value' => [0 => 'test street 1', 1 => 'test street 2']], + ['response_field' => 'city', 'expected_value' => 'test city'], + ['response_field' => 'postcode', 'expected_value' => '887766'], + ['response_field' => 'telephone', 'expected_value' => '88776655'] + ]; + + $this->assertResponseFields($shippingAddressResponse, $assertionMap); + } + + /** + * Verify the all the whitelisted fields for a Address Object + * + * @param array $shippingAddressResponse + */ + private function assertSavedShippingAddressFields(array $shippingAddressResponse): void + { + $assertionMap = [ + ['response_field' => 'firstname', 'expected_value' => 'John'], + ['response_field' => 'lastname', 'expected_value' => 'Smith'], + ['response_field' => 'company', 'expected_value' => 'CompanyName'], + ['response_field' => 'street', 'expected_value' => [0 => 'Green str, 67']], + ['response_field' => 'city', 'expected_value' => 'CityM'], + ['response_field' => 'postcode', 'expected_value' => '75477'], + ['response_field' => 'telephone', 'expected_value' => '3468676'] + ]; + + $this->assertResponseFields($shippingAddressResponse, $assertionMap); + } + + /** + * @param string $username + * @return array + */ + private function getHeaderMap(string $username = 'customer@example.com'): array + { + $password = 'password'; + /** @var CustomerTokenServiceInterface $customerTokenService */ + $customerTokenService = ObjectManager::getInstance() + ->get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } +} From 5d2b1c8d80f23a8061c63702d370728d05234cd5 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Mon, 5 Nov 2018 19:17:53 +0200 Subject: [PATCH 189/310] graphQl: fixed dependency issue --- app/code/Magento/QuoteGraphQl/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index c9900dd5f3150..485c958718bf3 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -7,7 +7,8 @@ "magento/framework": "*", "magento/module-quote": "*", "magento/module-catalog": "*", - "magento/module-store": "*" + "magento/module-store": "*", + "magento/module-checkout": "*" }, "suggest": { "magento/module-graph-ql": "*" From 743d3ebd5584b61b3c3d1ff804767871caff470c Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Mon, 5 Nov 2018 11:46:52 -0600 Subject: [PATCH 190/310] MAGETWO-95248: Show Packages button in Shipping and Tracking information section of Shipments doesn't work --- .../view/adminhtml/web/js/packages.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/code/Magento/Shipping/view/adminhtml/web/js/packages.js diff --git a/app/code/Magento/Shipping/view/adminhtml/web/js/packages.js b/app/code/Magento/Shipping/view/adminhtml/web/js/packages.js new file mode 100644 index 0000000000000..f46ad4192d170 --- /dev/null +++ b/app/code/Magento/Shipping/view/adminhtml/web/js/packages.js @@ -0,0 +1,39 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_Ui/js/modal/modal', + 'mage/translate' +], function ($, modal, $t) { + 'use strict'; + + return function (config, element) { + config.buttons = [ + { + text: $t('Print'), + 'class': 'action action-primary', + + /** + * Click handler + */ + click: function () { + window.location.href = this.options.url; + } + }, { + text: $t('Cancel'), + 'class': 'action action-secondary', + + /** + * Click handler + */ + click: function () { + this.closeModal(); + } + } + ]; + modal(config, element); + }; +}); From 3343ad61aa8ac0d596f5b6783b25852d8589f966 Mon Sep 17 00:00:00 2001 From: Dmytro Drozd <ddrozd@magento.com> Date: Mon, 5 Nov 2018 20:41:11 +0200 Subject: [PATCH 191/310] MAGETWO-96026: Create MFTF test for MAGETWO-93973. Add more stability --- ...PricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml index 6eb78809b187d..a8b2df1fcfa67 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml @@ -63,7 +63,9 @@ <click selector="{{AdminProductFormAdvancedInventorySection.enableQtyIncrementsUseConfigSettings}}" stepKey="clickOnEnableQtyIncrementsUseConfigSettingsCheckbox"/> <click selector="{{AdminProductFormAdvancedInventorySection.enableQtyIncrements}}" stepKey="clickOnEnableQtyIncrements"/> <click selector="{{AdminProductFormAdvancedInventorySection.enableQtyIncrementsOptions(('1'))}}" stepKey="chooseYesOnEnableQtyIncrements"/> + <scrollTo selector="{{AdminProductFormAdvancedInventorySection.qtyIncrementsUseConfigSettings}}" stepKey="scrollToQtyIncrementsUseConfigSettings"/> <click selector="{{AdminProductFormAdvancedInventorySection.qtyIncrementsUseConfigSettings}}" stepKey="clickOnQtyIncrementsUseConfigSettings"/> + <scrollTo selector="{{AdminProductFormAdvancedInventorySection.qtyIncrements}}" stepKey="scrollToQtyIncrements"/> <fillField selector="{{AdminProductFormAdvancedInventorySection.qtyIncrements}}" userInput=".5" stepKey="fillQtyIncrements"/> <!--Step6. Close *Advanced Inventory* (Click on button *Done*). Save *prod1* (Click on button *Save*) --> <click selector="{{AdminProductFormAdvancedInventorySection.doneButton}}" stepKey="clickOnDoneButton3"/> From e1f09fb984df705e35dde07092d04957c7c0e5c1 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Mon, 5 Nov 2018 21:15:25 +0200 Subject: [PATCH 192/310] graphQl: added extension point for multishipping --- .../Model/Cart/SetShippingAddressesOnCart.php | 48 +++++++++++++++++++ .../SingleShippingAddressProcessor.php} | 20 +++----- .../SetShippingAddressesOnCartInterface.php | 31 ++++++++++++ .../Resolver/SetShippingAddressesOnCart.php | 22 ++++----- .../Magento/QuoteGraphQl/etc/graphql/di.xml | 9 ++++ 5 files changed, 106 insertions(+), 24 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php rename app/code/Magento/QuoteGraphQl/Model/Cart/{SetShippingAddressOnCart.php => SetShippingAddressesOnCart/SingleShippingAddressProcessor.php} (85%) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php new file mode 100644 index 0000000000000..ad3615b94f8ef --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart; + +use InvalidArgumentException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; + +/** + * Set shipping addresses for shopping cart processors chain + */ +class SetShippingAddressesOnCart implements SetShippingAddressesOnCartInterface +{ + /** + * @var SetShippingAddressesOnCartInterface[] + */ + private $shippingAddressProcessors; + + /** + * @param array $shippingAddressProcessors + */ + public function __construct( + array $shippingAddressProcessors + ) { + $this->shippingAddressProcessors = $shippingAddressProcessors; + } + + /** + * @inheritdoc + */ + public function execute(ContextInterface $context, int $cartId, array $shippingAddresses): void + { + foreach ($this->shippingAddressProcessors as $shippingAddressProcessor) { + if (!$shippingAddressProcessor instanceof SetShippingAddressesOnCartInterface) { + throw new InvalidArgumentException( + get_class($shippingAddressProcessor) . + ' is not instance of \Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface.' + ); + } + + $shippingAddressProcessor->execute($context, $cartId, $shippingAddresses); + } + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php similarity index 85% rename from app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php index a2d93ac1753c5..6584b9887bed5 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Cart; +namespace Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCart; use Magento\Authorization\Model\UserContextInterface; use Magento\Customer\Api\Data\AddressInterface; @@ -15,13 +15,12 @@ use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\ShippingAddressManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface; /** - * Class SetShippingAddressOnCart - * - * Set shipping address for a specified shopping cart + * Set single shipping address for a specified shopping cart */ -class SetShippingAddressOnCart +class SingleShippingAddressProcessor implements SetShippingAddressesOnCartInterface { /** * @var ShippingAddressManagementInterface @@ -54,17 +53,12 @@ public function __construct( } /** - * @param ContextInterface $context - * @param int $cartId - * @param array $shippingAddresses - * @return void + * @inheritdoc */ - public function setAddresses(ContextInterface $context, int $cartId, array $shippingAddresses): void + public function execute(ContextInterface $context, int $cartId, array $shippingAddresses): void { if (count($shippingAddresses) > 1) { - throw new GraphQlInputException( - __('Multiple address does not allowed here!') - ); + return; } $shippingAddress = current($shippingAddresses); $customerAddressId = $shippingAddress['customer_address_id'] ?? null; diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php new file mode 100644 index 0000000000000..f9623e36c5395 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart; + +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; + +/** + * Extension point for setting shipping addresses for a specified shopping cart + * + * All objects that are responsible for set shipping addresses on cart via GraphQl should implement this interface. + */ +interface SetShippingAddressesOnCartInterface +{ + + /** + * Set shipping addresses for a specified shopping cart + * + * @param ContextInterface $context + * @param int $cartId + * @param array $shippingAddresses + * @return void + * @throws GraphQlInputException + */ + public function execute(ContextInterface $context, int $cartId, array $shippingAddresses): void; +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php index fcb4255a75eb7..ec151214dbc42 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php @@ -15,7 +15,7 @@ use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; use Magento\Quote\Model\ShippingAddressManagementInterface; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; -use Magento\QuoteGraphQl\Model\Cart\SetShippingAddressOnCart; +use Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface; /** * Class SetShippingAddressesOnCart @@ -29,11 +29,6 @@ class SetShippingAddressesOnCart implements ResolverInterface */ private $maskedQuoteIdToQuoteId; - /** - * @var SetShippingAddressOnCart - */ - private $setShippingAddressOnCart; - /** * @var ShippingAddressManagementInterface */ @@ -49,25 +44,30 @@ class SetShippingAddressesOnCart implements ResolverInterface */ private $arrayManager; + /** + * @var SetShippingAddressesOnCartInterface + */ + private $setShippingAddressesOnCart; + /** * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId - * @param SetShippingAddressOnCart $setShippingAddressOnCart * @param ShippingAddressManagementInterface $shippingAddressManagement * @param GetCartForUser $getCartForUser * @param ArrayManager $arrayManager + * @param SetShippingAddressesOnCartInterface $setShippingAddressesOnCart */ public function __construct( MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - SetShippingAddressOnCart $setShippingAddressOnCart, ShippingAddressManagementInterface $shippingAddressManagement, GetCartForUser $getCartForUser, - ArrayManager $arrayManager + ArrayManager $arrayManager, + SetShippingAddressesOnCartInterface $setShippingAddressesOnCart ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->setShippingAddressOnCart = $setShippingAddressOnCart; $this->shippingAddressManagement = $shippingAddressManagement; $this->getCartForUser = $getCartForUser; $this->arrayManager = $arrayManager; + $this->setShippingAddressesOnCart = $setShippingAddressesOnCart; } /** @@ -89,7 +89,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); $cartId = (int)$cart->getEntityId(); - $this->setShippingAddressOnCart->setAddresses($context, $cartId, $shippingAddresses); + $this->setShippingAddressesOnCart->execute($context, $cartId, $shippingAddresses); return [ 'cart' => [ diff --git a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml index 43941c9740048..f28d484fa0f6f 100644 --- a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml @@ -6,6 +6,15 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <preference for="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface" + type="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCart" /> + <type name="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface"> + <arguments> + <argument name="shippingAddressProcessors" xsi:type="array"> + <item name="singleShippingAddress" xsi:type="object">Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCart\SingleShippingAddressProcessor</item> + </argument> + </arguments> + </type> <virtualType name="multishippingPaymentSpecification" type="Magento\Payment\Model\Method\Specification\Composite"> <arguments> <argument name="specifications" xsi:type="array"> From 55b6f4e46809d162e2258e4da760228ec4062a93 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Mon, 5 Nov 2018 22:12:55 +0200 Subject: [PATCH 193/310] graphQl: fixed SetShippingAddressesInterface param --- .../QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php | 5 +++-- .../SingleShippingAddressProcessor.php | 5 +++-- .../Model/Cart/SetShippingAddressesOnCartInterface.php | 5 +++-- .../Model/Resolver/SetShippingAddressesOnCart.php | 3 +-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index ad3615b94f8ef..8ebcdef1f549a 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -9,6 +9,7 @@ use InvalidArgumentException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Quote\Api\Data\CartInterface; /** * Set shipping addresses for shopping cart processors chain @@ -32,7 +33,7 @@ public function __construct( /** * @inheritdoc */ - public function execute(ContextInterface $context, int $cartId, array $shippingAddresses): void + public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void { foreach ($this->shippingAddressProcessors as $shippingAddressProcessor) { if (!$shippingAddressProcessor instanceof SetShippingAddressesOnCartInterface) { @@ -42,7 +43,7 @@ public function execute(ContextInterface $context, int $cartId, array $shippingA ); } - $shippingAddressProcessor->execute($context, $cartId, $shippingAddresses); + $shippingAddressProcessor->execute($context, $cart, $shippingAddresses); } } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php index 6584b9887bed5..41675716c68bf 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php @@ -12,6 +12,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\ShippingAddressManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; @@ -55,7 +56,7 @@ public function __construct( /** * @inheritdoc */ - public function execute(ContextInterface $context, int $cartId, array $shippingAddresses): void + public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void { if (count($shippingAddresses) > 1) { return; @@ -89,6 +90,6 @@ public function execute(ContextInterface $context, int $cartId, array $shippingA $shippingAddress = $this->addressModel->addData($addressInput); } - $this->shippingAddressManagement->assign($cartId, $shippingAddress); + $this->shippingAddressManagement->assign($cart->getId(), $shippingAddress); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php index f9623e36c5395..ae337240c3a26 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php @@ -9,6 +9,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Quote\Api\Data\CartInterface; /** * Extension point for setting shipping addresses for a specified shopping cart @@ -22,10 +23,10 @@ interface SetShippingAddressesOnCartInterface * Set shipping addresses for a specified shopping cart * * @param ContextInterface $context - * @param int $cartId + * @param CartInterface $cart * @param array $shippingAddresses * @return void * @throws GraphQlInputException */ - public function execute(ContextInterface $context, int $cartId, array $shippingAddresses): void; + public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php index ec151214dbc42..587ebb2b6db1f 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php @@ -87,9 +87,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $maskedCartId = $args['input']['cart_id']; $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); - $cartId = (int)$cart->getEntityId(); - $this->setShippingAddressesOnCart->execute($context, $cartId, $shippingAddresses); + $this->setShippingAddressesOnCart->execute($context, $cart, $shippingAddresses); return [ 'cart' => [ From e496e26af5dfbe6d109a48c7f0071cd257bcf7fc Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Mon, 5 Nov 2018 14:32:34 -0600 Subject: [PATCH 194/310] MAGETWO-95589: [FT][Temando] Tests fail with enabled Temando extension --- .../Mftf/ActionGroup/StorefrontProductCartActionGroup.xml | 5 ++++- .../Test/Mftf/Section/CheckoutCartSummarySection.xml | 1 + .../Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml | 3 ++- .../Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml | 3 ++- .../Test/Mftf/Test/StorefrontCartPriceRuleState.xml | 3 ++- .../CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml | 2 ++ .../Test/Block/Adminhtml/Order/Create/Shipping/Method.php | 1 - 7 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index 4b5b250078ad4..8ef9ff9e8df38 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -84,7 +84,10 @@ <argument name="total"/> </arguments> <seeInCurrentUrl url="{{CheckoutCartPage.url}}" stepKey="assertUrl"/> - <waitForText userInput="${{total}}" selector="{{CheckoutCartSummarySection.total}}" time="30" stepKey="waitForTotal"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <conditionalClick selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.shippingMethodForm}}" visible="false" stepKey="openEstimateShippingSection"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="waitForShippingSection"/> + <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectShippingMethod"/> <see userInput="${{subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> <see userInput="${{shipping}}" selector="{{CheckoutCartSummarySection.shipping}}" stepKey="assertShipping"/> <see userInput="({{shippingMethod}})" selector="{{CheckoutCartSummarySection.shippingMethod}}" stepKey="assertShippingMethod"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index a1a8d2ba3eade..c2e0868848188 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutCartSummarySection"> <element name="subtotal" type="text" selector="//*[@id='cart-totals']//tr[@class='totals sub']//td//span[@class='price']"/> + <element name="shippingMethodForm" type="text" selector="#co-shipping-method-form"/> <element name="shippingMethod" type="text" selector="//*[@id='cart-totals']//tr[@class='totals shipping excl']//th//span[@class='value']"/> <element name="shipping" type="text" selector="//*[@id='cart-totals']//tr[@class='totals shipping excl']//td//span[@class='price']"/> <element name="total" type="text" selector="//*[@id='cart-totals']//tr[@class='grand totals']//td//span[@class='price']"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml index 3eec020e26347..045fdbb33763f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml @@ -72,11 +72,12 @@ <!-- Should not see the discount yet because we have not set country --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> + <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="openEstimateShippingSection"/> + <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> <!-- See discount if we use valid country --> - <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="expandShipping"/> <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="Brazil" stepKey="fillCountry"/> <waitForPageLoad stepKey="waitForCountry1"/> <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml index 73b5d2546f439..d8c3ef9c32b0b 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml @@ -76,11 +76,12 @@ <!-- Should not see the discount yet because we have not filled in postcode --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> + <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="openEstimateShippingSection"/> + <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> <!-- See discount if we use valid postcode --> - <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="expandShipping"/> <fillField selector="{{CheckoutCartSummarySection.postcode}}" userInput="78613" stepKey="fillPostcode"/> <waitForPageLoad stepKey="waitForPostcode1"/> <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml index 9395e87c20edb..647f4d6e5c800 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml @@ -72,11 +72,12 @@ <!-- Should not see the discount yet because we have not filled in postcode --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> + <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="expandShipping"/> + <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectFlatRateShipping"/> <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$123.00" stepKey="seeSubtotal"/> <dontSeeElement selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="dontSeeDiscount"/> <!-- See discount if we use valid postcode --> - <click selector="{{CheckoutCartSummarySection.shippingHeading}}" stepKey="expandShipping"/> <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="Indiana" stepKey="fillState"/> <waitForPageLoad stepKey="waitForPostcode1"/> <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml index 1fad2f282fa5d..719e9bb95694e 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/ApplyCatalogPriceRulesTest.xml @@ -106,6 +106,8 @@ <data name="productPrice/0/special" xsi:type="string">5</data> <data name="productPrice/0/sub_total" xsi:type="string">5</data> <data name="productPrice/0/regular" xsi:type="string">10</data> + <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleNotAppliedCatalogPage" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleNotAppliedProductPage" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleNotAppliedShoppingCart" /> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Method.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Method.php index 2ee6269c39d47..a0520e1c7ef8f 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Method.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Method.php @@ -67,7 +67,6 @@ function () { */ private function waitFormLoading() { - $this->_rootElement->click(); $this->browser->waitUntil( function () { return $this->browser->find($this->waitElement)->isVisible() ? null : true; From 8df9f0208f93993a065ace2ee5e32612134898bb Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 6 Nov 2018 09:30:41 +0200 Subject: [PATCH 195/310] MAGETWO-96007: Customer address issue when creating or updating via API --- .../ResourceModel/CustomerRepositoryTest.php | 63 ++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) 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 15afc87405ffd..75dfcf7e8f2fd 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php @@ -294,8 +294,6 @@ public function testUpdateCustomerPreserveAllAddresses() $customerId = 1; $customer = $this->customerRepository->getById($customerId); $customerDetails = $customer->__toArray(); - $defaultBilling = $customerDetails['default_billing']; - $defaultShipping = $customerDetails['default_shipping']; $newCustomerEntity = $this->customerFactory->create(); $this->dataObjectHelper->populateWithArray( $newCustomerEntity, @@ -307,19 +305,8 @@ public function testUpdateCustomerPreserveAllAddresses() $this->customerRepository->save($newCustomerEntity); $newCustomerDetails = $this->customerRepository->getById($customerId); - $newCustomerArray = $newCustomerDetails->__toArray(); //Verify that old addresses are still present $this->assertEquals(2, count($newCustomerDetails->getAddresses())); - $this->assertEquals( - $defaultBilling, - $newCustomerArray['default_billing'], - "Default billing invalid value" - ); - $this->assertEquals( - $defaultShipping, - $newCustomerArray['default_shipping'], - "Default shipping invalid value" - ); } /** @@ -345,19 +332,51 @@ public function testUpdateCustomerDeleteAllAddressesWithEmptyArray() $this->customerRepository->save($newCustomerEntity); $newCustomerDetails = $this->customerRepository->getById($customerId); - $newCustomerArray = $newCustomerDetails->__toArray(); //Verify that old addresses are removed $this->assertEquals(0, count($newCustomerDetails->getAddresses())); - $this->assertEquals( - $newCustomerArray['default_billing'], - null, - "Default billing invalid value" + } + + /** + * Test customer update with new address + * + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + */ + public function testUpdateCustomerWithNewAddress() + { + $customerId = 1; + $customer = $this->customerRepository->getById($customerId); + $customerDetails = $customer->__toArray(); + unset($customerDetails['default_billing']); + unset($customerDetails['default_shipping']); + + $beforeSaveCustomer = $this->customerFactory->create(); + $this->dataObjectHelper->populateWithArray( + $beforeSaveCustomer, + $customerDetails, + CustomerInterface::class ); - $this->assertEquals( - $newCustomerArray['default_shipping'], - null, - "Default shipping invalid value" + + $addresses = $customer->getAddresses(); + $beforeSaveAddress = $addresses[0]->__toArray(); + unset($beforeSaveAddress['id']); + $newAddressDataObject = $this->addressFactory->create(); + $this->dataObjectHelper->populateWithArray( + $newAddressDataObject, + $beforeSaveAddress, + AddressInterface::class ); + + $beforeSaveCustomer->setAddresses([$newAddressDataObject]); + $this->customerRepository->save($beforeSaveCustomer); + + $newCustomer = $this->customerRepository->getById($customerId); + $newCustomerAddresses = $newCustomer->getAddresses(); + $addressId = $newCustomerAddresses[0]->getId(); + + $this->assertEquals($newCustomer->getDefaultBilling(), $addressId, "Default billing invalid value"); + $this->assertEquals($newCustomer->getDefaultShipping(), $addressId, "Default shipping invalid value"); } /** From 46e06440229c51f35ff96123b575be4b21fa9f1e Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Tue, 6 Nov 2018 13:40:36 +0300 Subject: [PATCH 196/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 3fb7e9ecc8b4b..88758d63d0148 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -47,6 +47,7 @@ define([ if (~~this.currentPage() === this.pages()) { return false; } + for (index; index < recordData.length; index++) { recordData[index].position = index + offset; } From bfd8feb724a44beeee3945f06fc5946a1529b86d Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Tue, 6 Nov 2018 15:24:28 +0300 Subject: [PATCH 197/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 88758d63d0148..fbd134cdf29d8 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -54,7 +54,6 @@ define([ this.recordData(recordData); }, - /** * Update position for element after position from another page is entered * From c0c1d76192b57fe524d0b9d2672282221ea8b011 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 6 Nov 2018 14:07:03 +0100 Subject: [PATCH 198/310] Added checkout module dependency --- app/code/Magento/QuoteGraphQl/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index c9900dd5f3150..f0d2eea9bcaf6 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -6,6 +6,7 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-quote": "*", + "magento/module-checkout": "*", "magento/module-catalog": "*", "magento/module-store": "*" }, From c3b4a8078771dd6ba76b1d00d9709a37b81c7677 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 6 Nov 2018 15:00:00 +0100 Subject: [PATCH 199/310] Covered most cases except authorisation --- .../Quote/SetShippingMethodOnCartTest.php | 160 +++++++++++++++--- 1 file changed, 132 insertions(+), 28 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php index 64c7cbf9fd42e..932c8ac386c27 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php @@ -39,6 +39,9 @@ class SetShippingMethodOnCartTest extends GraphQlAbstract */ private $quoteIdToMaskedId; + /** + * @inheritdoc + */ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); @@ -51,26 +54,139 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php */ - public function testSetShippingOnCart() + public function testSetShippingMethodOnCart() { + $shippingCarrierCode = 'flatrate'; + $shippingMethodCode = 'flatrate'; $this->quoteResource->load( $this->quote, 'test_order_1', 'reserved_order_id' ); + $shippingAddress = $this->quote->getShippingAddress(); + $shippingAddressId = $shippingAddress->getId(); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + $response = $this->sendRequestWithToken($query); + + self::assertArrayHasKey('setShippingMethodsOnCart', $response); + self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); + self::assertEquals($maskedQuoteId, $response['setShippingMethodsOnCart']['cart']['cart_id']); + $addressesInformation = $response['setShippingMethodsOnCart']['cart']['addresses']; + self::assertCount(2, $addressesInformation); + self::assertEquals( + $addressesInformation[0]['selected_shipping_method']['code'], + $shippingCarrierCode . '_' . $shippingMethodCode + ); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetShippingMethodWithWrongCartId() + { + $shippingCarrierCode = 'flatrate'; + $shippingMethodCode = 'flatrate'; + $shippingAddressId = '1'; + $maskedQuoteId = 'invalid'; + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + self::expectExceptionMessage("Could not find a cart with ID \"$maskedQuoteId\""); + $this->sendRequestWithToken($query); + } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetNonExistingShippingMethod() + { + $shippingCarrierCode = 'non'; + $shippingMethodCode = 'existing'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); $shippingAddress = $this->quote->getShippingAddress(); $shippingAddressId = $shippingAddress->getId(); $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = <<<QUERY + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + self::expectExceptionMessage("Carrier with such method not found: $shippingCarrierCode, $shippingMethodCode"); + $this->sendRequestWithToken($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetShippingMethodWithNonExistingAddress() + { + $shippingCarrierCode = 'flatrate'; + $shippingMethodCode = 'flatrate'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $shippingAddressId = '-20'; + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + self::expectExceptionMessage('The shipping address is missing. Set the address and try again.'); + $this->sendRequestWithToken($query); + } + + // TODO: TBD - add check for guest with attempt to set shipping method to the customer's shopping cart + + /** + * Generates query for setting the specified shipping method on cart + * + * @param string $maskedQuoteId + * @param string $shippingMethodCode + * @param string $shippingCarrierCode + * @param string $shippingAddressId + * @return string + */ + private function prepareMutationQuery( + string $maskedQuoteId, + string $shippingMethodCode, + string $shippingCarrierCode, + string $shippingAddressId + ) : string { + return <<<QUERY mutation { setShippingMethodsOnCart(input: { cart_id: "$maskedQuoteId", shipping_methods: [ { - shipping_method_code: "flatrate" - shipping_carrier_code: "flatrate" + shipping_method_code: "$shippingMethodCode" + shipping_carrier_code: "$shippingCarrierCode" cart_address_id: $shippingAddressId } ]}) { @@ -78,22 +194,6 @@ public function testSetShippingOnCart() cart { cart_id, addresses { - firstname - lastname - company - address_type - city - street - region { - code - label - } - postcode - country { - code - label - } - selected_shipping_method { code label @@ -104,17 +204,21 @@ public function testSetShippingOnCart() } QUERY; + } + + /** + * Sends a GraphQL request with using a bearer token + * + * @param string $query + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function sendRequestWithToken(string $query): array + { $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - $response = $this->graphQlQuery($query, [], '', $headerMap); - - self::assertArrayHasKey('setShippingMethodsOnCart', $response); - self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); - self::assertEquals($maskedQuoteId, $response['setShippingMethodsOnCart']['cart']['cart_id']); - // TODO: check shipping method code and description + return $this->graphQlQuery($query, [], '', $headerMap); } - - // TODO: cover all other cases } From 5934b231fe89fde967936124f714b929f2a1aada Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Tue, 6 Nov 2018 17:38:53 +0300 Subject: [PATCH 200/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../adminhtml/web/js/grouped-product-grid.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index fbd134cdf29d8..9cb0a00dc9521 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -57,8 +57,8 @@ define([ /** * Update position for element after position from another page is entered * - * @param data - * @param event + * @param {Object} data + * @param {Object} event */ updateGridPosition: function (data, event) { var inputValue = parseInt(event.target.value, 10), @@ -102,8 +102,8 @@ define([ /** * Get updated record index * - * @param recordData - * @param recordId + * @param {Array} recordData + * @param {Number} recordId * @return {number} */ getUpdatedRecordIndex: function (recordData, recordId) { @@ -114,7 +114,7 @@ define([ /** * - * @param recordData to reprocess + * @param {Array} recordData to reprocess */ reloadGridData: function (recordData) { this.recordData(recordData.sort(function (a, b) { @@ -127,7 +127,7 @@ define([ /** * Event handler for "Send to bottom" button * - * @param positionObj + * @param {Object} positionObj * @return {boolean} */ sendToBottom: function (positionObj) { @@ -151,7 +151,7 @@ define([ /** * Event handler for "Send to top" button * - * @param positionObj + * @param {Object} positionObj * @returns {boolean} */ sendToTop: function (positionObj) { @@ -176,7 +176,7 @@ define([ /** * Get element from grid for update * - * @param object + * @param {Object} object * @return {*} */ getObjectToUpdate: function (object) { @@ -188,7 +188,7 @@ define([ /** * Value function for position input * - * @param data + * @param {Object} data * @return {number} */ getCalculatedPosition: function (data) { From bf5ad21fc451c17b496f5a24280600e5c66c1aba Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Tue, 6 Nov 2018 18:36:03 +0300 Subject: [PATCH 201/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../view/adminhtml/web/js/grouped-product-grid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 9cb0a00dc9521..c06f615ae48e6 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -104,7 +104,7 @@ define([ * * @param {Array} recordData * @param {Number} recordId - * @return {number} + * @return {Number} */ getUpdatedRecordIndex: function (recordData, recordId) { return recordData.map(function (o) { From 418d0cabbe129eb0fcc9cf793213de7217292be8 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Tue, 6 Nov 2018 11:18:38 -0600 Subject: [PATCH 202/310] MAGETWO-95745: Signifyd case not created for WorldPay --- app/code/Magento/Checkout/Controller/Onepage/Success.php | 5 ++++- app/code/Magento/Signifyd/etc/events.xml | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Controller/Onepage/Success.php b/app/code/Magento/Checkout/Controller/Onepage/Success.php index 7db5cd8f012c7..e0872b425cc7c 100644 --- a/app/code/Magento/Checkout/Controller/Onepage/Success.php +++ b/app/code/Magento/Checkout/Controller/Onepage/Success.php @@ -26,7 +26,10 @@ public function execute() $resultPage = $this->resultPageFactory->create(); $this->_eventManager->dispatch( 'checkout_onepage_controller_success_action', - ['order_ids' => [$session->getLastOrderId()]] + [ + 'order_ids' => [$session->getLastOrderId()], + 'order' => $session->getLastRealOrder() + ] ); return $resultPage; } diff --git a/app/code/Magento/Signifyd/etc/events.xml b/app/code/Magento/Signifyd/etc/events.xml index d5ba6e5a99227..e6418e2fdb6b4 100644 --- a/app/code/Magento/Signifyd/etc/events.xml +++ b/app/code/Magento/Signifyd/etc/events.xml @@ -15,4 +15,7 @@ <event name="paypal_checkout_success"> <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> </event> + <event name="checkout_onepage_controller_success_action"> + <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> + </event> </config> From 7c21b3b31e5f330e698d6a766cb45e90cf61a3f3 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Tue, 6 Nov 2018 20:36:45 +0200 Subject: [PATCH 203/310] graphQl: removed set shipping addresses chain, using di preference instead --- ...essor.php => SetShippingAddressOnCart.php} | 9 ++-- .../Model/Cart/SetShippingAddressesOnCart.php | 49 ------------------- app/code/Magento/QuoteGraphQl/composer.json | 4 +- .../Magento/QuoteGraphQl/etc/graphql/di.xml | 9 +--- .../Quote/SetShippingAddressOnCartTest.php | 46 +++++++++++++++++ 5 files changed, 55 insertions(+), 62 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/Cart/{SetShippingAddressesOnCart/SingleShippingAddressProcessor.php => SetShippingAddressOnCart.php} (92%) delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php similarity index 92% rename from app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php index 41675716c68bf..588d4f65cc565 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart/SingleShippingAddressProcessor.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCart; +namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Authorization\Model\UserContextInterface; use Magento\Customer\Api\Data\AddressInterface; @@ -16,12 +16,11 @@ use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\ShippingAddressManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface; /** * Set single shipping address for a specified shopping cart */ -class SingleShippingAddressProcessor implements SetShippingAddressesOnCartInterface +class SetShippingAddressOnCart implements SetShippingAddressesOnCartInterface { /** * @var ShippingAddressManagementInterface @@ -59,7 +58,9 @@ public function __construct( public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void { if (count($shippingAddresses) > 1) { - return; + throw new GraphQlInputException( + __('Multiple addresses do not allowed here!') + ); } $shippingAddress = current($shippingAddresses); $customerAddressId = $shippingAddress['customer_address_id'] ?? null; diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php deleted file mode 100644 index 8ebcdef1f549a..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Cart; - -use InvalidArgumentException; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; -use Magento\Quote\Api\Data\CartInterface; - -/** - * Set shipping addresses for shopping cart processors chain - */ -class SetShippingAddressesOnCart implements SetShippingAddressesOnCartInterface -{ - /** - * @var SetShippingAddressesOnCartInterface[] - */ - private $shippingAddressProcessors; - - /** - * @param array $shippingAddressProcessors - */ - public function __construct( - array $shippingAddressProcessors - ) { - $this->shippingAddressProcessors = $shippingAddressProcessors; - } - - /** - * @inheritdoc - */ - public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void - { - foreach ($this->shippingAddressProcessors as $shippingAddressProcessor) { - if (!$shippingAddressProcessor instanceof SetShippingAddressesOnCartInterface) { - throw new InvalidArgumentException( - get_class($shippingAddressProcessor) . - ' is not instance of \Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface.' - ); - } - - $shippingAddressProcessor->execute($context, $cart, $shippingAddresses); - } - } -} diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index 485c958718bf3..b3433fdc8fea6 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -8,7 +8,9 @@ "magento/module-quote": "*", "magento/module-catalog": "*", "magento/module-store": "*", - "magento/module-checkout": "*" + "magento/module-checkout": "*", + "magento/module-customer": "*", + "magento/module-authorization": "*" }, "suggest": { "magento/module-graph-ql": "*" diff --git a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml index f28d484fa0f6f..e7417d657a0ea 100644 --- a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml @@ -7,14 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface" - type="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCart" /> - <type name="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface"> - <arguments> - <argument name="shippingAddressProcessors" xsi:type="array"> - <item name="singleShippingAddress" xsi:type="object">Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCart\SingleShippingAddressProcessor</item> - </argument> - </arguments> - </type> + type="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressOnCart" /> <virtualType name="multishippingPaymentSpecification" type="Magento\Payment\Model\Method\Specification\Composite"> <arguments> <argument name="specifications" xsi:type="array"> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index 9771dbbf84c48..48ef8ebb790d1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -143,6 +143,52 @@ public function testSetSavedShippingAddressOnCartByGuest() $this->graphQlQuery($query); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoConfigFixture default_store multishipping/options/checkout_multiple 0 + */ + public function testSetMultipleShippingAddressesOnCartByGuest() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + customer_address_id: 1 + }, + { + customer_address_id: 1 + } + ] + } + ) { + cart { + addresses { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + self::expectExceptionMessage('Multiple addresses do not allowed here!'); + $this->graphQlQuery($query); + } + /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ From c8578575c498690cd672764c374ddafec18dcc90 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Tue, 6 Nov 2018 20:38:08 +0200 Subject: [PATCH 204/310] graphQl: fixed composer json --- app/code/Magento/QuoteGraphQl/composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index 05575ef371600..9d29cda3b872b 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -9,7 +9,6 @@ "magento/module-checkout": "*", "magento/module-catalog": "*", "magento/module-store": "*", - "magento/module-checkout": "*", "magento/module-customer": "*", "magento/module-authorization": "*" }, From cf824bcda59d97d2890b64a56a72cbb2f71e89ce Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Wed, 7 Nov 2018 12:33:59 +0400 Subject: [PATCH 205/310] MAGETWO-91658: Wrong Checkout Totals Sort Order in cart - Moved test from CE to EE. --- .../Test/Mftf/Data/CheckoutConfigData.xml | 25 ------ .../Mftf/Metadata/checkout_config-meta.xml | 86 ------------------- .../CheckoutTotalsSortOrderInCartTest.xml | 52 ----------- 3 files changed, 163 deletions(-) delete mode 100644 app/code/Magento/Checkout/Test/Mftf/Data/CheckoutConfigData.xml delete mode 100644 app/code/Magento/Checkout/Test/Mftf/Metadata/checkout_config-meta.xml delete mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/CheckoutConfigData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/CheckoutConfigData.xml deleted file mode 100644 index bf2ae28009011..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Data/CheckoutConfigData.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="CheckoutShippingTotalsSortOrder" type="checkout_totals_sort_order"> - <requiredEntity type="shipping">ShippingTotalsSortOrder</requiredEntity> - </entity> - - <entity name="ShippingTotalsSortOrder" type="shipping"> - <data key="value">27</data> - </entity> - - <entity name="DefaultCheckoutTotalsSortOrder" type="default_checkout_totals_sort_order"> - <requiredEntity type="checkoutTotalFlagZero">DefaultTotalFlagZero</requiredEntity> - </entity> - <entity name="DefaultTotalFlagZero" type="checkoutTotalFlagZero"> - <data key="value">0</data> - </entity> -</entities> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Metadata/checkout_config-meta.xml b/app/code/Magento/Checkout/Test/Mftf/Metadata/checkout_config-meta.xml deleted file mode 100644 index 55572ee73ac46..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Metadata/checkout_config-meta.xml +++ /dev/null @@ -1,86 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="SetCheckoutTotalsSortOrder" dataType="checkout_totals_sort_order" type="create" auth="adminFormKey" url="/admin/system_config/save/section/sales/" method="POST"> - <object key="groups" dataType="checkout_totals_sort_order"> - <object key="totals_sort" dataType="checkout_totals_sort_order"> - <object key="fields" dataType="checkout_totals_sort_order"> - <object key="subtotal" dataType="subtotal"> - <field key="value">integer</field> - </object> - <object key="discount" dataType="discount"> - <field key="value">integer</field> - </object> - <object key="shipping" dataType="shipping"> - <field key="value">integer</field> - </object> - <object key="tax" dataType="tax"> - <field key="value">integer</field> - </object> - <object key="weee" dataType="weee"> - <field key="value">integer</field> - </object> - <object key="grand_total" dataType="grand_total"> - <field key="value">integer</field> - </object> - <object key="giftcardaccount" dataType="giftcardaccount"> - <field key="value">integer</field> - </object> - <object key="customerbalance" dataType="customerbalance"> - <field key="value">integer</field> - </object> - </object> - </object> - </object> - </operation> - - <operation name="DefaultCheckoutTotalsSortOrder" dataType="default_checkout_totals_sort_order" type="create" auth="adminFormKey" url="/admin/system_config/save/section/sales/" method="POST"> - <object key="groups" dataType="default_checkout_totals_sort_order"> - <object key="totals_sort" dataType="default_checkout_totals_sort_order"> - <object key="fields" dataType="default_checkout_totals_sort_order"> - <object key="subtotal" dataType="default_checkout_totals_sort_order"> - <object key="inherit" dataType="checkoutTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="discount" dataType="default_checkout_totals_sort_order"> - <object key="inherit" dataType="checkoutTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="shipping" dataType="default_checkout_totals_sort_order"> - <object key="inherit" dataType="checkoutTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="tax" dataType="default_checkout_totals_sort_order"> - <object key="inherit" dataType="checkoutTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="weee" dataType="default_checkout_totals_sort_order"> - <object key="inherit" dataType="checkoutTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="grand_total" dataType="default_checkout_totals_sort_order"> - <object key="inherit" dataType="checkoutTotalFlagZero"> - <field key="value">integer</field> - </object> - </object> - <object key="giftcardaccount" dataType="giftcardaccount"> - <field key="value">integer</field> - </object> - <object key="customerbalance" dataType="customerbalance"> - <field key="value">integer</field> - </object> - </object> - </object> - </object> - </operation> -</operations> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml deleted file mode 100644 index 08623da68a8ef..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutTotalsSortOrderInCartTest.xml +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CheckoutTotalsSortOrderInCartTest"> - <annotations> - <title value="Checkout Totals Sort Order configuration and displaying in cart"/> - <stories value="MAGETWO-91658: Wrong Checkout Totals Sort Order in cart"/> - <description value="Checkout Totals Sort Order configuration and displaying in cart"/> - <features value="Checkout"/> - <severity value="AVERAGE"/> - <testCaseId value="MAGETWO-94944"/> - <group value="Checkout"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="defaultCategory"/> - <createData entity="SimpleProduct" stepKey="simpleProduct"> - <requiredEntity createDataKey="defaultCategory"/> - </createData> - - <createData entity="ApiCartRule" stepKey="cartRule"/> - - <createData entity="CheckoutShippingTotalsSortOrder" stepKey="setConfigShippingTotalsSortOrder"/> - </before> - - <actionGroup ref="VerifyDiscountAmount" stepKey="verifyStorefront"> - <argument name="productUrl" value="$$simpleProduct.sku$$.html"/> - <argument name="quantity" value="1"/> - <argument name="expectedDiscount" value="-$61.50"/> - </actionGroup> - - <actionGroup ref="CheckTotalsSortOrderInSummarySection" stepKey="checkTotalsSortOrderInSummarySection"> - <argument name="elementName" value="Shipping (Flat Rate - Fixed)"/> - <argument name="positionNumber" value="3"/> - </actionGroup> - - <after> - <createData entity="DefaultCheckoutTotalsSortOrder" stepKey="setDefaultTotalsSortOrder"/> - - <deleteData createDataKey="cartRule" stepKey="deleteCartRule"/> - <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="defaultCategory" stepKey="deleteCategory"/> - - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </after> - </test> -</tests> \ No newline at end of file From f631c90376dd2d217686e781147cc707bd816c8f Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 7 Nov 2018 14:36:22 +0200 Subject: [PATCH 206/310] magento/magento2#12970: [Forwardport] Can't add grouped product, with out of stock sub product, to cart. --- .../Model/Product/Type/Grouped.php | 2 +- .../_files/product_virtual_out_of_stock.php | 20 ++ .../product_virtual_out_of_stock_rollback.php | 26 +++ .../Model/Product/Type/GroupedTest.php | 194 ++++++++++++++++-- .../product_grouped_with_out_of_stock.php | 75 +++++++ ...uct_grouped_with_out_of_stock_rollback.php | 10 + 6 files changed, 311 insertions(+), 16 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php create mode 100644 dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 80824d45cb6e5..56de9ee9552ae 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -347,7 +347,7 @@ protected function getProductInfo(\Magento\Framework\DataObject $buyRequest, $pr if ($isStrictProcessMode && !$subProduct->getQty()) { return __('Please specify the quantity of product(s).')->render(); } - $productsInfo[$subProduct->getId()] = (int)$subProduct->getQty(); + $productsInfo[$subProduct->getId()] = $subProduct->isSalable() ? (float)$subProduct->getQty() : 0; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock.php new file mode 100644 index 0000000000000..cff2e83ed002e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL) + ->setId(31) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Virtual Product Out') + ->setSku('virtual-product-out') + ->setPrice(10) + ->setTaxClassId(0) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData(['is_in_stock' => 0]) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock_rollback.php new file mode 100644 index 0000000000000..8055ca4a25569 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_virtual_out_of_stock_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('virtual-product-out', false, null, true); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php index dcf4565873ea5..ed283d196e69c 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Model/Product/Type/GroupedTest.php @@ -5,8 +5,19 @@ */ namespace Magento\GroupedProduct\Model\Product\Type; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\CatalogInventory\Model\Configuration; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\Value; + class GroupedTest extends \PHPUnit\Framework\TestCase { + /** + * @var ReinitableConfigInterface + */ + private $reinitableConfig; + /** * @var \Magento\Framework\ObjectManagerInterface */ @@ -20,16 +31,21 @@ class GroupedTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->_productType = $this->objectManager->get(\Magento\Catalog\Model\Product\Type::class); + $this->reinitableConfig = $this->objectManager->get(ReinitableConfigInterface::class); + } + + protected function tearDown() + { + $this->dropConfigValue(Configuration::XML_PATH_SHOW_OUT_OF_STOCK); } public function testFactory() { $product = new \Magento\Framework\DataObject(); - $product->setTypeId(\Magento\GroupedProduct\Model\Product\Type\Grouped::TYPE_CODE); + $product->setTypeId(Grouped::TYPE_CODE); $type = $this->_productType->factory($product); - $this->assertInstanceOf(\Magento\GroupedProduct\Model\Product\Type\Grouped::class, $type); + $this->assertInstanceOf(Grouped::class, $type); } /** @@ -38,12 +54,12 @@ public function testFactory() */ public function testGetAssociatedProducts() { - $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - /** @var \Magento\Catalog\Model\Product $product */ + /** @var Product $product */ $product = $productRepository->get('grouped-product'); $type = $product->getTypeInstance(); - $this->assertInstanceOf(\Magento\GroupedProduct\Model\Product\Type\Grouped::class, $type); + $this->assertInstanceOf(Grouped::class, $type); $associatedProducts = $type->getAssociatedProducts($product); $this->assertCount(2, $associatedProducts); @@ -53,7 +69,7 @@ public function testGetAssociatedProducts() } /** - * @param \Magento\Catalog\Model\Product $product + * @param Product $product */ private function assertProductInfo($product) { @@ -92,25 +108,25 @@ public function testPrepareProduct() \Magento\Framework\DataObject::class, ['data' => ['value' => ['qty' => 2]]] ); - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get('grouped-product'); - /** @var \Magento\GroupedProduct\Model\Product\Type\Grouped $type */ - $type = $this->objectManager->get(\Magento\GroupedProduct\Model\Product\Type\Grouped::class); + /** @var Grouped $type */ + $type = $this->objectManager->get(Grouped::class); $processModes = [ - \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_FULL, - \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_LITE + Grouped::PROCESS_MODE_FULL, + Grouped::PROCESS_MODE_LITE ]; $expectedData = [ - \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_FULL => [ + Grouped::PROCESS_MODE_FULL => [ 1 => '{"super_product_config":{"product_type":"grouped","product_id":"' . $product->getId() . '"}}', 21 => '{"super_product_config":{"product_type":"grouped","product_id":"' . $product->getId() . '"}}', ], - \Magento\GroupedProduct\Model\Product\Type\Grouped::PROCESS_MODE_LITE => [ + Grouped::PROCESS_MODE_LITE => [ $product->getId() => '{"value":{"qty":2}}', ] ]; @@ -127,4 +143,152 @@ public function testPrepareProduct() } } } + + /** + * Test adding grouped product to cart when one of subproducts is out of stock. + * + * @magentoDataFixture Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @dataProvider outOfStockSubProductDataProvider + * @param bool $outOfStockShown + * @param array $data + * @param array $expected + */ + public function testOutOfStockSubProduct(bool $outOfStockShown, array $data, array $expected) + { + $this->changeConfigValue(Configuration::XML_PATH_SHOW_OUT_OF_STOCK, $outOfStockShown); + $buyRequest = new \Magento\Framework\DataObject($data); + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var Product $product */ + $product = $productRepository->get('grouped-product'); + /** @var Grouped $groupedProduct */ + $groupedProduct = $this->objectManager->get(Grouped::class); + $actual = $groupedProduct->prepareForCartAdvanced($buyRequest, $product, Grouped::PROCESS_MODE_FULL); + self::assertEquals( + count($expected), + count($actual) + ); + /** @var Product $product */ + foreach ($actual as $product) { + $sku = $product->getSku(); + self::assertEquals( + $expected[$sku], + $product->getCartQty(), + "Failed asserting that Product Cart Quantity matches expected" + ); + } + } + + /** + * Data provider for testOutOfStockSubProduct. + * + * @return array + */ + public function outOfStockSubProductDataProvider() + { + return [ + 'Out of stock product are shown #1' => [ + true, + [ + 'product' => 3, + 'qty' => 1, + 'super_group' => [ + 1 => 4, + 21 => 5, + ], + ], + [ + 'virtual-product' => 5, + 'simple' => 4 + ], + ], + 'Out of stock product are shown #2' => [ + true, + [ + 'product' => 3, + 'qty' => 1, + 'super_group' => [ + 1 => 0, + ], + ], + [ + 'virtual-product' => 2.5, // This is a default quantity. + ], + ], + 'Out of stock product are hidden #1' => [ + false, + [ + 'product' => 3, + 'qty' => 1, + 'super_group' => [ + 1 => 4, + 21 => 5, + ], + ], + [ + 'virtual-product' => 5, + 'simple' => 4, + ], + ], + 'Out of stock product are hidden #2' => [ + false, + [ + 'product' => 3, + 'qty' => 1, + 'super_group' => [ + 1 => 0, + ], + ], + [ + 'virtual-product' => 2.5, // This is a default quantity. + ], + ], + ]; + } + + /** + * Write config value to database. + * + * @param string $path + * @param string $value + * @param string $scope + * @param int $scopeId + */ + private function changeConfigValue(string $path, string $value, string $scope = 'default', int $scopeId = 0) + { + $configValue = $this->objectManager->create(Value::class); + $configValue->setPath($path) + ->setValue($value) + ->setScope($scope) + ->setScopeId($scopeId) + ->save(); + $this->reinitConfig(); + } + + /** + * Delete config value from database. + * + * @param string $path + */ + private function dropConfigValue(string $path) + { + $configValue = $this->objectManager->create(Value::class); + try { + $configValue->load($path, 'path'); + $configValue->delete(); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + // do nothing + } + $this->reinitConfig(); + } + + /** + * Reinit config. + */ + private function reinitConfig() + { + $this->reinitableConfig->reinit(); + } } diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php new file mode 100644 index 0000000000000..7aa62b149b8c0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php @@ -0,0 +1,75 @@ +\<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require realpath(__DIR__ . '/../../') . '/Catalog/_files/product_associated.php'; +require realpath(__DIR__ . '/../../') . '/Catalog/_files/product_virtual_in_stock.php'; +require realpath(__DIR__ . '/../../') . '/Catalog/_files/product_virtual_out_of_stock.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->isObjectNew(true); +$product->setTypeId( + \Magento\GroupedProduct\Model\Product\Type\Grouped::TYPE_CODE +)->setAttributeSetId( + 4 +)->setWebsiteIds( + [1] +)->setName( + 'Grouped Product' +)->setSku( + 'grouped-product' +)->setPrice( + 100 +)->setTaxClassId( + 0 +)->setVisibility( + \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH +)->setStatus( + \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED +); + +$newLinks = []; +$productLinkFactory = $objectManager->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class); + +/** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ +$productLink = $productLinkFactory->create(); +$linkedProduct = $productRepository->getById(1); +$productLink->setSku($product->getSku()) + ->setLinkType('associated') + ->setLinkedProductSku($linkedProduct->getSku()) + ->setLinkedProductType($linkedProduct->getTypeId()) + ->setPosition(1) + ->getExtensionAttributes() + ->setQty(1); +$newLinks[] = $productLink; + +$subProductsSkus = ['virtual-product', 'virtual-product-out']; +foreach ($subProductsSkus as $sku) { + /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ + $productLink = $productLinkFactory->create(); + $linkedProduct = $productRepository->get($sku); + $productLink->setSku($product->getSku()) + ->setLinkType('associated') + ->setLinkedProductSku($sku) + ->setLinkedProductType($linkedProduct->getTypeId()) + ->getExtensionAttributes() + ->setQty(2.5); + $newLinks[] = $productLink; +} +$product->setProductLinks($newLinks); +$product->save(); + +/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php new file mode 100644 index 0000000000000..48e7d495f8985 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php @@ -0,0 +1,10 @@ +\<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require 'product_grouped_rollback.php'; +require realpath(__DIR__ . '/../../') . '/Catalog/_files/product_virtual_out_of_stock_rollback.php'; +require realpath(__DIR__ . '/../../') . '/Catalog/_files/product_virtual_in_stock_rollback.php'; +require realpath(__DIR__ . '/../../') . '/Catalog/_files/product_associated_rollback.php'; From 2cab212809680085450836442c9346130c1f6440 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 7 Nov 2018 16:02:31 +0200 Subject: [PATCH 207/310] Covering the \Magento\Weee\Observer\AddPaymentWeeeItem by Unit Test --- .../Unit/Observer/AddPaymentWeeeItemTest.php | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 app/code/Magento/Weee/Test/Unit/Observer/AddPaymentWeeeItemTest.php diff --git a/app/code/Magento/Weee/Test/Unit/Observer/AddPaymentWeeeItemTest.php b/app/code/Magento/Weee/Test/Unit/Observer/AddPaymentWeeeItemTest.php new file mode 100644 index 0000000000000..1e707b118dccb --- /dev/null +++ b/app/code/Magento/Weee/Test/Unit/Observer/AddPaymentWeeeItemTest.php @@ -0,0 +1,154 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Test\Unit\Observer; + +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Payment\Model\Cart; +use Magento\Payment\Model\Cart\SalesModel\SalesModelInterface; +use Magento\Quote\Model\Quote\Item; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Weee\Helper\Data; +use Magento\Weee\Observer\AddPaymentWeeeItem; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class AddPaymentWeeeItemTest + */ +class AddPaymentWeeeItemTest extends TestCase +{ + /** + * Testable object + * + * @var AddPaymentWeeeItem + */ + private $observer; + + /** + * @var Data|MockObject + */ + private $weeeHelperMock; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * Set Up + */ + protected function setUp() + { + $this->weeeHelperMock = $this->createMock(Data::class); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + + $this->observer = new AddPaymentWeeeItem( + $this->weeeHelperMock, + $this->storeManagerMock + ); + } + + /** + * Test execute + * + * @dataProvider dataProvider + * @param $isEnabled + * @param $includeInSubtotal + * @return void + */ + public function testExecute($isEnabled, $includeInSubtotal): void + { + /** @var Observer|MockObject $observerMock */ + $observerMock = $this->createMock(Observer::class); + $cartModelMock = $this->createMock(Cart::class); + $salesModelMock = $this->createMock(SalesModelInterface::class); + $itemMock = $this->createPartialMock(Item::class, ['getOriginalItem']); + $originalItemMock = $this->createPartialMock(Item::class, ['getParentItem']); + $parentItemMock = $this->createMock(Item::class); + $eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getCart']) + ->getMock(); + + $asCustomItem = $this->prepareShouldBeAddedAsCustomItem($isEnabled, $includeInSubtotal); + $toBeCalled = 1; + if (!$asCustomItem) { + $toBeCalled = 0; + } + + $eventMock->expects($this->exactly($toBeCalled)) + ->method('getCart') + ->willReturn($cartModelMock); + $observerMock->expects($this->exactly($toBeCalled)) + ->method('getEvent') + ->willReturn($eventMock); + $itemMock->expects($this->exactly($toBeCalled)) + ->method('getOriginalItem') + ->willReturn($originalItemMock); + $originalItemMock->expects($this->exactly($toBeCalled)) + ->method('getParentItem') + ->willReturn($parentItemMock); + $salesModelMock->expects($this->exactly($toBeCalled)) + ->method('getAllItems') + ->willReturn([$itemMock]); + $cartModelMock->expects($this->exactly($toBeCalled)) + ->method('getSalesModel') + ->willReturn($salesModelMock); + + $this->observer->execute($observerMock); + } + + /** + * @return array + */ + public function dataProvider(): array + { + return [ + [true, false], + [true, true], + [false, true], + [false, false], + ]; + } + + /** + * Prepare if FPT should be added to payment cart as custom item or not. + * + * @param $isEnabled + * @param $includeInSubtotal + * @return bool + */ + private function prepareShouldBeAddedAsCustomItem(bool $isEnabled, bool $includeInSubtotal): bool + { + $storeMock = $this->getMockBuilder(StoreInterface::class) + ->setMethods(['getId']) + ->getMockForAbstractClass(); + $storeMock->expects($this->once()) + ->method('getId') + ->willReturn(Store::DEFAULT_STORE_ID); + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->willReturn($storeMock); + $this->weeeHelperMock->expects($this->once()) + ->method('isEnabled') + ->with(Store::DEFAULT_STORE_ID) + ->willReturn($isEnabled); + + if ($isEnabled) { + $this->weeeHelperMock->expects($this->once()) + ->method('includeInSubtotal') + ->with(Store::DEFAULT_STORE_ID) + ->willReturn($includeInSubtotal); + } + + return $isEnabled && !$includeInSubtotal; + } +} From bb20a8b757ca43f16b63e98dd97aa5c6f630eaa0 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 7 Nov 2018 16:45:52 +0200 Subject: [PATCH 208/310] Small refactoring. Covering the SetWeeeRendererInFormObserver class by Unit Test --- .../Unit/Observer/AddPaymentWeeeItemTest.php | 10 +-- .../SetWeeeRendererInFormObserverTest.php | 88 +++++++++++++++++++ 2 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Weee/Test/Unit/Observer/SetWeeeRendererInFormObserverTest.php diff --git a/app/code/Magento/Weee/Test/Unit/Observer/AddPaymentWeeeItemTest.php b/app/code/Magento/Weee/Test/Unit/Observer/AddPaymentWeeeItemTest.php index 1e707b118dccb..dd0547354cfa4 100644 --- a/app/code/Magento/Weee/Test/Unit/Observer/AddPaymentWeeeItemTest.php +++ b/app/code/Magento/Weee/Test/Unit/Observer/AddPaymentWeeeItemTest.php @@ -60,11 +60,11 @@ protected function setUp() * Test execute * * @dataProvider dataProvider - * @param $isEnabled - * @param $includeInSubtotal + * @param bool $isEnabled + * @param bool $includeInSubtotal * @return void */ - public function testExecute($isEnabled, $includeInSubtotal): void + public function testExecute(bool $isEnabled, bool $includeInSubtotal): void { /** @var Observer|MockObject $observerMock */ $observerMock = $this->createMock(Observer::class); @@ -122,8 +122,8 @@ public function dataProvider(): array /** * Prepare if FPT should be added to payment cart as custom item or not. * - * @param $isEnabled - * @param $includeInSubtotal + * @param bool $isEnabled + * @param bool $includeInSubtotal * @return bool */ private function prepareShouldBeAddedAsCustomItem(bool $isEnabled, bool $includeInSubtotal): bool diff --git a/app/code/Magento/Weee/Test/Unit/Observer/SetWeeeRendererInFormObserverTest.php b/app/code/Magento/Weee/Test/Unit/Observer/SetWeeeRendererInFormObserverTest.php new file mode 100644 index 0000000000000..188b42cb37906 --- /dev/null +++ b/app/code/Magento/Weee/Test/Unit/Observer/SetWeeeRendererInFormObserverTest.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Test\Unit\Observer; + +use Magento\Framework\Data\Form; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\View\LayoutInterface; +use Magento\Weee\Model\Tax; +use Magento\Weee\Observer\SetWeeeRendererInFormObserver; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class AddPaymentWeeeItemTest + */ +class SetWeeeRendererInFormObserverTest extends TestCase +{ + /** + * Testable object + * + * @var SetWeeeRendererInFormObserver + */ + private $observer; + + /** + * @var LayoutInterface|MockObject + */ + private $layoutMock; + + /** + * @var Tax|MockObject + */ + private $taxModelMock; + + /** + * Set Up + */ + protected function setUp() + { + $this->layoutMock = $this->createMock(LayoutInterface::class); + $this->taxModelMock = $this->createMock(Tax::class); + $this->observer = new SetWeeeRendererInFormObserver( + $this->layoutMock, + $this->taxModelMock + ); + } + + /** + * Test assigning a custom renderer for product create/edit form weee attribute element + * + * @return void + */ + public function testExecute(): void + { + $attributes = new \ArrayIterator(['element_code_1', 'element_code_2']); + /** @var Event|MockObject $eventMock */ + $eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getForm']) + ->getMock(); + + /** @var Observer|MockObject $observerMock */ + $observerMock = $this->createMock(Observer::class); + /** @var Form|MockObject $formMock */ + $formMock = $this->createMock(Form::class); + + $eventMock->expects($this->once()) + ->method('getForm') + ->willReturn($formMock); + $observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($eventMock); + $this->taxModelMock->expects($this->once()) + ->method('getWeeeAttributeCodes') + ->willReturn($attributes); + $formMock->expects($this->exactly($attributes->count())) + ->method('getElement') + ->willReturnSelf(); + + $this->observer->execute($observerMock); + } +} From d7fca7216491ac404b00ca6ff43d0ac6751a5597 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Tue, 6 Nov 2018 19:26:30 -0600 Subject: [PATCH 209/310] MAGETWO-95745: Signifyd case not created for WorldPay - revert MAGETWO-94269 --- .../Paypal/Controller/Payflow/ReturnUrl.php | 4 ++-- .../Unit/Controller/Payflow/ReturnUrlTest.php | 16 ---------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php index 56a5da40edb85..cf932f046d1be 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php +++ b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php @@ -6,6 +6,7 @@ */ namespace Magento\Paypal\Controller\Payflow; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\CsrfAwareActionInterface; use Magento\Framework\App\Request\InvalidRequestException; use Magento\Framework\App\RequestInterface; @@ -13,7 +14,7 @@ use Magento\Paypal\Model\Config; use Magento\Sales\Model\Order; -class ReturnUrl extends Payflow implements CsrfAwareActionInterface +class ReturnUrl extends Payflow implements CsrfAwareActionInterface, HttpGetActionInterface { /** * @var array of allowed order states on frontend @@ -68,7 +69,6 @@ public function execute() if ($order->getIncrementId()) { if ($this->checkOrderState($order)) { $redirectBlock->setData('goto_success_page', true); - $this->_eventManager->dispatch('paypal_checkout_success', ['order' => $order]); } else { if ($this->checkPaymentMethod($order)) { $gotoSection = $this->_cancelPayment((string)$this->getRequest()->getParam('RESPMSG')); diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php index 44775d7e381bb..32d3f2c73b159 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Payflow/ReturnUrlTest.php @@ -5,7 +5,6 @@ */ namespace Magento\Paypal\Test\Unit\Controller\Payflow; -use Magento\Framework\Event\ManagerInterface; use Magento\Sales\Api\PaymentFailuresInterface; use Magento\Checkout\Block\Onepage\Success; use Magento\Checkout\Model\Session; @@ -97,11 +96,6 @@ class ReturnUrlTest extends \PHPUnit\Framework\TestCase */ private $paymentFailures; - /** - * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $eventManagerMock; - /** * @inheritdoc */ @@ -158,16 +152,10 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->eventManagerMock = $this->getMockBuilder(ManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->context->method('getView') ->willReturn($this->view); $this->context->method('getRequest') ->willReturn($this->request); - $this->context->method('getEventManager') - ->willReturn($this->eventManagerMock); $this->returnUrl = $this->objectManager->getObject( ReturnUrl::class, @@ -199,10 +187,6 @@ public function testExecuteAllowedOrderState($state) ->with('goto_success_page', true) ->willReturnSelf(); - $this->eventManagerMock->expects($this->once()) - ->method('dispatch') - ->with('paypal_checkout_success', $this->arrayHasKey('order')); - $result = $this->returnUrl->execute(); $this->assertNull($result); } From 385b01ef801cfa5fc5e2745bd71154bee85f8c3b Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Wed, 7 Nov 2018 10:58:54 -0600 Subject: [PATCH 210/310] MAGETWO-95745: Signifyd case not created for WorldPay - revert MAGETWO-71763 --- app/code/Magento/Signifyd/etc/events.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Signifyd/etc/events.xml b/app/code/Magento/Signifyd/etc/events.xml index e6418e2fdb6b4..d44665f9fb97b 100644 --- a/app/code/Magento/Signifyd/etc/events.xml +++ b/app/code/Magento/Signifyd/etc/events.xml @@ -9,9 +9,6 @@ <event name="checkout_submit_all_after"> <observer name="signifyd_place_order_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> </event> - <event name="paypal_express_place_order_success"> - <observer name="signifyd_place_order_paypal_express_observer" instance="Magento\Signifyd\Observer\PlaceOrder"/> - </event> <event name="paypal_checkout_success"> <observer name="signifyd_place_order_checkout_success_observer" instance="Magento\Signifyd\Observer\PlaceOrder" /> </event> From 436f0d995df9c0a374ec504f4302d2bf6e909af5 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Wed, 7 Nov 2018 11:32:54 -0600 Subject: [PATCH 211/310] MAGETWO-95745: Signifyd case not created for WorldPay - fix static test failures --- app/code/Magento/Checkout/Controller/Onepage/Success.php | 4 +++- app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Controller/Onepage/Success.php b/app/code/Magento/Checkout/Controller/Onepage/Success.php index e0872b425cc7c..a657b23cca4d6 100644 --- a/app/code/Magento/Checkout/Controller/Onepage/Success.php +++ b/app/code/Magento/Checkout/Controller/Onepage/Success.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -8,6 +7,9 @@ use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; +/** + * Onepage checkout success controller class + */ class Success extends \Magento\Checkout\Controller\Onepage implements HttpGetActionInterface { /** diff --git a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php index cf932f046d1be..d2a14febe54dd 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php +++ b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -14,6 +13,9 @@ use Magento\Paypal\Model\Config; use Magento\Sales\Model\Order; +/** + * Paypal Payflow ReturnUrl controller class + */ class ReturnUrl extends Payflow implements CsrfAwareActionInterface, HttpGetActionInterface { /** From 240cfb3e8b2ed77cf63566d7326e537771e8bceb Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Wed, 7 Nov 2018 20:35:18 +0200 Subject: [PATCH 212/310] graphQl: fixed typos and wrong descriptions --- .../QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php | 8 ++++---- .../Model/Cart/SetShippingAddressesOnCartInterface.php | 3 ++- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 3 +-- .../GraphQl/Quote/SetShippingAddressOnCartTest.php | 9 ++++----- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php index 588d4f65cc565..960900682db4c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php @@ -59,7 +59,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s { if (count($shippingAddresses) > 1) { throw new GraphQlInputException( - __('Multiple addresses do not allowed here!') + __('You cannot specify multiple shipping addresses.') ); } $shippingAddress = current($shippingAddresses); @@ -68,19 +68,19 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s if (!$customerAddressId && !$addressInput) { throw new GraphQlInputException( - __('Shipping address should contain either "customer_address_id" or "address" input.') + __('The shipping address must contain either "customer_address_id" or "address".') ); } if ($customerAddressId && $addressInput) { throw new GraphQlInputException( - __('Shipping address can\'t contain "customer_address_id" and "address" input at the same time.') + __('The shipping address cannot contain "customer_address_id" and "address" at the same time.') ); } if ($customerAddressId) { if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { throw new GraphQlAuthorizationException( __( - 'Address management allowed only for authorized customers.' + 'Guest users cannot manage addresses.' ) ); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php index ae337240c3a26..dde9cce9d8693 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php @@ -14,7 +14,8 @@ /** * Extension point for setting shipping addresses for a specified shopping cart * - * All objects that are responsible for set shipping addresses on cart via GraphQl should implement this interface. + * All objects that are responsible for setting shipping addresses on a cart via GraphQl + *should implement this interface. */ interface SetShippingAddressesOnCartInterface { diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 017b1f8119829..15d90c352ba62 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -6,11 +6,10 @@ type Query { } type Mutation { - createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") + createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates an empty shopping cart for a guest or logged in user") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart") - createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates empty shopping cart for guest or logged in user") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart") setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index 48ef8ebb790d1..2b4223cf67a89 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -139,13 +139,12 @@ public function testSetSavedShippingAddressOnCartByGuest() } } QUERY; - self::expectExceptionMessage('Address management allowed only for authorized customers.'); + self::expectExceptionMessage('Guest users cannot manage addresses.'); $this->graphQlQuery($query); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoConfigFixture default_store multishipping/options/checkout_multiple 0 */ public function testSetMultipleShippingAddressesOnCartByGuest() { @@ -185,7 +184,7 @@ public function testSetMultipleShippingAddressesOnCartByGuest() } } QUERY; - self::expectExceptionMessage('Multiple addresses do not allowed here!'); + self::expectExceptionMessage('You cannot specify multiple shipping addresses.'); $this->graphQlQuery($query); } @@ -240,7 +239,7 @@ public function testSetSavedAndNewShippingAddressOnCartAtTheSameTime() } QUERY; self::expectExceptionMessage( - 'Shipping address can\'t contain "customer_address_id" and "address" input at the same time.' + 'The shipping address cannot contain "customer_address_id" and "address" at the same time.' ); $this->graphQlQuery($query); } @@ -282,7 +281,7 @@ public function testSetShippingAddressOnCartWithNoAddresses() } QUERY; self::expectExceptionMessage( - 'Shipping address should contain either "customer_address_id" or "address" input.' + 'The shipping address must contain either "customer_address_id" or "address".' ); $this->graphQlQuery($query); } From b490fb16a0e21eb4b11327d4dcc2e54ebdfdaca4 Mon Sep 17 00:00:00 2001 From: larsroettig <l.roettig@techdivision.com> Date: Wed, 7 Nov 2018 20:35:28 +0100 Subject: [PATCH 213/310] #18956 Add TestCase for no root_category_id set --- .../Config/Importer/Processor/CreateTest.php | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php b/app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php index 0901464224399..0fbf7bb7f044b 100644 --- a/app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/Config/Importer/Processor/CreateTest.php @@ -196,14 +196,30 @@ private function initTestData() 'root_category_id' => '1', 'default_store_id' => '1', 'code' => 'default', + ], + 2 => [ + 'group_id' => '1', + 'website_id' => '1', + 'name' => 'Default1', + 'default_store_id' => '1', + 'code' => 'default1', ] ]; - $this->trimmedGroup = [ - 'name' => 'Default', - 'root_category_id' => '1', - 'code' => 'default', - 'default_store_id' => '1', - ]; + $this->trimmedGroup = + [ + 0 => [ + 'name' => 'Default', + 'root_category_id' => '1', + 'code' => 'default', + 'default_store_id' => '1', + ], + 1 => [ + 'name' => 'Default1', + 'root_category_id' => '0', + 'code' => 'default1', + 'default_store_id' => '1' + ] + ]; $this->stores = [ 'default' => [ 'store_id' => '1', @@ -280,31 +296,34 @@ public function testRunGroup() [ScopeInterface::SCOPE_GROUPS, $this->groups, $this->groups], ]); - $this->websiteMock->expects($this->once()) + $this->websiteMock->expects($this->exactly(2)) ->method('getResource') ->willReturn($this->abstractDbMock); - $this->groupMock->expects($this->once()) + $this->groupMock->expects($this->exactly(2)) ->method('setData') - ->with($this->trimmedGroup) - ->willReturnSelf(); - $this->groupMock->expects($this->exactly(3)) + ->withConsecutive( + [$this->equalTo($this->trimmedGroup[0])], + [$this->equalTo($this->trimmedGroup[1])] + )->willReturnSelf(); + + $this->groupMock->expects($this->exactly(6)) ->method('getResource') ->willReturn($this->abstractDbMock); - $this->groupMock->expects($this->once()) + $this->groupMock->expects($this->exactly(2)) ->method('getDefaultStoreId') ->willReturn($defaultStoreId); - $this->groupMock->expects($this->once()) + $this->groupMock->expects($this->exactly(2)) ->method('setDefaultStoreId') ->with($storeId); - $this->groupMock->expects($this->once()) + $this->groupMock->expects($this->exactly(2)) ->method('setWebsite') ->with($this->websiteMock); - $this->storeMock->expects($this->once()) + $this->storeMock->expects($this->exactly(2)) ->method('getResource') ->willReturn($this->abstractDbMock); - $this->storeMock->expects($this->once()) + $this->storeMock->expects($this->exactly(2)) ->method('getStoreId') ->willReturn($storeId); @@ -312,11 +331,11 @@ public function testRunGroup() ->method('load') ->withConsecutive([$this->websiteMock, 'base', 'code'], [$this->storeMock, 'default', 'code']) ->willReturnSelf(); - $this->abstractDbMock->expects($this->exactly(2)) + $this->abstractDbMock->expects($this->exactly(4)) ->method('save') ->with($this->groupMock) ->willReturnSelf(); - $this->abstractDbMock->expects($this->once()) + $this->abstractDbMock->expects($this->exactly(2)) ->method('addCommitCallback') ->willReturnCallback(function ($function) { return $function(); From bd34b53918b3c159b5ceb87ac59f66c84f1b76ee Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Thu, 8 Nov 2018 10:48:18 +0300 Subject: [PATCH 214/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../view/adminhtml/web/js/grouped-product-grid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index c06f615ae48e6..91454e8b3de78 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -114,7 +114,7 @@ define([ /** * - * @param {Array} recordData to reprocess + * @param {Array} - recordData to reprocess */ reloadGridData: function (recordData) { this.recordData(recordData.sort(function (a, b) { From c7a140ea0d878c43699d535b3c307ed2ffc5f7a0 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Thu, 8 Nov 2018 16:02:50 +0300 Subject: [PATCH 215/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../view/adminhtml/web/js/grouped-product-grid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 91454e8b3de78..58c3b79022a62 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -114,7 +114,7 @@ define([ /** * - * @param {Array} - recordData to reprocess + * @param {Array} recordData - to reprocess */ reloadGridData: function (recordData) { this.recordData(recordData.sort(function (a, b) { From f9acb655f01565c58d1df26643b4446407c4eb0a Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 8 Nov 2018 15:18:58 +0100 Subject: [PATCH 216/310] Added a separate resolver for returning category products count --- app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php | 1 - .../Magento/CatalogGraphQl/Model/Resolver/Category/Products.php | 2 +- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php b/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php index f0cdab9498abb..e783c749fdc6e 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php @@ -52,7 +52,6 @@ public function hydrateCategory(Category $category, $basicFieldsOnly = false) : $categoryData = $category->getData(); } else { $categoryData = $this->dataObjectProcessor->buildOutputDataArray($category, CategoryInterface::class); - $categoryData['product_count'] = $category->getProductCount(); } $categoryData['id'] = $category->getId(); $categoryData['children'] = []; diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php index 557c7e08ff432..59c21b6b0109d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php @@ -65,7 +65,7 @@ public function resolve( 'eq' => $value['id'] ] ]; - $searchCriteria = $this->searchCriteriaBuilder->build($field->getName(), $args); + $searchCriteria = $this->searchCriteriaBuilder->build($field->getName(), $args); $searchCriteria->setCurrentPage($args['currentPage']); $searchCriteria->setPageSize($args['pageSize']); $searchResult = $this->filterQuery->getResult($searchCriteria, $info); diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 5d62cb63f1662..7f31266ebf266 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -379,7 +379,7 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model level: Int @doc(description: "Indicates the depth of the category within the tree") created_at: String @doc(description: "Timestamp indicating when the category was created") updated_at: String @doc(description: "Timestamp indicating when the category was updated") - product_count: Int @doc(description: "The number of products in the category") + product_count: Int @doc(description: "The number of products in the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\ProductsCount") default_sort_by: String @doc(description: "The attribute to use for sorting") products( pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), From 9aacf0e0f9044c133786de725fbe5d926c0223d5 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 8 Nov 2018 15:29:11 +0100 Subject: [PATCH 217/310] Missing resolver added --- .../Model/Resolver/Category/Products.php | 2 +- .../Model/Resolver/Category/ProductsCount.php | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Category/ProductsCount.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php index 59c21b6b0109d..557c7e08ff432 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php @@ -65,7 +65,7 @@ public function resolve( 'eq' => $value['id'] ] ]; - $searchCriteria = $this->searchCriteriaBuilder->build($field->getName(), $args); + $searchCriteria = $this->searchCriteriaBuilder->build($field->getName(), $args); $searchCriteria->setCurrentPage($args['currentPage']); $searchCriteria->setPageSize($args['pageSize']); $searchResult = $this->filterQuery->getResult($searchCriteria, $info); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/ProductsCount.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/ProductsCount.php new file mode 100644 index 0000000000000..397fd12b7e714 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/ProductsCount.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Category; + +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product\Visibility; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessor\StockProcessor; +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; + +/** + * Retrieves products count for a category + */ +class ProductsCount implements ResolverInterface +{ + /** + * @var Visibility + */ + private $catalogProductVisibility; + + /** + * @var StockProcessor + */ + private $stockProcessor; + + /** + * @var SearchCriteriaInterface + */ + private $searchCriteria; + + /** + * @param Visibility $catalogProductVisibility + * @param SearchCriteriaInterface $searchCriteria + * @param StockProcessor $stockProcessor + */ + public function __construct( + Visibility $catalogProductVisibility, + SearchCriteriaInterface $searchCriteria, + StockProcessor $stockProcessor + ) { + $this->catalogProductVisibility = $catalogProductVisibility; + $this->searchCriteria = $searchCriteria; + $this->stockProcessor = $stockProcessor; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new GraphQlInputException(__('"model" value should be specified')); + } + /** @var Category $category */ + $category = $value['model']; + $productsCollection = $category->getProductCollection(); + $productsCollection->setVisibility($this->catalogProductVisibility->getVisibleInSiteIds()); + $productsCollection = $this->stockProcessor->process($productsCollection, $this->searchCriteria, []); + + return $productsCollection->getSize(); + } +} From c713b675dc4c473d9d001ec2ade980aeeaf254f3 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 8 Nov 2018 15:39:28 +0100 Subject: [PATCH 218/310] Added test for setting shipping methods as guest for a customer cart --- .../Quote/SetShippingMethodOnCartTest.php | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php index 932c8ac386c27..7e77284c6b220 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php @@ -161,7 +161,35 @@ public function testSetShippingMethodWithNonExistingAddress() $this->sendRequestWithToken($query); } - // TODO: TBD - add check for guest with attempt to set shipping method to the customer's shopping cart + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetShippingMethodByGuestToCustomerCart() + { + $shippingCarrierCode = 'flatrate'; + $shippingMethodCode = 'flatrate'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $shippingAddress = $this->quote->getShippingAddress(); + $shippingAddressId = $shippingAddress->getId(); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + self::expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + + $this->graphQlQuery($query); + } /** * Generates query for setting the specified shipping method on cart From 9b853a7303093342c2a29a17f3029da4428f8839 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Mon, 5 Nov 2018 15:42:06 -0600 Subject: [PATCH 219/310] MAGETWO-95906: Sales rules sets default time using UTC timezone when left empty --- .../Page/AdminConfigurationStoresPage.xml | 3 + .../Mftf/Section/LocaleOptionsSection.xml | 15 +++ .../StorefrontProductCartActionGroup.xml | 2 +- .../Controller/Adminhtml/Promo/Quote/Save.php | 38 ++++++- ...inCreateCartPriceRuleEmptyFromDateTest.xml | 104 ++++++++++++++++++ 5 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml index d1bf3c2cb2ed6..8afc2c5bbb32f 100644 --- a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml @@ -14,4 +14,7 @@ <page name="WebConfigurationPage" url="admin/system_config/edit/section/web/" area="admin" module="Backend"> <section name="WYSIWYGOptionsSection"/> </page> + <page name="GeneralConfigurationPage" url="admin/system_config/edit/section/general/" area="admin" module="Backend"> + <section name="LocaleOptionsSection"/> + </page> </pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml new file mode 100644 index 0000000000000..c50bf0664f9cb --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="LocaleOptionsSection"> + <element name="sectionHeader" type="text" selector="#general_locale-head"/> + <element name="timezone" type="select" selector="#general_locale_timezone"/> + </section> +</sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index 4b5b250078ad4..1473618ceeb34 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -86,8 +86,8 @@ <seeInCurrentUrl url="{{CheckoutCartPage.url}}" stepKey="assertUrl"/> <waitForText userInput="${{total}}" selector="{{CheckoutCartSummarySection.total}}" time="30" stepKey="waitForTotal"/> <see userInput="${{subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> - <see userInput="${{shipping}}" selector="{{CheckoutCartSummarySection.shipping}}" stepKey="assertShipping"/> <see userInput="({{shippingMethod}})" selector="{{CheckoutCartSummarySection.shippingMethod}}" stepKey="assertShippingMethod"/> + <waitForText userInput="${{shipping}}" selector="{{CheckoutCartSummarySection.shipping}}" time="30" stepKey="assertShipping"/> <see userInput="${{total}}" selector="{{CheckoutCartSummarySection.total}}" stepKey="assertTotal"/> </actionGroup> diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php index a5b71130e70eb..388679e6d9eff 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Save.php @@ -6,8 +6,41 @@ */ namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; -class Save extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; + +/** + * SalesRule save controller + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class Save extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote implements HttpPostActionInterface { + /** + * @var TimezoneInterface + */ + private $timezone; + + /** + * @param \Magento\Backend\App\Action\Context $context + * @param \Magento\Framework\Registry $coreRegistry + * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory + * @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter + * @param TimezoneInterface $timezone + */ + public function __construct( + \Magento\Backend\App\Action\Context $context, + \Magento\Framework\Registry $coreRegistry, + \Magento\Framework\App\Response\Http\FileFactory $fileFactory, + \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter, + TimezoneInterface $timezone = null + ) { + parent::__construct($context, $coreRegistry, $fileFactory, $dateFilter); + $this->timezone = $timezone ?? \Magento\Framework\App\ObjectManager::getInstance()->get( + TimezoneInterface::class + ); + } + /** * Promo quote save action * @@ -26,6 +59,9 @@ public function execute() ['request' => $this->getRequest()] ); $data = $this->getRequest()->getPostValue(); + if (empty($data['from_date'])) { + $data['from_date'] = $this->timezone->formatDate(); + } $filterValues = ['from_date' => $this->_dateFilter]; if ($this->getRequest()->getParam('to_date')) { diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml new file mode 100644 index 0000000000000..e6676dab4eb5e --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCartPriceRuleEmptyFromDateTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Admin should be able to create a cart price rule with no starting date"/> + <description value="Admin should be able to create a cart price rule without specifying the from_date and it should be set with the current date"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-5299"/> + <group value="SalesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="SimpleProduct" stepKey="product"> + <requiredEntity createDataKey="category"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <!-- Delete the cart price rule we made during the test --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule"> + <argument name="ruleName" value="{{_defaultCoupon.code}}"/> + </actionGroup> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <deleteData createDataKey="product" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Set timezone--> + <!--Set timezone so we need compare with the same timezone used in "generateDate" action--> + <amOnPage url="{{GeneralConfigurationPage.url}}" stepKey="goToGeneralConfig"/> + <waitForPageLoad stepKey="waitForConfigPage"/> + <wait stepKey="wait" time="10"/> + <conditionalClick selector="{{LocaleOptionsSection.sectionHeader}}" dependentSelector="{{LocaleOptionsSection.timezone}}" visible="false" stepKey="openLocaleSection"/> + <grabValueFrom selector="{{LocaleOptionsSection.timezone}}" stepKey="originalTimezone"/> + <selectOption selector="{{LocaleOptionsSection.timezone}}" userInput="America/Los_Angeles" stepKey="setTimezone"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfig"/> + + <!-- Create a cart price rule based on a coupon code --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForPriceList"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> + <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectCustomerGroup"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{_defaultCoupon.code}}" stepKey="fillCouponCode"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="5" stepKey="fillDiscountAmount"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + + <!-- Verify initial successful save --> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + <fillField selector="{{AdminCartPriceRulesSection.filterByNameInput}}" userInput="{{_defaultCoupon.code}}" stepKey="filterByName"/> + <click selector="{{AdminCartPriceRulesSection.searchButton}}" stepKey="doFilter"/> + <see selector="{{AdminCartPriceRulesSection.nameColumns}}" userInput="{{_defaultCoupon.code}}" stepKey="seeRuleInResults"/> + + <!-- Verify further on the Rule's edit page --> + <click selector="{{AdminCartPriceRulesSection.rowContainingText(_defaultCoupon.code)}}" stepKey="goToEditRule"/> + <seeInField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="seeRuleName"/> + <seeOptionIsSelected selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="seeWebsites"/> + <seeOptionIsSelected selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="seeCouponType"/> + <seeInField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{_defaultCoupon.code}}" stepKey="seeCouponCode"/> + <generateDate date="now" format="m/j/Y" timezone="America/Los_Angeles" stepKey="today"/> + <seeInField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="$today" stepKey="seeCorrectFromDate"/> + <seeInField selector="{{AdminCartPriceRulesFormSection.toDate}}" userInput="" stepKey="seeEmptyToDate"/> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions2"/> + <seeOptionIsSelected selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="seeActionType"/> + <seeInField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="5" stepKey="seeDiscountAmount"/> + + <!-- Spot check the storefront --> + <amOnPage url="$$product.custom_attributes[url_key]$$.html" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="applyCoupon"> + <argument name="coupon" value="_defaultCoupon"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$5.00" stepKey="seeDiscountTotal"/> + + <!--Reset timezone--> + <amOnPage url="{{GeneralConfigurationPage.url}}" stepKey="goToGeneralConfigReset"/> + <waitForPageLoad stepKey="waitForConfigPageReset"/> + <conditionalClick selector="{{LocaleOptionsSection.sectionHeader}}" dependentSelector="{{LocaleOptionsSection.timezone}}" visible="false" stepKey="openLocaleSectionReset"/> + <selectOption selector="{{LocaleOptionsSection.timezone}}" userInput="$originalTimezone" stepKey="resetTimezone"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfigReset"/> + </test> +</tests> From 13f4ad193092a153338418024facef621e598468 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Thu, 8 Nov 2018 08:41:45 -0600 Subject: [PATCH 220/310] MAGETWO-96192: Products with "Less Than", "Greater Than", "Less Than Or Equal", "Greater Than Or Equal" condition returns error on storefront - Added < and > to list of encoded characters --- .../Cms/Test/Mftf/Section/TinyMCESection.xml | 3 +++ ...oWYSIWYGWithCatalogProductListTypeTest.xml | 23 ++++++++++++++++++- .../Framework/Data/Wysiwyg/Normalizer.php | 3 ++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml index 92112661846c0..c7ea85e441bb9 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml @@ -98,6 +98,9 @@ <element name="AddParam" type="button" selector=".rule-param-add"/> <element name="ConditionsDropdown" type="select" selector="#conditions__1__new_child"/> <element name="RuleParam" type="button" selector="//a[text()='...']"/> + <element name="RuleParamSelect" type="select" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//select" parameterized="true"/> + <element name="RuleParamInput" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//input" parameterized="true"/> + <element name="RuleParamLabel" type="input" selector="//ul[contains(@class,'rule-param-children')]/li[{{arg1}}]//*[contains(@class,'rule-param')][{{arg2}}]//a" parameterized="true"/> <element name="Chooser" type="button" selector="//img[@title='Open Chooser']"/> <element name="PageSize" type="input" selector="input[name='parameters[page_size]']"/> <element name="ProductAttribute" type="multiselect" selector="select[name='parameters[show_attributes][]']" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml index 705f2883f5839..2586ffc11d086 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml @@ -57,8 +57,29 @@ <click selector="{{WidgetSection.RuleParam}}" stepKey="clickRuleParam" /> <waitForElementVisible selector="{{WidgetSection.Chooser}}" stepKey="waitForElement" /> <click selector="{{WidgetSection.Chooser}}" stepKey="clickChooser" /> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear3" /> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear4" /> <click selector="{{WidgetSection.PreCreateCategory('$$createPreReqCategory.name$$')}}" stepKey="selectPreCategory" /> + + <!-- Test that the "<" operand functions correctly --> + <click selector="{{WidgetSection.AddParam}}" stepKey="clickAddParamBtn2" /> + <waitForElementVisible selector="{{WidgetSection.ConditionsDropdown}}" stepKey="waitForDropdownVisible2"/> + <selectOption selector="{{WidgetSection.ConditionsDropdown}}" userInput="Price" stepKey="selectPriceCondition"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear3"/> + <click selector="{{WidgetSection.RuleParamLabel('2','1')}}" stepKey="clickOperatorLabel"/> + <selectOption selector="{{WidgetSection.RuleParamSelect('2','1')}}" userInput="<" stepKey="selectLessThanCondition"/> + <click selector="{{WidgetSection.RuleParam}}" stepKey="clickRuleParam2"/> + <fillField selector="{{WidgetSection.RuleParamInput('2','2')}}" userInput="125" stepKey="fillMaxPrice"/> + + <!-- Test that the ">" operand functions correctly --> + <click selector="{{WidgetSection.AddParam}}" stepKey="clickAddParamBtn3" /> + <waitForElementVisible selector="{{WidgetSection.ConditionsDropdown}}" stepKey="waitForDropdownVisible3"/> + <selectOption selector="{{WidgetSection.ConditionsDropdown}}" userInput="Price" stepKey="selectPriceCondition2"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear5"/> + <click selector="{{WidgetSection.RuleParamLabel('3','1')}}" stepKey="clickOperatorLabel2"/> + <selectOption selector="{{WidgetSection.RuleParamSelect('3','1')}}" userInput=">" stepKey="selectLessThanCondition2"/> + <click selector="{{WidgetSection.RuleParam}}" stepKey="clickRuleParam3"/> + <fillField selector="{{WidgetSection.RuleParamInput('3','2')}}" userInput="1" stepKey="fillMinPrice"/> + <click selector="{{WidgetSection.InsertWidget}}" stepKey="clickInsertWidget" /> <waitForPageLoad stepKey="wait6" /> <scrollTo selector="{{CmsNewPagePageSeoSection.header}}" stepKey="scrollToSearchEngineTab" /> diff --git a/lib/internal/Magento/Framework/Data/Wysiwyg/Normalizer.php b/lib/internal/Magento/Framework/Data/Wysiwyg/Normalizer.php index 62e7500a302a6..bbf10e9f574cf 100644 --- a/lib/internal/Magento/Framework/Data/Wysiwyg/Normalizer.php +++ b/lib/internal/Magento/Framework/Data/Wysiwyg/Normalizer.php @@ -10,12 +10,13 @@ */ class Normalizer { - const WYSIWYG_RESERVED_CHARACTERS_REPLACEMENT_MAP = [ '{' => '^[', '}' => '^]', '"' => '`', '\\' => '|', + '<' => '^(', + '>' => '^)' ]; /** From e5e7db172a906ae3baf94379d673db4856a78643 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Thu, 8 Nov 2018 17:48:37 +0300 Subject: [PATCH 221/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../view/adminhtml/web/js/grouped-product-grid.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 58c3b79022a62..28553eaa41d12 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -128,7 +128,7 @@ define([ * Event handler for "Send to bottom" button * * @param {Object} positionObj - * @return {boolean} + * @return {Boolean} */ sendToBottom: function (positionObj) { @@ -152,7 +152,7 @@ define([ * Event handler for "Send to top" button * * @param {Object} positionObj - * @returns {boolean} + * @return {Boolean} */ sendToTop: function (positionObj) { var objectToUpdate = this.getObjectToUpdate(positionObj), @@ -189,7 +189,7 @@ define([ * Value function for position input * * @param {Object} data - * @return {number} + * @return {Number} */ getCalculatedPosition: function (data) { return (~~this.currentPage() - 1) * this.pageSize + this.elems().pluck("name").indexOf(data.name); @@ -198,7 +198,7 @@ define([ /** * Return Page Boundary * - * @return {number} + * @return {Number} */ getDefaultPageBoundary: function () { return ~~this.currentPage() * this.pageSize - 1; @@ -207,7 +207,7 @@ define([ /** * Returns position for last element to be moved after * - * @return {number} + * @return {Number} */ getGlobalMaxPosition: function () { return _.max(this.recordData().map(function (r) { From 803cdb1a15c3f6736c3674a8eb24aa8713992e63 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi <mankivsk@adobe.com> Date: Thu, 8 Nov 2018 09:23:21 -0600 Subject: [PATCH 222/310] ENGCOM-3408: #18956 Fixes for set root_category_id #18958 - fixed docblocks --- .../Model/Config/Importer/Processor/Create.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php index 71ffa5303293d..1fb9f1c224d3e 100644 --- a/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php +++ b/app/code/Magento/Store/Model/Config/Importer/Processor/Create.php @@ -17,8 +17,6 @@ /** * The processor for creating of new entities. - * - * {@inheritdoc} */ class Create implements ProcessorInterface { @@ -85,7 +83,9 @@ public function __construct( /** * Creates entities in application according to the data set. * - * {@inheritdoc} + * @param array $data The data to be processed + * @return void + * @throws RuntimeException If processor was unable to finish execution */ public function run(array $data) { @@ -230,8 +230,7 @@ private function createStores(array $items, array $data) } /** - * Searches through given websites and compares with current websites. - * Returns found website. + * Searches through given websites and compares with current websites and returns found website. * * @param array $data The data to be searched in * @param string $websiteId The website id @@ -253,8 +252,7 @@ private function detectWebsiteById(array $data, $websiteId) } /** - * Searches through given groups and compares with current websites. - * Returns found group. + * Searches through given groups and compares with current websites and returns found group. * * @param array $data The data to be searched in * @param string $groupId The group id @@ -276,8 +274,7 @@ private function detectGroupById(array $data, $groupId) } /** - * Searches through given stores and compares with current stores. - * Returns found store. + * Searches through given stores and compares with current stores and returns found store. * * @param array $data The data to be searched in * @param string $storeId The store id From 12d035d335e3d8903253cf00f155c35efb701df5 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Thu, 8 Nov 2018 13:29:50 -0600 Subject: [PATCH 223/310] MAGETWO-96192: Products with "Less Than", "Greater Than", "Less Than Or Equal", "Greater Than Or Equal" condition returns error on storefront - Updatd integration test --- .../Magento/Widget/Setup/LayoutUpdateConverterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Widget/Setup/LayoutUpdateConverterTest.php b/dev/tests/integration/testsuite/Magento/Widget/Setup/LayoutUpdateConverterTest.php index 43c0bd202cc92..3d43fb5a44575 100644 --- a/dev/tests/integration/testsuite/Magento/Widget/Setup/LayoutUpdateConverterTest.php +++ b/dev/tests/integration/testsuite/Magento/Widget/Setup/LayoutUpdateConverterTest.php @@ -34,7 +34,7 @@ public function convertDataProvider() // @codingStandardsIgnoreStart $beginning = '<body><referenceContainer name="content"><block class="Magento\CatalogWidget\Block\Product\ProductsList" name="23e38bbfa7cc6474454570e51aeffcc3" template="Magento_CatalogWidget::product/widget/content/grid.phtml"><action method="setData"><argument name="name" xsi:type="string">show_pager</argument><argument name="value" xsi:type="string">0</argument></action><action method="setData"><argument name="name" xsi:type="string">products_count</argument><argument name="value" xsi:type="string">10</argument></action><action method="setData">'; $serializedWidgetXml = '<argument name="name" xsi:type="string">conditions_encoded</argument><argument name="value" xsi:type="string">a:3:[i:1;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Combine`;s:10:`aggregator`;s:3:`all`;s:5:`value`;s:1:`1`;s:9:`new_child`;s:0:``;]s:4:`1--1`;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Product`;s:9:`attribute`;s:3:`sku`;s:8:`operator`;s:2:`()`;s:5:`value`;s:15:`simple, simple1`;]s:4:`1--2`;a:4:[s:4:`type`;s:50:`Magento|CatalogWidget|Model|Rule|Condition|Product`;s:9:`attribute`;s:5:`price`;s:8:`operator`;s:2:`<=`;s:5:`value`;s:2:`10`;]]</argument>'; - $jsonEncodedWidgetXml = '<argument name="name" xsi:type="string">conditions_encoded</argument><argument name="value" xsi:type="string">^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,`aggregator`:`all`,`value`:`1`,`new_child`:``^],`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,`attribute`:`sku`,`operator`:`()`,`value`:`simple, simple1`^],`1--2`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,`attribute`:`price`,`operator`:`<=`,`value`:`10`^]^]</argument>'; + $jsonEncodedWidgetXml = '<argument name="name" xsi:type="string">conditions_encoded</argument><argument name="value" xsi:type="string">^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,`aggregator`:`all`,`value`:`1`,`new_child`:``^],`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,`attribute`:`sku`,`operator`:`()`,`value`:`simple, simple1`^],`1--2`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,`attribute`:`price`,`operator`:`^(=`,`value`:`10`^]^]</argument>'; $ending = '</action><action method="setData"><argument name="name" xsi:type="string">page_var_name</argument><argument name="value" xsi:type="string">pobqks</argument></action></block></referenceContainer></body>'; // @codingStandardsIgnoreEnd return [ From 336f62c887b625cdec1a050a42aa19538eb51005 Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Thu, 8 Nov 2018 19:07:56 -0600 Subject: [PATCH 224/310] MAGETWO-89232: Validation error appears if duplicate product twice - added functional test to cover the bug fix --- .../AdminProductGridActionGroup.xml | 7 ++++ .../Section/AdminProductFormActionSection.xml | 1 + .../AdminCreateProductDuplicateUrlkeyTest.xml | 42 +++++++++++++++++++ .../AdminSaveAndCloseActionGroup.xml | 6 +++ 4 files changed, 56 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 1bd9bb4a09c86..a077eced6d5d5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -164,6 +164,13 @@ <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> </actionGroup> + <!--Delete all products by filtering grid and using mass delete action--> + <actionGroup name="deleteAllDuplicateProductUsingProductGrid" extends="deleteProductUsingProductGrid"> + <arguments> + <argument name="product"/> + </arguments> + <remove keyForRemoval="seeProductSkuInGrid"/> + </actionGroup> <!--Delete a product by filtering grid and using delete action--> <actionGroup name="deleteProductBySku"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml index afbaba41a9bb7..c7d4cd16d788a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml @@ -15,5 +15,6 @@ <element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/> <element name="changeStoreButton" type="button" selector="#store-change-button" timeout="10"/> <element name="selectStoreView" type="button" selector="//ul[@data-role='stores-list']/li/a[normalize-space(.)='{{var1}}']" timeout="10" parameterized="true"/> + <element name="saveAndDuplicate" type="button" selector="span[id='save_and_duplicate']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml index 95d74b9653113..8dffc3d352a7b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml @@ -40,4 +40,46 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <see userInput="The value specified in the URL Key field would generate a URL that already exists" selector="{{AdminProductMessagesSection.errorMessage}}" stepKey="assertErrorMessage"/> </test> + <test name="AdminCreateProductDuplicateProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Errors"/> + <title value="No validation errors when trying to duplicate product twice"/> + <description value="No validation errors when trying to duplicate product twice"/> + <severity value="MAJOR"/> + <testCaseId value="MC-5472"/> + <group value="product"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!--Delete all products by filtering grid and using mass delete action--> + <actionGroup ref="deleteAllDuplicateProductUsingProductGrid" stepKey="deleteAllDuplicateProducts"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <deleteData createDataKey="createCategory" stepKey="deletePreReqCatalog" /> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!--Save and duplicated the product once--> + <actionGroup ref="AdminFormSaveAndDuplicate" stepKey="saveAndDuplicateProductForm1"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct2"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!--Save and duplicated the product second time--> + <actionGroup ref="AdminFormSaveAndDuplicate" stepKey="saveAndDuplicateProductForm2"/> + </test> </tests> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml index 9a9458ab34d2b..20c8927d49171 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminSaveAndCloseActionGroup.xml @@ -13,4 +13,10 @@ <click selector="{{AdminProductFormActionSection.saveAndClose}}" stepKey="clickOnSaveAndClose"/> <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> </actionGroup> + <actionGroup name="AdminFormSaveAndDuplicate"> + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> + <click selector="{{AdminProductFormActionSection.saveAndDuplicate}}" stepKey="clickOnSaveAndDuplicate"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveSuccess" userInput="You saved the product."/> + <see selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertDuplicateSuccess" userInput="You duplicated the product."/> + </actionGroup> </actionGroups> From dc51f3e13799a46d8ea6204892c230d15e142b84 Mon Sep 17 00:00:00 2001 From: Alexandr Voronoy <servermed@gmail.com> Date: Fri, 9 Nov 2018 04:25:16 +0200 Subject: [PATCH 225/310] Missed PHPDoc argument headers in method --- .../framework/Magento/TestFramework/TestCase/GraphQlAbstract.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php index cfcd5dd1b51dd..9754a340900e2 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php @@ -33,6 +33,7 @@ abstract class GraphQlAbstract extends WebapiAbstract * @param string $query * @param array $variables * @param string $operationName + * @param array $headers * @return array|int|string|float|bool GraphQL call results * @throws \Exception */ From 15668ac2018ebfae033bd0cc18ded3e8628549ec Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Fri, 9 Nov 2018 12:30:37 +0300 Subject: [PATCH 226/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 28553eaa41d12..1ec11797e311f 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -170,6 +170,7 @@ define([ }); this.reloadGridData(recordData); } + return false; }, From 10e64f25fc69c35010a74cb5628e67a64e6f9256 Mon Sep 17 00:00:00 2001 From: Yevhenii Dumskyi <yevhenii.dumskyi@gmail.com> Date: Fri, 9 Nov 2018 12:16:37 +0200 Subject: [PATCH 227/310] Add save product with image integration test scenario --- .../Catalog/Model/ProductRepositoryTest.php | 48 +++++++++++++++++++ .../_files/product_simple_with_image.php | 45 +++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_image.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php index 39752460a1cd7..d4016b2bfa8d4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php @@ -90,4 +90,52 @@ public function skuDataProvider(): array ['sku' => 'simple '], ]; } + + /** + * Test save product with gallery image + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_image.php + * + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\StateException + */ + public function testSaveProductWithGalleryImage(): void + { + /** @var $mediaConfig \Magento\Catalog\Model\Product\Media\Config */ + $mediaConfig = Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Model\Product\Media\Config::class); + + /** @var $mediaDirectory \Magento\Framework\Filesystem\Directory\WriteInterface */ + $mediaDirectory = Bootstrap::getObjectManager() + ->get(\Magento\Framework\Filesystem::class) + ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA); + + $product = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $product->load(1); + + $path = $mediaConfig->getBaseMediaPath() . '/magento_image.jpg'; + $absolutePath = $mediaDirectory->getAbsolutePath() . $path; + $product->addImageToMediaGallery($absolutePath, [ + 'image', + 'small_image', + ], false, false); + + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productRepository->save($product); + + $gallery = $product->getData('media_gallery'); + $this->assertArrayHasKey('images', $gallery); + $images = array_values($gallery['images']); + + $this->assertNotEmpty($gallery); + $this->assertTrue(isset($images[0]['file'])); + $this->assertStringStartsWith('/m/a/magento_image', $images[0]['file']); + $this->assertArrayHasKey('media_type', $images[0]); + $this->assertEquals('image', $images[0]['media_type']); + $this->assertStringStartsWith('/m/a/magento_image', $product->getData('image')); + $this->assertStringStartsWith('/m/a/magento_image', $product->getData('small_image')); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_image.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_image.php new file mode 100644 index 0000000000000..252f99c97b787 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_image.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Framework\App\Filesystem\DirectoryList; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(); + +/** @var \Magento\TestFramework\ObjectManager $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->isObjectNew(true); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId(1) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product') + ->setSku('simple') + ->setPrice(10) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED); + +/** @var $mediaConfig \Magento\Catalog\Model\Product\Media\Config */ +$mediaConfig = $objectManager->get(\Magento\Catalog\Model\Product\Media\Config::class); + +/** @var $mediaDirectory \Magento\Framework\Filesystem\Directory\WriteInterface */ +$mediaDirectory = $objectManager->get(\Magento\Framework\Filesystem::class) + ->getDirectoryWrite(DirectoryList::MEDIA); + +$targetDirPath = $mediaConfig->getBaseMediaPath(); +$targetTmpDirPath = $mediaConfig->getBaseTmpMediaPath(); + +$mediaDirectory->create($targetDirPath); +$mediaDirectory->create($targetTmpDirPath); + +$dist = $mediaDirectory->getAbsolutePath($mediaConfig->getBaseMediaPath() . DIRECTORY_SEPARATOR . 'magento_image.jpg'); +copy(__DIR__ . '/magento_image.jpg', $dist); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productRepository->save($product); From 88b3e279455ffbcb5e901ab747dbd052bb851465 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Fri, 9 Nov 2018 13:33:57 +0300 Subject: [PATCH 228/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Fix static tests --- .../view/adminhtml/web/js/grouped-product-grid.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js index 1ec11797e311f..0ac3b58d6e3a7 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js +++ b/app/code/Magento/GroupedProduct/view/adminhtml/web/js/grouped-product-grid.js @@ -193,7 +193,7 @@ define([ * @return {Number} */ getCalculatedPosition: function (data) { - return (~~this.currentPage() - 1) * this.pageSize + this.elems().pluck("name").indexOf(data.name); + return (~~this.currentPage() - 1) * this.pageSize + this.elems().pluck('name').indexOf(data.name); }, /** From fe7b9ac55097b64a023e607acacb1857f1926367 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu <v.burlacu@atwix.com> Date: Fri, 9 Nov 2018 13:48:17 +0200 Subject: [PATCH 229/310] magento/magento2:#19101 - API REST and Reserved Order Id - Fixed issue "Can not update cart with a reserved order number like 000000651" --- app/code/Magento/Quote/Api/Data/CartInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Api/Data/CartInterface.php b/app/code/Magento/Quote/Api/Data/CartInterface.php index 551833e3effb1..b87869de6b3df 100644 --- a/app/code/Magento/Quote/Api/Data/CartInterface.php +++ b/app/code/Magento/Quote/Api/Data/CartInterface.php @@ -223,14 +223,14 @@ public function setBillingAddress(\Magento\Quote\Api\Data\AddressInterface $bill /** * Returns the reserved order ID for the cart. * - * @return int|null Reserved order ID. Otherwise, null. + * @return string|null Reserved order ID. Otherwise, null. */ public function getReservedOrderId(); /** * Sets the reserved order ID for the cart. * - * @param int $reservedOrderId + * @param string $reservedOrderId * @return $this */ public function setReservedOrderId($reservedOrderId); From e767121cab5385e1ea5f36b237622eb258333566 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Fri, 9 Nov 2018 15:54:57 +0300 Subject: [PATCH 230/310] MAGETWO-91650: Translation not working for product alerts - Add store id for product alert --- .../ProductAlert/etc/db_schema_whitelist.json | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json b/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json index 234e3d14876e5..94c9d07c85015 100644 --- a/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json +++ b/app/code/Magento/ProductAlert/etc/db_schema_whitelist.json @@ -1,6 +1,7 @@ { "product_alert_price": { "column": { + "store_id": true, "alert_price_id": true, "customer_id": true, "product_id": true, @@ -9,26 +10,26 @@ "add_date": true, "last_send_date": true, "send_count": true, - "status": true, - "store_id": true + "status": true }, "index": { + "PRODUCT_ALERT_PRICE_STORE_ID": true, "PRODUCT_ALERT_PRICE_CUSTOMER_ID": true, "PRODUCT_ALERT_PRICE_PRODUCT_ID": true, - "PRODUCT_ALERT_PRICE_WEBSITE_ID": true, - "PRODUCT_ALERT_PRICE_STORE_ID": true + "PRODUCT_ALERT_PRICE_WEBSITE_ID": true }, "constraint": { + "PRODUCT_ALERT_PRICE_STORE_ID_STORE_STORE_ID": true, "PRIMARY": true, "PRODUCT_ALERT_PRICE_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, "PRODUCT_ALERT_PRICE_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, "PRODUCT_ALERT_PRICE_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, - "PRODUCT_ALERT_PRICE_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true, - "PRODUCT_ALERT_PRICE_STORE_ID_STORE_STORE_ID": true + "PRODUCT_ALERT_PRICE_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true } }, "product_alert_stock": { "column": { + "store_id": true, "alert_stock_id": true, "customer_id": true, "product_id": true, @@ -36,22 +37,21 @@ "add_date": true, "send_date": true, "send_count": true, - "status": true, - "store_id": true + "status": true }, "index": { + "PRODUCT_ALERT_STOCK_STORE_ID": true, "PRODUCT_ALERT_STOCK_CUSTOMER_ID": true, "PRODUCT_ALERT_STOCK_PRODUCT_ID": true, - "PRODUCT_ALERT_STOCK_WEBSITE_ID": true, - "PRODUCT_ALERT_STOCK_STORE_ID": true + "PRODUCT_ALERT_STOCK_WEBSITE_ID": true }, "constraint": { + "PRODUCT_ALERT_STOCK_STORE_ID_STORE_STORE_ID": true, "PRIMARY": true, "PRODUCT_ALERT_STOCK_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true, "PRODUCT_ALERT_STOCK_CUSTOMER_ID_CUSTOMER_ENTITY_ENTITY_ID": true, "PRODUCT_ALERT_STOCK_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID": true, - "PRODUCT_ALERT_STOCK_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true, - "PRODUCT_ALERT_STOCK_STORE_ID_STORE_STORE_ID": true + "PRODUCT_ALERT_STOCK_PRODUCT_ID_SEQUENCE_PRODUCT_SEQUENCE_VALUE": true } } } From 40b2ebb83674897d81a2e98d309403ca7d5e75f1 Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Fri, 9 Nov 2018 18:15:52 +0400 Subject: [PATCH 231/310] MAGETWO-91633: Grouped Products: Associated Products Can't Be Sorted Between Pages - Updated element locator for test --- .../Mftf/Section/AdminProductFormGroupedProductsSection.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml index f5549f26bfd56..547c856d144c8 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminProductFormGroupedProductsSection.xml @@ -11,9 +11,9 @@ <section name="AdminProductFormGroupedProductsSection"> <element name="toggleGroupedProduct" type="button" selector="div[data-index=grouped] .admin__collapsible-title"/> <element name="addProductsToGroup" type="button" selector="button[data-index='grouped_products_button']" timeout="30"/> - <element name="nextActionButton" type="button" selector=".admin__field > .admin__field-control > .admin__control-table-pagination > .admin__data-grid-pager > .action-next"/> - <element name="previousActionButton" type="button" selector=".admin__field > .admin__field-control > .admin__control-table-pagination > .admin__data-grid-pager > .action-previous"/> + <element name="nextActionButton" type="button" selector="//*[@data-index='grouped']//*[@class='action-next']"/> + <element name="previousActionButton" type="button" selector="//*[@data-index='grouped']//*[@class='action-previous']"/> <element name="positionProduct" type="input" selector="//tbody/tr[{{arg}}][contains(@class,'data-row')]/td[10]//input[@class='position-widget-input']" parameterized="true"/> <element name="nameProductFromGrid" type="text" selector="//tbody/tr[{{arg}}][contains(@class,'data-row')]/td[4]//*[@class='admin__field-control']//span" parameterized="true"/> </section> -</sections> \ No newline at end of file +</sections> From e9f2b802a6127811867e193e3b792f7f8a0d1051 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Fri, 9 Nov 2018 09:44:47 -0600 Subject: [PATCH 232/310] MAGETWO-96231: All pages in magento are broken on Nginx (MAGE_DIRS errors) - Added check for array existence --- pub/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pub/index.php b/pub/index.php index 90b4778265447..612e190719053 100644 --- a/pub/index.php +++ b/pub/index.php @@ -26,7 +26,7 @@ $params = $_SERVER; $params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] = array_replace_recursive( - $params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS], + $params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] ?? [], [ DirectoryList::PUB => [DirectoryList::URL_PATH => ''], DirectoryList::MEDIA => [DirectoryList::URL_PATH => 'media'], From 6c8dfe2c4f2556a2c182f9042fab93ff0d5cc75d Mon Sep 17 00:00:00 2001 From: Deepty Thampy <dthampy@adobe.com> Date: Fri, 9 Nov 2018 11:06:40 -0600 Subject: [PATCH 233/310] MAGETWO-89232: Validation error appears if duplicate product twice - CR comment --- .../Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml index 8dffc3d352a7b..6658ad36d7150 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml @@ -43,7 +43,7 @@ <test name="AdminCreateProductDuplicateProductTest"> <annotations> <features value="Catalog"/> - <stories value="Errors"/> + <stories value="Validation Errors"/> <title value="No validation errors when trying to duplicate product twice"/> <description value="No validation errors when trying to duplicate product twice"/> <severity value="MAJOR"/> From 15dc79bea4a604a22a68e3a14fbd643336b3a400 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi <mankivsk@adobe.com> Date: Fri, 9 Nov 2018 16:04:29 -0600 Subject: [PATCH 234/310] ENGCOM-3421: Magento 2.3 Fix Notice and Exception while adding image to product programmatically #18952 - Fixed docblocks --- .../Magento/Catalog/Model/Product/Gallery/Processor.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php index 9cd5073f4357c..0912324745360 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php @@ -88,6 +88,8 @@ public function __construct( } /** + * Return media_gallery attribute + * * @return \Magento\Catalog\Api\Data\ProductAttributeInterface * @since 101.0.0 */ @@ -383,7 +385,8 @@ public function setMediaAttribute(\Magento\Catalog\Model\Product $product, $medi } /** - * get media attribute codes + * Get media attribute codes + * * @return array * @since 101.0.0 */ @@ -393,6 +396,8 @@ public function getMediaAttributeCodes() } /** + * Trim .tmp ending from filename + * * @param string $file * @return string * @since 101.0.0 @@ -514,7 +519,6 @@ public function getAffectedFields($object) /** * Attribute value is not to be saved in a conventional way, separate table is used to store the complex value * - * {@inheritdoc} * @since 101.0.0 */ public function isScalar() From e827b3220c12cc8e793e0486cd9425e4ca4c2853 Mon Sep 17 00:00:00 2001 From: Vova Yatsyuk <vova.yatsyuk@gmail.com> Date: Sat, 10 Nov 2018 11:43:05 +0200 Subject: [PATCH 235/310] Allow to read HTTP/2 response header. The issue is HTTP/2 sends the following headers `HTTP/2 200`, `HTTP/2 401` and so on. It doesn't have third parameter like HTTP/1.1 has: `HTTP/2 200 OK` --- lib/internal/Magento/Framework/HTTP/Client/Curl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/HTTP/Client/Curl.php b/lib/internal/Magento/Framework/HTTP/Client/Curl.php index d1dfafdeb2adb..0ac65a420ddcf 100644 --- a/lib/internal/Magento/Framework/HTTP/Client/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Client/Curl.php @@ -438,7 +438,7 @@ protected function parseHeaders($ch, $data) { if ($this->_headerCount == 0) { $line = explode(" ", trim($data), 3); - if (count($line) != 3) { + if (count($line) < 2) { $this->doError("Invalid response line returned from server: " . $data); } $this->_responseStatus = (int)$line[1]; From 57fcc1b24c4c582a96930ff7465e5f74417f2bcc Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi <v.podorozhnyi@ism-ukraine.com> Date: Sat, 10 Nov 2018 11:46:07 +0200 Subject: [PATCH 236/310] =?UTF-8?q?magento/magento2#17833:=C2=A0=20Child?= =?UTF-8?q?=20theme=20does=20not=20inherit=20translations=20from=20parent?= =?UTF-8?q?=20theme=20-=20forward=20port=20unit=20tests=20from=202.2-devel?= =?UTF-8?q?op=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Framework/Test/Unit/TranslateTest.php | 371 ++++++++++++++++++ lib/internal/Magento/Framework/Translate.php | 4 +- 2 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 lib/internal/Magento/Framework/Test/Unit/TranslateTest.php diff --git a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php new file mode 100644 index 0000000000000..6a6407c97a95f --- /dev/null +++ b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php @@ -0,0 +1,371 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Test\Unit; + +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\Translate; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class TranslateTest extends \PHPUnit\Framework\TestCase +{ + /** @var Translate */ + protected $translate; + + /** @var \Magento\Framework\View\DesignInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $viewDesign; + + /** @var \Magento\Framework\Cache\FrontendInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $cache; + + /** @var \Magento\Framework\View\FileSystem|\PHPUnit_Framework_MockObject_MockObject */ + protected $viewFileSystem; + + /** @var \Magento\Framework\Module\ModuleList|\PHPUnit_Framework_MockObject_MockObject */ + protected $moduleList; + + /** @var \Magento\Framework\Module\Dir\Reader|\PHPUnit_Framework_MockObject_MockObject */ + protected $modulesReader; + + /** @var \Magento\Framework\App\ScopeResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $scopeResolver; + + /** @var \Magento\Framework\Translate\ResourceInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $resource; + + /** @var \Magento\Framework\Locale\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $locale; + + /** @var \Magento\Framework\App\State|\PHPUnit_Framework_MockObject_MockObject */ + protected $appState; + + /** @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject */ + protected $filesystem; + + /** @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $request; + + /** @var \Magento\Framework\File\Csv|\PHPUnit_Framework_MockObject_MockObject */ + protected $csvParser; + + /** @var \Magento\Framework\App\Language\Dictionary|\PHPUnit_Framework_MockObject_MockObject */ + protected $packDictionary; + + /** @var \Magento\Framework\Filesystem\Directory\ReadInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $directory; + + /** @var \Magento\Framework\Filesystem\DriverInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $fileDriver; + + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->viewDesign = $this->createMock(\Magento\Framework\View\DesignInterface::class); + $this->cache = $this->createMock(\Magento\Framework\Cache\FrontendInterface::class); + $this->viewFileSystem = $this->createMock(\Magento\Framework\View\FileSystem::class); + $this->moduleList = $this->createMock(\Magento\Framework\Module\ModuleList::class); + $this->modulesReader = $this->createMock(\Magento\Framework\Module\Dir\Reader::class); + $this->scopeResolver = $this->createMock(\Magento\Framework\App\ScopeResolverInterface::class); + $this->resource = $this->createMock(\Magento\Framework\Translate\ResourceInterface::class); + $this->locale = $this->createMock(\Magento\Framework\Locale\ResolverInterface::class); + $this->appState = $this->createMock(\Magento\Framework\App\State::class); + $this->request = $this->getMockForAbstractClass( + \Magento\Framework\App\RequestInterface::class, + [], + '', + false, + false, + true, + ['getParam', 'getControllerModule'] + ); + $this->csvParser = $this->createMock(\Magento\Framework\File\Csv::class); + $this->packDictionary = $this->createMock(\Magento\Framework\App\Language\Dictionary::class); + $this->directory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class); + $filesystem = $this->createMock(\Magento\Framework\Filesystem::class); + $filesystem->expects($this->once())->method('getDirectoryRead')->will($this->returnValue($this->directory)); + $this->fileDriver = $this->createMock(\Magento\Framework\Filesystem\DriverInterface::class); + + $this->translate = new Translate( + $this->viewDesign, + $this->cache, + $this->viewFileSystem, + $this->moduleList, + $this->modulesReader, + $this->scopeResolver, + $this->resource, + $this->locale, + $this->appState, + $filesystem, + $this->request, + $this->csvParser, + $this->packDictionary, + $this->fileDriver + ); + + $serializerMock = $this->createMock(SerializerInterface::class); + $serializerMock->method('serialize') + ->willReturnCallback(function ($data) { + return json_encode($data); + }); + $serializerMock->method('unserialize') + ->willReturnCallback(function ($string) { + return json_decode($string, true); + }); + $objectManager->setBackwardCompatibleProperty( + $this->translate, + 'serializer', + $serializerMock + ); + } + + /** + * @param string $area + * @param bool $forceReload + * @param array $cachedData + * @dataProvider dataProviderLoadDataCachedTranslation + */ + public function testLoadDataCachedTranslation($area, $forceReload, $cachedData) + { + $this->expectsSetConfig('Magento/luma'); + + $this->cache->expects($this->once()) + ->method('load') + ->will($this->returnValue(json_encode($cachedData))); + + $this->appState->expects($this->exactly($area ? 0 : 1)) + ->method('getAreaCode') + ->will($this->returnValue('frontend')); + + $this->translate->loadData($area, $forceReload); + $this->assertEquals($cachedData, $this->translate->getData()); + } + + /** + * @return array + */ + public function dataProviderLoadDataCachedTranslation() + { + $cachedData = ['cached 1' => 'translated 1', 'cached 2' => 'translated 2']; + return [ + ['adminhtml', false, $cachedData], + ['frontend', false, $cachedData], + [null, false, $cachedData], + ]; + } + + /** + * @param string $area + * @param bool $forceReload + * @dataProvider dataProviderForTestLoadData + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function testLoadData($area, $forceReload) + { + $this->expectsSetConfig('Magento/luma'); + + $this->appState->expects($this->exactly($area ? 0 : 1)) + ->method('getAreaCode') + ->will($this->returnValue('frontend')); + + $this->cache->expects($this->exactly($forceReload ? 0 : 1)) + ->method('load') + ->will($this->returnValue(false)); + + $this->directory->expects($this->any())->method('isExist')->will($this->returnValue(true)); + + // _loadModuleTranslation() + $modules = ['some_module', 'other_module', 'another_module', 'current_module']; + $this->request->expects($this->any()) + ->method('getControllerModule') + ->willReturn('current_module'); + $this->moduleList->expects($this->once())->method('getNames')->will($this->returnValue($modules)); + $moduleData = [ + 'module original' => 'module translated', + 'module theme' => 'module-theme original translated', + 'module pack' => 'module-pack original translated', + 'module db' => 'module-db original translated', + ]; + $this->modulesReader->expects($this->any())->method('getModuleDir')->will($this->returnValue('/app/module')); + $themeData = [ + 'theme original' => 'theme translated', + 'module theme' => 'theme translated overwrite', + 'module pack' => 'theme-pack translated overwrite', + 'module db' => 'theme-db translated overwrite', + ]; + $this->csvParser->expects($this->any()) + ->method('getDataPairs') + ->will( + $this->returnValueMap( + [ + ['/app/module/en_US.csv', 0, 1, $moduleData], + ['/app/module/en_GB.csv', 0, 1, $moduleData], + ['/theme.csv', 0, 1, $themeData], + ] + ) + ); + $this->fileDriver->expects($this->any()) + ->method('isExists') + ->will( + $this->returnValueMap( + [ + ['/app/module/en_US.csv', true], + ['/app/module/en_GB.csv', true], + ['/theme.csv', true], + ] + ) + ); + + // _loadPackTranslation + $packData = [ + 'pack original' => 'pack translated', + 'module pack' => 'pack translated overwrite', + 'module db' => 'pack-db translated overwrite', + ]; + $this->packDictionary->expects($this->once())->method('getDictionary')->will($this->returnValue($packData)); + + // _loadThemeTranslation() + $this->viewFileSystem->expects($this->any()) + ->method('getLocaleFileName') + ->will($this->returnValue('/theme.csv')); + + // _loadDbTranslation() + $dbData = [ + 'db original' => 'db translated', + 'module db' => 'db translated overwrite', + ]; + $this->resource->expects($this->any())->method('getTranslationArray')->will($this->returnValue($dbData)); + + $this->cache->expects($this->exactly($forceReload ? 0 : 1))->method('save'); + + $this->translate->loadData($area, $forceReload); + + $expected = [ + 'module original' => 'module translated', + 'module theme' => 'theme translated overwrite', + 'module pack' => 'theme-pack translated overwrite', + 'module db' => 'db translated overwrite', + 'theme original' => 'theme translated', + 'pack original' => 'pack translated', + 'db original' => 'db translated', + ]; + $this->assertEquals($expected, $this->translate->getData()); + } + + /** + * @return array + */ + public function dataProviderForTestLoadData() + { + return [ + ['adminhtml', true], + ['adminhtml', false], + ['frontend', true], + ['frontend', false], + [null, true], + [null, false] + ]; + } + + /** + * @param $data + * @param $result + * @dataProvider dataProviderForTestGetData + */ + public function testGetData($data, $result) + { + $this->cache->expects($this->once()) + ->method('load') + ->will($this->returnValue(json_encode($data))); + $this->expectsSetConfig('themeId'); + $this->translate->loadData('frontend'); + $this->assertEquals($result, $this->translate->getData()); + } + + /** + * @return array + */ + public function dataProviderForTestGetData() + { + $data = ['original 1' => 'translated 1', 'original 2' => 'translated 2']; + return [ + [$data, $data], + [null, []] + ]; + } + + public function testGetLocale() + { + $this->locale->expects($this->once())->method('getLocale')->will($this->returnValue('en_US')); + $this->assertEquals('en_US', $this->translate->getLocale()); + + $this->locale->expects($this->never())->method('getLocale'); + $this->assertEquals('en_US', $this->translate->getLocale()); + + $this->locale->expects($this->never())->method('getLocale'); + $this->translate->setLocale('en_GB'); + $this->assertEquals('en_GB', $this->translate->getLocale()); + } + + public function testSetLocale() + { + $this->translate->setLocale('en_GB'); + $this->locale->expects($this->never())->method('getLocale'); + $this->assertEquals('en_GB', $this->translate->getLocale()); + } + + public function testGetTheme() + { + $this->request->expects($this->at(0))->method('getParam')->with('theme')->will($this->returnValue('')); + + $requestTheme = ['theme_title' => 'Theme Title']; + $this->request->expects($this->at(1))->method('getParam')->with('theme') + ->will($this->returnValue($requestTheme)); + + $this->assertEquals('theme', $this->translate->getTheme()); + $this->assertEquals('themeTheme Title', $this->translate->getTheme()); + } + + public function testLoadDataNoTheme() + { + $forceReload = true; + $this->expectsSetConfig(null, null); + $this->moduleList->expects($this->once())->method('getNames')->will($this->returnValue([])); + $this->appState->expects($this->once())->method('getAreaCode')->will($this->returnValue('frontend')); + $this->packDictionary->expects($this->once())->method('getDictionary')->will($this->returnValue([])); + $this->resource->expects($this->any())->method('getTranslationArray')->will($this->returnValue([])); + $this->assertEquals($this->translate, $this->translate->loadData(null, $forceReload)); + } + + /** + * Declare calls expectation for setConfig() method + */ + protected function expectsSetConfig($themeId, $localeCode = 'en_US') + { + $this->locale->expects($this->any())->method('getLocale')->will($this->returnValue($localeCode)); + $scope = new \Magento\Framework\DataObject(['code' => 'frontendCode', 'id' => 1]); + $scopeAdmin = new \Magento\Framework\DataObject(['code' => 'adminCode', 'id' => 0]); + $this->scopeResolver->expects($this->any()) + ->method('getScope') + ->will( + $this->returnValueMap( + [ + [null, $scope], + ['admin', $scopeAdmin], + ] + ) + ); + $designTheme = $this->getMockBuilder(\Magento\Theme\Model\Theme::class) + ->disableOriginalConstructor() + ->getMock(); + + $designTheme->expects($this->once()) + ->method('getThemePath') + ->willReturn($themeId); + + $this->viewDesign->expects($this->any())->method('getDesignTheme')->will($this->returnValue($designTheme)); + } +} diff --git a/lib/internal/Magento/Framework/Translate.php b/lib/internal/Magento/Framework/Translate.php index ff1bf99162a8a..ce73338eee292 100644 --- a/lib/internal/Magento/Framework/Translate.php +++ b/lib/internal/Magento/Framework/Translate.php @@ -398,11 +398,11 @@ protected function _getModuleTranslationFile($moduleName, $locale) /** * Get theme translation locale file name * - * @param string $locale + * @param string|null $locale * @param array $config * @return string|null */ - private function getThemeTranslationFileName(string $locale, array $config): ?string + private function getThemeTranslationFileName(?string $locale, array $config): ?string { $fileName = $this->_viewFileSystem->getLocaleFileName( 'i18n' . '/' . $locale . '.csv', From fe73cf00549475b0ffc5a470e10ba4f1f3893b78 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi <v.podorozhnyi@ism-ukraine.com> Date: Sat, 10 Nov 2018 11:57:22 +0200 Subject: [PATCH 237/310] =?UTF-8?q?magento/magento2#17833:=C2=A0=20Child?= =?UTF-8?q?=20theme=20does=20not=20inherit=20translations=20from=20parent?= =?UTF-8?q?=20theme=20-=20forward=20port=20unit=20tests=20from=202.2-devel?= =?UTF-8?q?op=20branch=20-=20declare=20strict=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Framework/Test/Unit/TranslateTest.php | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php index 6a6407c97a95f..885be8557fa10 100644 --- a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Test\Unit; use Magento\Framework\Serialize\SerializerInterface; @@ -61,7 +63,7 @@ class TranslateTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Filesystem\DriverInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $fileDriver; - protected function setUp() + protected function setUp(): void { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->viewDesign = $this->createMock(\Magento\Framework\View\DesignInterface::class); @@ -128,7 +130,7 @@ protected function setUp() * @param array $cachedData * @dataProvider dataProviderLoadDataCachedTranslation */ - public function testLoadDataCachedTranslation($area, $forceReload, $cachedData) + public function testLoadDataCachedTranslation($area, $forceReload, $cachedData): void { $this->expectsSetConfig('Magento/luma'); @@ -147,7 +149,7 @@ public function testLoadDataCachedTranslation($area, $forceReload, $cachedData) /** * @return array */ - public function dataProviderLoadDataCachedTranslation() + public function dataProviderLoadDataCachedTranslation(): array { $cachedData = ['cached 1' => 'translated 1', 'cached 2' => 'translated 2']; return [ @@ -163,7 +165,7 @@ public function dataProviderLoadDataCachedTranslation() * @dataProvider dataProviderForTestLoadData * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function testLoadData($area, $forceReload) + public function testLoadData($area, $forceReload): void { $this->expectsSetConfig('Magento/luma'); @@ -258,7 +260,7 @@ public function testLoadData($area, $forceReload) /** * @return array */ - public function dataProviderForTestLoadData() + public function dataProviderForTestLoadData(): array { return [ ['adminhtml', true], @@ -275,7 +277,7 @@ public function dataProviderForTestLoadData() * @param $result * @dataProvider dataProviderForTestGetData */ - public function testGetData($data, $result) + public function testGetData($data, $result): void { $this->cache->expects($this->once()) ->method('load') @@ -288,7 +290,7 @@ public function testGetData($data, $result) /** * @return array */ - public function dataProviderForTestGetData() + public function dataProviderForTestGetData(): array { $data = ['original 1' => 'translated 1', 'original 2' => 'translated 2']; return [ @@ -297,7 +299,7 @@ public function dataProviderForTestGetData() ]; } - public function testGetLocale() + public function testGetLocale(): void { $this->locale->expects($this->once())->method('getLocale')->will($this->returnValue('en_US')); $this->assertEquals('en_US', $this->translate->getLocale()); @@ -310,14 +312,14 @@ public function testGetLocale() $this->assertEquals('en_GB', $this->translate->getLocale()); } - public function testSetLocale() + public function testSetLocale(): void { $this->translate->setLocale('en_GB'); $this->locale->expects($this->never())->method('getLocale'); $this->assertEquals('en_GB', $this->translate->getLocale()); } - public function testGetTheme() + public function testGetTheme(): void { $this->request->expects($this->at(0))->method('getParam')->with('theme')->will($this->returnValue('')); @@ -329,7 +331,7 @@ public function testGetTheme() $this->assertEquals('themeTheme Title', $this->translate->getTheme()); } - public function testLoadDataNoTheme() + public function testLoadDataNoTheme(): void { $forceReload = true; $this->expectsSetConfig(null, null); @@ -343,7 +345,7 @@ public function testLoadDataNoTheme() /** * Declare calls expectation for setConfig() method */ - protected function expectsSetConfig($themeId, $localeCode = 'en_US') + protected function expectsSetConfig($themeId, $localeCode = 'en_US'): void { $this->locale->expects($this->any())->method('getLocale')->will($this->returnValue($localeCode)); $scope = new \Magento\Framework\DataObject(['code' => 'frontendCode', 'id' => 1]); From 284daec007cc9cc5bacc736e3d6ce00ebdf751ef Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 12 Nov 2018 15:00:28 +0200 Subject: [PATCH 238/310] magento/magento2#5929: [Forwardport] Saving Product does not update URL rewrite. --- ...ProductProcessUrlRewriteSavingObserver.php | 21 +++++++++++++++++-- ...uctProcessUrlRewriteSavingObserverTest.php | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index c4d67f447e2cf..6eda8dd0b61ee 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -6,11 +6,15 @@ namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\ObjectManager; use Magento\UrlRewrite\Model\UrlPersistInterface; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\Framework\Event\ObserverInterface; +/** + * Class ProductProcessUrlRewriteSavingObserver + */ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface { /** @@ -23,22 +27,33 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface */ private $urlPersist; + /** + * @var ProductUrlPathGenerator + */ + private $productUrlPathGenerator; + /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator * @param UrlPersistInterface $urlPersist + * @param ProductUrlPathGenerator|null $productUrlPathGenerator */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, - UrlPersistInterface $urlPersist + UrlPersistInterface $urlPersist, + ProductUrlPathGenerator $productUrlPathGenerator = null ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; + $this->productUrlPathGenerator = $productUrlPathGenerator ?: ObjectManager::getInstance() + ->get(ProductUrlPathGenerator::class); } /** * Generate urls for UrlRewrite and save it in storage + * * @param \Magento\Framework\Event\Observer $observer * @return void + * @throws \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException */ public function execute(\Magento\Framework\Event\Observer $observer) { @@ -51,6 +66,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) || $product->dataHasChangedFor('visibility') ) { if ($product->isVisibleInSiteVisibility()) { + $product->unsUrlPath(); + $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php index 9de0588356c43..c72a58197b1fd 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -90,6 +90,7 @@ public function testUrlKeyHasChangedInGlobalContext() $product->setData('save_rewrites_history', true); $product->setUrlKey('new-url'); + $product->setUrlPath('new-path'); $product->save(); $expected = [ @@ -152,6 +153,7 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() $product->setData('save_rewrites_history', true); $product->setUrlKey('new-url'); + $product->setUrlPath('new-path'); $product->save(); $expected = [ @@ -207,6 +209,7 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio $product->setData('save_rewrites_history', false); $product->setUrlKey('new-url'); + $product->setUrlPath('new-path'); $product->save(); $expected = [ From 63922949f0a1a2d018900672968cd8714a901db7 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 12 Nov 2018 15:01:50 +0200 Subject: [PATCH 239/310] GraphQl-28: Implement query complexity limiting --- app/code/Magento/GraphQl/etc/di.xml | 2 +- .../Framework/GraphQl/Query/QueryComplexityLimiter.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index 622888f697b84..914dcc78e49e1 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -100,7 +100,7 @@ <type name="Magento\Framework\GraphQl\Query\QueryComplexityLimiter"> <arguments> <argument name="queryDepth" xsi:type="number">20</argument> - <argument name="queryComplexity" xsi:type="number">160</argument> + <argument name="queryComplexity" xsi:type="number">250</argument> </arguments> </type> </config> diff --git a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php index 3936da21fc56a..5730156ca5b34 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php @@ -38,8 +38,8 @@ class QueryComplexityLimiter * @param int $queryComplexity */ public function __construct( - int $queryDepth = 20, - int $queryComplexity = 160 + int $queryDepth, + int $queryComplexity ) { $this->queryDepth = $queryDepth; $this->queryComplexity = $queryComplexity; From f0dedde4c6fa32c84579928d48d613cf65684eca Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 12 Nov 2018 15:59:02 +0200 Subject: [PATCH 240/310] GraphQl-41: [Query] My Account > My Orders --- .../SalesGraphQl/Model/Resolver/Orders.php | 98 ++++++------ app/code/Magento/SalesGraphQl/composer.json | 3 + .../Magento/GraphQl/Sales/OrdersTest.php | 133 ++++++++-------- .../Sales/_files/orders_with_customer.php | 148 +++++++++--------- 4 files changed, 188 insertions(+), 194 deletions(-) diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php index 05af6829de454..f058ce9ae3bdd 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php @@ -7,70 +7,66 @@ namespace Magento\SalesGraphQl\Model\Resolver; -use Magento\Authorization\Model\UserContextInterface; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccountInterface; +use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; /** - * Class Orders + * Orders data reslover */ class Orders implements ResolverInterface { - /** - * @var CollectionFactoryInterface - */ - private $collectionFactory; + /** + * @var CollectionFactoryInterface + */ + private $collectionFactory; - /** - * @var CheckCustomerAccountInterface - */ - private $checkCustomerAccount; + /** + * @var CheckCustomerAccount + */ + private $checkCustomerAccount; - /** - * Orders constructor. - * @param CollectionFactoryInterface $collectionFactory - * @param CheckCustomerAccountInterface $checkCustomerAccount - */ - public function __construct( - CollectionFactoryInterface $collectionFactory, - CheckCustomerAccountInterface $checkCustomerAccount - ) { - $this->collectionFactory = $collectionFactory; - $this->checkCustomerAccount = $checkCustomerAccount; + /** + * @param CollectionFactoryInterface $collectionFactory + * @param CheckCustomerAccount $checkCustomerAccount + */ + public function __construct( + CollectionFactoryInterface $collectionFactory, + CheckCustomerAccount $checkCustomerAccount + ) { + $this->collectionFactory = $collectionFactory; + $this->checkCustomerAccount = $checkCustomerAccount; - } + } - /** - * {@inheritdoc} - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - $customerId = $context->getUserId(); + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $customerId = $context->getUserId(); + $this->checkCustomerAccount->execute($customerId, $context->getUserType()); - $this->checkCustomerAccount->execute($customerId, $context->getUserType()); + $items = []; + $orders = $this->collectionFactory->create($customerId); - $items = []; - $orders = $this->collectionFactory->create($customerId); - - /** @var \Magento\Sales\Model\Order $order */ - foreach ($orders as $order) { - $items[] = [ - 'id' => $order->getId(), - 'increment_id' => $order->getIncrementId(), - 'created_at' => $order->getCreatedAt(), - 'grand_total' => $order->getGrandTotal(), - 'status' => $order->getStatus() - ]; - } - - return ['items' => $items]; - } + /** @var \Magento\Sales\Model\Order $order */ + foreach ($orders as $order) { + $items[] = [ + 'id' => $order->getId(), + 'increment_id' => $order->getIncrementId(), + 'created_at' => $order->getCreatedAt(), + 'grand_total' => $order->getGrandTotal(), + 'status' => $order->getStatus(), + ]; + } + return ['items' => $items]; + } } diff --git a/app/code/Magento/SalesGraphQl/composer.json b/app/code/Magento/SalesGraphQl/composer.json index b75e18c7b7a97..3e29adbe7009e 100644 --- a/app/code/Magento/SalesGraphQl/composer.json +++ b/app/code/Magento/SalesGraphQl/composer.json @@ -5,8 +5,11 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-authorization": "*", "magento/module-customer": "*", + "magento/module-customer-graph-ql": "*", "magento/module-catalog": "*", + "magento/module-sales": "*", "magento/module-store": "*" }, "suggest": { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php index 33620f6e55386..589b0bc7746f8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php @@ -16,93 +16,88 @@ */ class OrdersTest extends GraphQlAbstract { - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; - /** - * {@inheritdoc} - */ protected function setUp() { parent::setUp(); - $objectManager = Bootstrap::getObjectManager(); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Sales/_files/orders_with_customer.php - */ + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Sales/_files/orders_with_customer.php + */ public function testOrdersQuery() { - $query = - <<<QUERY + $query = + <<<QUERY query { customerOrders { items { - id - increment_id - created_at - grand_total - status - } + id + increment_id + created_at + grand_total + status + } } } QUERY; - $currentEmail = 'customer@example.com'; - $currentPassword = 'password'; - - $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); - $expectedData = [ - [ - 'increment_id' => '100000002', - 'status' => 'processing', - 'grand_total' => 120.00 - ], - [ - 'increment_id' => '100000003', - 'status' => 'processing', - 'grand_total' => 130.00 - ], - [ - 'increment_id' => '100000004', - 'status' => 'closed', - 'grand_total' => 140.00 - ], - [ - 'increment_id' => '100000005', - 'status' => 'complete', - 'grand_total' => 150.00 - ], - [ - 'increment_id' => '100000006', - 'status' => 'complete', - 'grand_total' => 160.00 - ] - ]; + $expectedData = [ + [ + 'increment_id' => '100000002', + 'status' => 'processing', + 'grand_total' => 120.00 + ], + [ + 'increment_id' => '100000003', + 'status' => 'processing', + 'grand_total' => 130.00 + ], + [ + 'increment_id' => '100000004', + 'status' => 'closed', + 'grand_total' => 140.00 + ], + [ + 'increment_id' => '100000005', + 'status' => 'complete', + 'grand_total' => 150.00 + ], + [ + 'increment_id' => '100000006', + 'status' => 'complete', + 'grand_total' => 160.00 + ] + ]; - $actualData = $response['customerOrders']['items']; + $actualData = $response['customerOrders']['items']; - foreach ($expectedData as $key => $data) { - $this->assertEquals($data['increment_id'], $actualData[$key]['increment_id']); - $this->assertEquals($data['grand_total'], $actualData[$key]['grand_total']); - $this->assertEquals($data['status'], $actualData[$key]['status']); - } + foreach ($expectedData as $key => $data) { + $this->assertEquals($data['increment_id'], $actualData[$key]['increment_id']); + $this->assertEquals($data['grand_total'], $actualData[$key]['grand_total']); + $this->assertEquals($data['status'], $actualData[$key]['status']); + } } - /** - * @param string $email - * @param string $password - * @return array - * @throws \Magento\Framework\Exception\AuthenticationException - */ - private function getCustomerAuthHeaders(string $email, string $password): array - { - $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); - return ['Authorization' => 'Bearer ' . $customerToken]; - } + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php index e649396391ca9..6906166f7cbe5 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php @@ -14,88 +14,88 @@ /** @var Order\Item $orderItem */ /** @var array $addressData Data for creating addresses for the orders. */ $orders = [ - [ - 'increment_id' => '100000002', - 'state' => \Magento\Sales\Model\Order::STATE_NEW, - 'status' => 'processing', - 'grand_total' => 120.00, - 'subtotal' => 120.00, - 'base_grand_total' => 120.00, - 'store_id' => 1, - 'website_id' => 1, - 'payment' => $payment - ], - [ - 'increment_id' => '100000003', - 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, - 'status' => 'processing', - 'grand_total' => 130.00, - 'base_grand_total' => 130.00, - 'subtotal' => 130.00, - 'store_id' => 0, - 'website_id' => 0, - 'payment' => $payment - ], - [ - 'increment_id' => '100000004', - 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, - 'status' => 'closed', - 'grand_total' => 140.00, - 'base_grand_total' => 140.00, - 'subtotal' => 140.00, - 'store_id' => 1, - 'website_id' => 1, - 'payment' => $payment - ], - [ - 'increment_id' => '100000005', - 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, - 'status' => 'complete', - 'grand_total' => 150.00, - 'base_grand_total' => 150.00, - 'subtotal' => 150.00, - 'store_id' => 1, - 'website_id' => 1, - 'payment' => $payment - ], - [ - 'increment_id' => '100000006', - 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, - 'status' => 'complete', - 'grand_total' => 160.00, - 'base_grand_total' => 160.00, - 'subtotal' => 160.00, - 'store_id' => 1, - 'website_id' => 1, - 'payment' => $payment - ], + [ + 'increment_id' => '100000002', + 'state' => \Magento\Sales\Model\Order::STATE_NEW, + 'status' => 'processing', + 'grand_total' => 120.00, + 'subtotal' => 120.00, + 'base_grand_total' => 120.00, + 'store_id' => 1, + 'website_id' => 1, + 'payment' => $payment + ], + [ + 'increment_id' => '100000003', + 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, + 'status' => 'processing', + 'grand_total' => 130.00, + 'base_grand_total' => 130.00, + 'subtotal' => 130.00, + 'store_id' => 0, + 'website_id' => 0, + 'payment' => $payment + ], + [ + 'increment_id' => '100000004', + 'state' => \Magento\Sales\Model\Order::STATE_PROCESSING, + 'status' => 'closed', + 'grand_total' => 140.00, + 'base_grand_total' => 140.00, + 'subtotal' => 140.00, + 'store_id' => 1, + 'website_id' => 1, + 'payment' => $payment + ], + [ + 'increment_id' => '100000005', + 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, + 'status' => 'complete', + 'grand_total' => 150.00, + 'base_grand_total' => 150.00, + 'subtotal' => 150.00, + 'store_id' => 1, + 'website_id' => 1, + 'payment' => $payment + ], + [ + 'increment_id' => '100000006', + 'state' => \Magento\Sales\Model\Order::STATE_COMPLETE, + 'status' => 'complete', + 'grand_total' => 160.00, + 'base_grand_total' => 160.00, + 'subtotal' => 160.00, + 'store_id' => 1, + 'website_id' => 1, + 'payment' => $payment + ], ]; /** @var OrderRepositoryInterface $orderRepository */ $orderRepository = $objectManager->create(OrderRepositoryInterface::class); /** @var array $orderData */ foreach ($orders as $orderData) { - /** @var $order \Magento\Sales\Model\Order */ - $order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Sales\Model\Order::class - ); + /** @var $order \Magento\Sales\Model\Order */ + $order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Sales\Model\Order::class + ); - // Reset addresses - /** @var Order\Address $billingAddress */ - $billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); - $billingAddress->setAddressType('billing'); + // Reset addresses + /** @var Order\Address $billingAddress */ + $billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); + $billingAddress->setAddressType('billing'); - $shippingAddress = clone $billingAddress; - $shippingAddress->setId(null)->setAddressType('shipping'); + $shippingAddress = clone $billingAddress; + $shippingAddress->setId(null)->setAddressType('shipping'); - $order - ->setData($orderData) - ->addItem($orderItem) - ->setCustomerIsGuest(false) - ->setCustomerId(1) - ->setCustomerEmail('customer@example.com') - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress); + $order + ->setData($orderData) + ->addItem($orderItem) + ->setCustomerIsGuest(false) + ->setCustomerId(1) + ->setCustomerEmail('customer@example.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress); - $orderRepository->save($order); + $orderRepository->save($order); } From 198fc61326ad0c575997649e649753d946ac3b70 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 12 Nov 2018 16:08:57 +0200 Subject: [PATCH 241/310] GraphQl-41: [Query] My Account > My Orders --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index d4ac4fc091bbc..7fc23572cbb34 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "78153b5c8150c0d145b3372a534a45eb", + "content-hash": "7986e75e7ec3308bfd361f2076544eb7", "packages": [ { "name": "braintree/braintree_php", From a04317746efcaa2aa00c04930a5de428e2753ac1 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 12 Nov 2018 16:11:11 +0200 Subject: [PATCH 242/310] GraphQl-41: [Query] My Account > My Orders --- app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php index f058ce9ae3bdd..5802115d44b5e 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php @@ -38,7 +38,6 @@ public function __construct( ) { $this->collectionFactory = $collectionFactory; $this->checkCustomerAccount = $checkCustomerAccount; - } /** From 34af04fc6f288e217129ed32a82a60a51bdce5b9 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Mon, 12 Nov 2018 17:11:25 +0300 Subject: [PATCH 243/310] MAGETWO-91784: On Payment screen up and down arrow key allow to add -ve numbers - Fixed static test. --- .../view/base/web/js/model/credit-card-validation/validator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/validator.js b/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/validator.js index f9af514a6d4da..c41be40cba144 100644 --- a/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/validator.js +++ b/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/validator.js @@ -23,7 +23,7 @@ }(function ($, cvvValidator, creditCardNumberValidator, yearValidator, monthValidator, creditCardData) { 'use strict'; - $('.payment-method-content input[type="number"]').on('keyup', function() { + $('.payment-method-content input[type="number"]').on('keyup', function () { if ($(this).val() < 0) { $(this).val($(this).val().replace(/^-/, '')); } From 7e5be32f56ac80bde8dbd6d8e55da12c3923dbb4 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Mon, 12 Nov 2018 17:18:10 +0300 Subject: [PATCH 244/310] MAGETWO-62728: My Wishlist - quantity input box issue - Fixed static test. --- .../Wishlist/view/frontend/templates/item/column/cart.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml index c1cccae85971c..7578774fabc2c 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml @@ -22,7 +22,7 @@ $allowedQty = $block->getMinMaxQty(); <div class="field qty"> <label class="label" for="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> - <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true, 'validate-item-quantity':{'minAllowed':<?= $allowedQty['minAllowed'] ?>,'maxAllowed':<?= $allowedQty['maxAllowed'] ?>}}" + <input type="number" data-role="qty" id="qty[<?= /* @noEscape */ $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true, 'validate-item-quantity':{'minAllowed':<?= $allowedQty['minAllowed'] ?>,'maxAllowed':<?= $allowedQty['maxAllowed'] ?>}}" name="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" value="<?= /* @noEscape */ (int)($block->getAddToCartQty($item) * 1) ?>"> </div> </div> From abc43aca2b420e0b4baebab6c414e2f20a63a3c1 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 12 Nov 2018 16:49:40 +0200 Subject: [PATCH 245/310] GraphQl-41: [Query] My Account > My Orders --- app/code/Magento/SalesGraphQl/composer.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/SalesGraphQl/composer.json b/app/code/Magento/SalesGraphQl/composer.json index 3e29adbe7009e..0549d31d59a24 100644 --- a/app/code/Magento/SalesGraphQl/composer.json +++ b/app/code/Magento/SalesGraphQl/composer.json @@ -5,12 +5,8 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-authorization": "*", - "magento/module-customer": "*", "magento/module-customer-graph-ql": "*", - "magento/module-catalog": "*", - "magento/module-sales": "*", - "magento/module-store": "*" + "magento/module-sales": "*" }, "suggest": { "magento/module-graph-ql": "*" From 0aad3fb3eeea271eed53f923d0464147666135a5 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 12 Nov 2018 17:02:06 +0200 Subject: [PATCH 246/310] GraphQl-41: [Query] My Account > My Orders --- .../testsuite/Magento/Sales/_files/orders_with_customer.php | 1 + .../Magento/Sales/_files/orders_with_customer_rollback.php | 1 + 2 files changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php index 6906166f7cbe5..753adb1f38596 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); use Magento\Sales\Model\Order; use Magento\Sales\Api\OrderRepositoryInterface; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer_rollback.php index 6e24cee501f51..1fb4b4636ab29 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/orders_with_customer_rollback.php @@ -3,5 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); require 'default_rollback.php'; From 0566957554fa964f79518b7b93920fe12db39c8a Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 12 Nov 2018 17:03:30 +0200 Subject: [PATCH 247/310] magento/magento2#18939: [Forwardport] fixed js translation. --- app/code/Magento/Tax/i18n/en_US.csv | 1 + .../Tax/view/frontend/web/js/view/checkout/summary/tax.js | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Tax/i18n/en_US.csv b/app/code/Magento/Tax/i18n/en_US.csv index e6d89deb7696c..836221e2ec974 100644 --- a/app/code/Magento/Tax/i18n/en_US.csv +++ b/app/code/Magento/Tax/i18n/en_US.csv @@ -178,3 +178,4 @@ Rate,Rate "Your credit card will be charged for","Your credit card will be charged for" "An error occurred while loading tax rates.","An error occurred while loading tax rates." "You will be charged for","You will be charged for" +"Not yet calculated", "Not yet calculated" diff --git a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js index c86c3b4d1ab06..b21be98531ba9 100644 --- a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js +++ b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js @@ -11,8 +11,9 @@ define([ 'ko', 'Magento_Checkout/js/view/summary/abstract-total', 'Magento_Checkout/js/model/quote', - 'Magento_Checkout/js/model/totals' -], function (ko, Component, quote, totals) { + 'Magento_Checkout/js/model/totals', + 'mage/translate' +], function (ko, Component, quote, totals, $t) { 'use strict'; var isTaxDisplayedInGrandTotal = window.checkoutConfig.includeTaxInGrandTotal, @@ -22,7 +23,7 @@ define([ return Component.extend({ defaults: { isTaxDisplayedInGrandTotal: isTaxDisplayedInGrandTotal, - notCalculatedMessage: 'Not yet calculated', + notCalculatedMessage: $t('Not yet calculated'), template: 'Magento_Tax/checkout/summary/tax' }, totals: quote.getTotals(), From 84a3cd8f9c7aa58fccf8f71e527e26295529268a Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 12 Nov 2018 18:55:56 +0200 Subject: [PATCH 248/310] Covering the CheckUserLoginBackendObserver by Unit Test --- .../CheckUserLoginBackendObserverTest.php | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php new file mode 100644 index 0000000000000..b7b11a56fd5ce --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php @@ -0,0 +1,174 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Captcha\Test\Unit\Observer; + +use Magento\Captcha\Helper\Data; +use Magento\Captcha\Model\DefaultModel; +use Magento\Captcha\Observer\CaptchaStringResolver; +use Magento\Captcha\Observer\CheckUserLoginBackendObserver; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\Exception\Plugin\AuthenticationException; +use Magento\Framework\Message\ManagerInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class CheckUserLoginBackendObserverTest + */ +class CheckUserLoginBackendObserverTest extends TestCase +{ + /** + * @var CheckUserLoginBackendObserver + */ + private $observer; + + /** + * @var ManagerInterface|MockObject + */ + private $messageManagerMock; + + /** + * @var CaptchaStringResolver|MockObject + */ + private $captchaStringResolverMock; + + /** + * @var RequestInterface|MockObject + */ + private $requestMock; + + /** + * @var Data|MockObject + */ + private $helperMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->helperMock = $this->createMock(Data::class); + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + $this->captchaStringResolverMock = $this->createMock(CaptchaStringResolver::class); + $this->requestMock = $this->createMock(RequestInterface::class); + + $this->observer = new CheckUserLoginBackendObserver( + $this->helperMock, + $this->captchaStringResolverMock, + $this->requestMock + ); + } + + /** + * Test check user login in backend with correct captcha + * + * @dataProvider captchaCorrectnessCheckDataProvider + * @param bool $isRequired + * @param bool $isCorrect + * @param int $invokedTimes + * @return void + * @throws AuthenticationException + */ + public function testCheckOnBackendLoginWithCorrectCaptcha($isRequired, $isCorrect, $invokedTimes) + { + $formId = 'backend_login'; + $login = 'admin'; + $captchaValue = 'captcha-value'; + + $observerMock = $this->createPartialMock(Observer::class, ['getEvent']); + $eventMock = $this->createPartialMock(Event::class, ['getUsername']); + $captcha = $this->createMock(DefaultModel::class); + + $eventMock->expects($this->any()) + ->method('getUsername') + ->willReturn('admin'); + $observerMock->expects($this->any()) + ->method('getEvent') + ->willReturn($eventMock); + $captcha->expects($this->once())->method('isRequired') + ->with($login) + ->willReturn($isRequired); + $captcha->expects($this->exactly($invokedTimes)) + ->method('isCorrect') + ->with($captchaValue) + ->willReturn($isCorrect); + $this->helperMock->expects($this->once()) + ->method('getCaptcha') + ->with($formId) + ->willReturn($captcha); + + $this->captchaStringResolverMock->expects($this->exactly($invokedTimes)) + ->method('resolve') + ->with($this->requestMock, $formId) + ->willReturn($captchaValue); + + $this->messageManagerMock->expects($this->exactly(0)) + ->method('addError') + ->with(__('Incorrect CAPTCHA')); + + $this->observer->execute($observerMock); + } + + /** + * @return array + */ + public function captchaCorrectnessCheckDataProvider() + { + return [ + [true, true, 1], + [false, true, 0] + ]; + } + + + /** + * Test check user login in backend with wrong captcha + * + * @return void + * @throws AuthenticationException + */ + public function testCheckOnBackendLoginWithWrongCaptcha() + { + $formId = 'backend_login'; + $login = 'admin'; + $captchaValue = 'captcha-value'; + + $observerMock = $this->createPartialMock(Observer::class, ['getEvent']); + $eventMock = $this->createPartialMock(Event::class, ['getUsername']); + $captcha = $this->createMock(DefaultModel::class); + + $eventMock->expects($this->any()) + ->method('getUsername') + ->willReturn('admin'); + $observerMock->expects($this->any()) + ->method('getEvent') + ->willReturn($eventMock); + $captcha->expects($this->once())->method('isRequired') + ->with($login) + ->willReturn(true); + $captcha->expects($this->exactly(1)) + ->method('isCorrect') + ->with($captchaValue) + ->willReturn(false); + $this->helperMock->expects($this->once()) + ->method('getCaptcha') + ->with($formId) + ->willReturn($captcha); + + $this->captchaStringResolverMock->expects($this->exactly(1)) + ->method('resolve') + ->with($this->requestMock, $formId) + ->willReturn($captchaValue); + + $this->expectException(AuthenticationException::class, 'Incorrect CAPTCHA.'); + + $this->observer->execute($observerMock); + } +} From 246eb673397e7358bc1ee295bec59ef66b6cfba2 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 12 Nov 2018 19:02:12 +0200 Subject: [PATCH 249/310] Adding strict types --- .../Observer/CheckUserLoginBackendObserverTest.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php index b7b11a56fd5ce..02f6bed520acd 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Captcha\Test\Unit\Observer; use Magento\Captcha\Helper\Data; @@ -76,12 +78,13 @@ protected function setUp() * @return void * @throws AuthenticationException */ - public function testCheckOnBackendLoginWithCorrectCaptcha($isRequired, $isCorrect, $invokedTimes) + public function testCheckOnBackendLoginWithCorrectCaptcha(bool $isRequired, bool $isCorrect, int $invokedTimes): void { $formId = 'backend_login'; $login = 'admin'; $captchaValue = 'captcha-value'; + /** @var Observer|MockObject $observerMock */ $observerMock = $this->createPartialMock(Observer::class, ['getEvent']); $eventMock = $this->createPartialMock(Event::class, ['getUsername']); $captcha = $this->createMock(DefaultModel::class); @@ -119,7 +122,7 @@ public function testCheckOnBackendLoginWithCorrectCaptcha($isRequired, $isCorrec /** * @return array */ - public function captchaCorrectnessCheckDataProvider() + public function captchaCorrectnessCheckDataProvider(): array { return [ [true, true, 1], @@ -134,12 +137,13 @@ public function captchaCorrectnessCheckDataProvider() * @return void * @throws AuthenticationException */ - public function testCheckOnBackendLoginWithWrongCaptcha() + public function testCheckOnBackendLoginWithWrongCaptcha(): void { $formId = 'backend_login'; $login = 'admin'; $captchaValue = 'captcha-value'; + /** @var Observer|MockObject $observerMock */ $observerMock = $this->createPartialMock(Observer::class, ['getEvent']); $eventMock = $this->createPartialMock(Event::class, ['getUsername']); $captcha = $this->createMock(DefaultModel::class); @@ -167,7 +171,7 @@ public function testCheckOnBackendLoginWithWrongCaptcha() ->with($this->requestMock, $formId) ->willReturn($captchaValue); - $this->expectException(AuthenticationException::class, 'Incorrect CAPTCHA.'); + $this->expectException(AuthenticationException::class); $this->observer->execute($observerMock); } From 548ca3c97565e99ebe8bcfa42e1f421de23c4898 Mon Sep 17 00:00:00 2001 From: Alexandr Voronoy <servermed@gmail.com> Date: Mon, 12 Nov 2018 19:11:54 +0200 Subject: [PATCH 250/310] Added option_id in response for product with customizable options. --- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 1 + .../testsuite/Magento/GraphQl/Catalog/ProductViewTest.php | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 5efe37d8f91f6..5c15d1f4a07e7 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -358,6 +358,7 @@ interface CustomizableOptionInterface @typeResolver(class: "Magento\\CatalogGrap title: String @doc(description: "The display name for this option") required: Boolean @doc(description: "Indicates whether the option is required") sort_order: Int @doc(description: "The order in which the option is displayed") + option_id: Int @doc(description: "Option ID") } interface CustomizableProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "CustomizableProductInterface contains information about customizable product options.") { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index 791696b2fa29e..77384165db97f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -91,6 +91,7 @@ public function testQueryAllFieldsSimpleProduct() title required sort_order + option_id ... on CustomizableFieldOption { product_sku field_option: value { @@ -337,6 +338,7 @@ public function testQueryMediaGalleryEntryFieldsSimpleProduct() title required sort_order + option_id ... on CustomizableFieldOption { product_sku field_option: value { @@ -753,7 +755,8 @@ private function assertOptions($product, $actualResponse) $assertionMap = [ ['response_field' => 'sort_order', 'expected_value' => $option->getSortOrder()], ['response_field' => 'title', 'expected_value' => $option->getTitle()], - ['response_field' => 'required', 'expected_value' => $option->getIsRequire()] + ['response_field' => 'required', 'expected_value' => $option->getIsRequire()], + ['response_field' => 'option_id', 'expected_value' => $option->getOptionId()] ]; if (!empty($option->getValues())) { @@ -777,7 +780,7 @@ private function assertOptions($product, $actualResponse) ['response_field' => 'product_sku', 'expected_value' => $option->getProductSku()], ] ); - $valueKeyName = ""; + if ($option->getType() === 'file') { $valueKeyName = 'file_option'; $valueAssertionMap = [ From 815f013d2a04212f69ae84871eb2d22ea60efcad Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi <mankivsk@adobe.com> Date: Mon, 12 Nov 2018 16:17:38 -0600 Subject: [PATCH 251/310] #2331: Fixing static tests --- app/code/Magento/Customer/Controller/Section/Load.php | 2 +- app/code/Magento/Customer/CustomerData/Section/Identifier.php | 2 +- app/code/Magento/Customer/CustomerData/SectionPool.php | 2 +- .../Magento/Customer/Controller/Section/LoadTest.php | 4 +++- lib/internal/Magento/Framework/Test/Unit/TranslateTest.php | 1 + 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Section/Load.php b/app/code/Magento/Customer/Controller/Section/Load.php index 89d4417c79c19..37cd071b13623 100644 --- a/app/code/Magento/Customer/Controller/Section/Load.php +++ b/app/code/Magento/Customer/Controller/Section/Load.php @@ -59,7 +59,7 @@ public function __construct( } /** - * @return \Magento\Framework\Controller\Result\Json + * @inheritdoc */ public function execute() { diff --git a/app/code/Magento/Customer/CustomerData/Section/Identifier.php b/app/code/Magento/Customer/CustomerData/Section/Identifier.php index 54d7cee2d90bd..a8bc2c8abc11a 100644 --- a/app/code/Magento/Customer/CustomerData/Section/Identifier.php +++ b/app/code/Magento/Customer/CustomerData/Section/Identifier.php @@ -67,7 +67,7 @@ public function initMark($forceNewTimestamp) * Mark sections with data id * * @param array $sectionsData - * @param null $sectionNames + * @param array|null $sectionNames * @param bool $forceNewTimestamp * @return array */ diff --git a/app/code/Magento/Customer/CustomerData/SectionPool.php b/app/code/Magento/Customer/CustomerData/SectionPool.php index 618d52079973d..efea1762d9de6 100644 --- a/app/code/Magento/Customer/CustomerData/SectionPool.php +++ b/app/code/Magento/Customer/CustomerData/SectionPool.php @@ -53,7 +53,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getSectionsData(array $sectionNames = null, $forceNewTimestamp = false) { diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php index 3563087d3722b..3db22b8379850 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php @@ -13,7 +13,9 @@ public function testLoadInvalidSection() $expected = [ 'message' => 'The "section<invalid" section source isn't supported.', ]; - $this->dispatch('/customer/section/load/?sections=section<invalid&force_new_section_timestamp=false&_=147066166394'); + $this->dispatch( + '/customer/section/load/?sections=section<invalid&force_new_section_timestamp=false&_=147066166394' + ); self::assertEquals(json_encode($expected), $this->getResponse()->getBody()); } } diff --git a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php index 885be8557fa10..0ec27d6d053c3 100644 --- a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php @@ -11,6 +11,7 @@ use Magento\Framework\Translate; /** + * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TranslateTest extends \PHPUnit\Framework\TestCase From 993ba971580958cdcdb779c80c193bd86183776c Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 13 Nov 2018 09:47:20 +0200 Subject: [PATCH 252/310] Adjusting the Unit Test strictness --- .../CheckUserLoginBackendObserverTest.php | 76 +++++-------------- 1 file changed, 18 insertions(+), 58 deletions(-) diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php index 02f6bed520acd..2d0a2a7e1c99a 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php @@ -14,7 +14,6 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Event; use Magento\Framework\Event\Observer; -use Magento\Framework\Exception\Plugin\AuthenticationException; use Magento\Framework\Message\ManagerInterface; use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject as MockObject; @@ -71,14 +70,11 @@ protected function setUp() /** * Test check user login in backend with correct captcha * - * @dataProvider captchaCorrectnessCheckDataProvider + * @dataProvider requiredCaptchaDataProvider * @param bool $isRequired - * @param bool $isCorrect - * @param int $invokedTimes * @return void - * @throws AuthenticationException */ - public function testCheckOnBackendLoginWithCorrectCaptcha(bool $isRequired, bool $isCorrect, int $invokedTimes): void + public function testCheckOnBackendLoginWithCorrectCaptcha(bool $isRequired): void { $formId = 'backend_login'; $login = 'admin'; @@ -89,44 +85,25 @@ public function testCheckOnBackendLoginWithCorrectCaptcha(bool $isRequired, bool $eventMock = $this->createPartialMock(Event::class, ['getUsername']); $captcha = $this->createMock(DefaultModel::class); - $eventMock->expects($this->any()) - ->method('getUsername') - ->willReturn('admin'); - $observerMock->expects($this->any()) - ->method('getEvent') - ->willReturn($eventMock); - $captcha->expects($this->once())->method('isRequired') - ->with($login) - ->willReturn($isRequired); - $captcha->expects($this->exactly($invokedTimes)) - ->method('isCorrect') - ->with($captchaValue) - ->willReturn($isCorrect); - $this->helperMock->expects($this->once()) - ->method('getCaptcha') - ->with($formId) - ->willReturn($captcha); - - $this->captchaStringResolverMock->expects($this->exactly($invokedTimes)) - ->method('resolve') - ->with($this->requestMock, $formId) + $eventMock->method('getUsername')->willReturn('admin'); + $observerMock->method('getEvent')->willReturn($eventMock); + $captcha->method('isRequired')->with($login)->willReturn($isRequired); + $captcha->method('isCorrect')->with($captchaValue)->willReturn(true); + $this->helperMock->method('getCaptcha')->with($formId)->willReturn($captcha); + $this->captchaStringResolverMock->method('resolve')->with($this->requestMock, $formId) ->willReturn($captchaValue); - $this->messageManagerMock->expects($this->exactly(0)) - ->method('addError') - ->with(__('Incorrect CAPTCHA')); - $this->observer->execute($observerMock); } /** * @return array */ - public function captchaCorrectnessCheckDataProvider(): array + public function requiredCaptchaDataProvider(): array { return [ - [true, true, 1], - [false, true, 0] + [true], + [false] ]; } @@ -135,7 +112,7 @@ public function captchaCorrectnessCheckDataProvider(): array * Test check user login in backend with wrong captcha * * @return void - * @throws AuthenticationException + * @expectedException \Magento\Framework\Exception\Plugin\AuthenticationException */ public function testCheckOnBackendLoginWithWrongCaptcha(): void { @@ -148,31 +125,14 @@ public function testCheckOnBackendLoginWithWrongCaptcha(): void $eventMock = $this->createPartialMock(Event::class, ['getUsername']); $captcha = $this->createMock(DefaultModel::class); - $eventMock->expects($this->any()) - ->method('getUsername') - ->willReturn('admin'); - $observerMock->expects($this->any()) - ->method('getEvent') - ->willReturn($eventMock); - $captcha->expects($this->once())->method('isRequired') - ->with($login) - ->willReturn(true); - $captcha->expects($this->exactly(1)) - ->method('isCorrect') - ->with($captchaValue) - ->willReturn(false); - $this->helperMock->expects($this->once()) - ->method('getCaptcha') - ->with($formId) - ->willReturn($captcha); - - $this->captchaStringResolverMock->expects($this->exactly(1)) - ->method('resolve') - ->with($this->requestMock, $formId) + $eventMock->method('getUsername')->willReturn($login); + $observerMock->method('getEvent')->willReturn($eventMock); + $captcha->method('isRequired')->with($login)->willReturn(true); + $captcha->method('isCorrect')->with($captchaValue)->willReturn(false); + $this->helperMock->method('getCaptcha')->with($formId)->willReturn($captcha); + $this->captchaStringResolverMock->method('resolve')->with($this->requestMock, $formId) ->willReturn($captchaValue); - $this->expectException(AuthenticationException::class); - $this->observer->execute($observerMock); } } From 8f463db4808afc9f8f4908a4dee65f3abe981eba Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Mon, 22 Oct 2018 18:23:19 +0530 Subject: [PATCH 253/310] Added tier price logic for special price --- .../Model/Product/Attribute/Backend/Tierprice.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php index 92b9a2e4239b2..55b5c6c925b05 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -159,8 +159,22 @@ protected function validatePrice(array $priceRow) */ protected function modifyPriceData($object, $data) { + /** @var \Magento\Catalog\Model\Product $object */ $data = parent::modifyPriceData($object, $data); $price = $object->getPrice(); + + $specialPrice = $object->getSpecialPrice(); + $specialPriceFromDate = $object->getSpecialFromDate(); + $specialPriceToDate = $object->getSpecialToDate(); + $today = time(); + + if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())){ + if ($today >= strtotime($specialPriceFromDate) && $today <= strtotime($specialPriceToDate) || + $today >= strtotime($specialPriceFromDate) && is_null($specialPriceToDate)) { + $price = $specialPrice; + } + } + foreach ($data as $key => $tierPrice) { $percentageValue = $this->getPercentage($tierPrice); if ($percentageValue) { From 63c9ea46673e58f2712da5a768d0840ece184152 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Tue, 23 Oct 2018 08:19:55 +0530 Subject: [PATCH 254/310] Fixed code standard error --- .../Catalog/Model/Product/Attribute/Backend/Tierprice.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php index 55b5c6c925b05..88d8198d0249e 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -168,9 +168,9 @@ protected function modifyPriceData($object, $data) $specialPriceToDate = $object->getSpecialToDate(); $today = time(); - if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())){ + if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())) { if ($today >= strtotime($specialPriceFromDate) && $today <= strtotime($specialPriceToDate) || - $today >= strtotime($specialPriceFromDate) && is_null($specialPriceToDate)) { + $today >= strtotime($specialPriceFromDate) && is_null($specialPriceToDate) === TRUE) { $price = $specialPrice; } } From 77492c1c08695753b3a274b02552fab7be9c64c0 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Tue, 23 Oct 2018 16:27:07 +0530 Subject: [PATCH 255/310] Update Tierprice.php --- .../Catalog/Model/Product/Attribute/Backend/Tierprice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php index 88d8198d0249e..b74be1372a3c0 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -170,7 +170,7 @@ protected function modifyPriceData($object, $data) if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())) { if ($today >= strtotime($specialPriceFromDate) && $today <= strtotime($specialPriceToDate) || - $today >= strtotime($specialPriceFromDate) && is_null($specialPriceToDate) === TRUE) { + $today >= strtotime($specialPriceFromDate) && is_null($specialPriceToDate)) { $price = $specialPrice; } } From d0ba9973dffac27560364906b22786d70410d69d Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Thu, 1 Nov 2018 17:59:10 +0530 Subject: [PATCH 256/310] Update Tierprice.php --- .../Catalog/Model/Product/Attribute/Backend/Tierprice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php index b74be1372a3c0..23b2dfa01bfbd 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -170,7 +170,7 @@ protected function modifyPriceData($object, $data) if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())) { if ($today >= strtotime($specialPriceFromDate) && $today <= strtotime($specialPriceToDate) || - $today >= strtotime($specialPriceFromDate) && is_null($specialPriceToDate)) { + $today >= strtotime($specialPriceFromDate) && $specialPriceToDate === null) { $price = $specialPrice; } } From b1bf89323d5bd6a84f45dd3c7788b2c60312b345 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 13 Nov 2018 10:23:41 +0200 Subject: [PATCH 257/310] MAGETWO-96118: Few optimizations on category & product pages --- .../Model/Product/Gallery/ReadHandler.php | 41 +++------------- .../Model/ResourceModel/Product/Gallery.php | 13 +++-- .../ResourceModel/Product/GalleryTest.php | 48 +++++++++++++++---- app/code/Magento/Catalog/etc/db_schema.xml | 5 ++ .../Catalog/etc/db_schema_whitelist.json | 5 +- .../Model/Product/Type/Configurable.php | 44 +++++++++++++---- .../Product/Type/Configurable.php | 11 +++-- .../Pricing/Renderer/SalableResolver.php | 4 +- .../Plugin/ExternalVideoResourceBackend.php | 40 ++++++++-------- 9 files changed, 127 insertions(+), 84 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php index c785d08e64b7f..4ad275bc70f90 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php @@ -55,9 +55,6 @@ public function __construct( */ public function execute($entity, $arguments = []) { - $value = []; - $value['images'] = []; - $mediaEntries = $this->resourceModel->loadProductGalleryByAttributeId( $entity, $this->getAttribute()->getAttributeId() @@ -79,37 +76,13 @@ public function execute($entity, $arguments = []) */ public function addMediaDataToProduct(Product $product, array $mediaEntries) { - $attrCode = $this->getAttribute()->getAttributeCode(); - $value = []; - $value['images'] = []; - $value['values'] = []; - - foreach ($mediaEntries as $mediaEntry) { - $mediaEntry = $this->substituteNullsWithDefaultValues($mediaEntry); - $value['images'][$mediaEntry['value_id']] = $mediaEntry; - } - $product->setData($attrCode, $value); - } - - /** - * @param array $rawData - * @return array - */ - private function substituteNullsWithDefaultValues(array $rawData) - { - $processedData = []; - foreach ($rawData as $key => $rawValue) { - if (null !== $rawValue) { - $processedValue = $rawValue; - } elseif (isset($rawData[$key . '_default'])) { - $processedValue = $rawData[$key . '_default']; - } else { - $processedValue = null; - } - $processedData[$key] = $processedValue; - } - - return $processedData; + $product->setData( + $this->getAttribute()->getAttributeCode(), + [ + 'images' => array_column($mediaEntries, null, 'value_id'), + 'values' => [] + ] + ); } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php index 2868392f85280..b68c43e40ff2f 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php @@ -190,7 +190,7 @@ public function createBatchBaseSelect($storeId, $attributeId) 'value.' . $linkField . ' = entity.' . $linkField, ] ), - ['label', 'position', 'disabled'] + [] )->joinLeft( ['default_value' => $this->getTable(self::GALLERY_VALUE_TABLE)], implode( @@ -201,8 +201,15 @@ public function createBatchBaseSelect($storeId, $attributeId) 'default_value.' . $linkField . ' = entity.' . $linkField, ] ), - ['label_default' => 'label', 'position_default' => 'position', 'disabled_default' => 'disabled'] - )->where( + [] + )->columns([ + 'label' => $this->getConnection()->getIfNullSql('`value`.`label`', '`default_value`.`label`'), + 'position' => $this->getConnection()->getIfNullSql('`value`.`position`', '`default_value`.`position`'), + 'disabled' => $this->getConnection()->getIfNullSql('`value`.`disabled`', '`default_value`.`disabled`'), + 'label_default' => 'default_value.label', + 'position_default' => 'default_value.position', + 'disabled_default' => 'default_value.disabled' + ])->where( $mainTableAlias . '.attribute_id = ?', $attributeId )->where( diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php index dfed4e4f37385..47ef3c999125f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/GalleryTest.php @@ -281,6 +281,9 @@ public function testBindValueToEntityRecordExists() $this->resource->bindValueToEntity($valueId, $entityId); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function testLoadGallery() { $productId = 5; @@ -329,7 +332,8 @@ public function testLoadGallery() 'main.value_id = entity.value_id', ['entity_id'] )->willReturnSelf(); - $this->product->expects($this->at(0))->method('getData')->with('entity_id')->willReturn($productId); + $this->product->expects($this->at(0))->method('getData') + ->with('entity_id')->willReturn($productId); $this->product->expects($this->at(1))->method('getStoreId')->will($this->returnValue($storeId)); $this->connection->expects($this->exactly(2))->method('quoteInto')->withConsecutive( ['value.store_id = ?'], @@ -338,26 +342,50 @@ public function testLoadGallery() 'value.store_id = ' . $storeId, 'default_value.store_id = ' . 0 ); + $this->connection->expects($this->any())->method('getIfNullSql')->will( + $this->returnValueMap([ + [ + '`value`.`label`', + '`default_value`.`label`', + 'IFNULL(`value`.`label`, `default_value`.`label`)' + ], + [ + '`value`.`position`', + '`default_value`.`position`', + 'IFNULL(`value`.`position`, `default_value`.`position`)' + ], + [ + '`value`.`disabled`', + '`default_value`.`disabled`', + 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)' + ] + ]) + ); $this->select->expects($this->at(2))->method('joinLeft')->with( ['value' => $getTableReturnValue], $quoteInfoReturnValue, - [ - 'label', - 'position', - 'disabled' - ] + [] )->willReturnSelf(); $this->select->expects($this->at(3))->method('joinLeft')->with( ['default_value' => $getTableReturnValue], $quoteDefaultInfoReturnValue, - ['label_default' => 'label', 'position_default' => 'position', 'disabled_default' => 'disabled'] + [] )->willReturnSelf(); - $this->select->expects($this->at(4))->method('where')->with( + $this->select->expects($this->at(4))->method('columns')->with([ + 'label' => 'IFNULL(`value`.`label`, `default_value`.`label`)', + 'position' => 'IFNULL(`value`.`position`, `default_value`.`position`)', + 'disabled' => 'IFNULL(`value`.`disabled`, `default_value`.`disabled`)', + 'label_default' => 'default_value.label', + 'position_default' => 'default_value.position', + 'disabled_default' => 'default_value.disabled' + ])->willReturnSelf(); + $this->select->expects($this->at(5))->method('where')->with( 'main.attribute_id = ?', $attributeId )->willReturnSelf(); - $this->select->expects($this->at(5))->method('where')->with('main.disabled = 0')->willReturnSelf(); - $this->select->expects($this->at(7))->method('where') + $this->select->expects($this->at(6))->method('where') + ->with('main.disabled = 0')->willReturnSelf(); + $this->select->expects($this->at(8))->method('where') ->with('entity.entity_id = ?', $productId) ->willReturnSelf(); $this->select->expects($this->once())->method('order') diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index a366065c89e76..8dcc623dd64dd 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -835,6 +835,11 @@ <index referenceId="CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID" indexType="btree"> <column name="value_id"/> </index> + <index referenceId="CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID_VALUE_ID_STORE_ID" indexType="btree"> + <column name="entity_id"/> + <column name="value_id"/> + <column name="store_id"/> + </index> </table> <table name="catalog_product_option" resource="default" engine="innodb" comment="Catalog Product Option Table"> <column xsi:type="int" name="option_id" padding="10" unsigned="true" nullable="false" identity="true" diff --git a/app/code/Magento/Catalog/etc/db_schema_whitelist.json b/app/code/Magento/Catalog/etc/db_schema_whitelist.json index b1543a6a007f9..31620e4e920f4 100644 --- a/app/code/Magento/Catalog/etc/db_schema_whitelist.json +++ b/app/code/Magento/Catalog/etc/db_schema_whitelist.json @@ -484,7 +484,8 @@ "index": { "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_STORE_ID": true, "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID": true, - "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID": true + "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID": true, + "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID_VALUE_ID_STORE_ID": true }, "constraint": { "PRIMARY": true, @@ -1121,4 +1122,4 @@ "CATALOG_PRODUCT_FRONTEND_ACTION_CUSTOMER_ID_PRODUCT_ID_TYPE_ID": true } } -} \ No newline at end of file +} diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 19de63b7a976c..f98075f2294cc 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -453,6 +453,10 @@ public function getConfigurableAttributes($product) ['group' => 'CONFIGURABLE', 'method' => __METHOD__] ); if (!$product->hasData($this->_configurableAttributes)) { + // for new product do not load configurable attributes + if (!$product->getId()) { + return []; + } $configurableAttributes = $this->getConfigurableAttributeCollection($product); $this->extensionAttributesJoinProcessor->process($configurableAttributes); $configurableAttributes->orderByPosition()->load(); @@ -1398,23 +1402,47 @@ private function getConfiguredUsedProductCollection( $skipStockFilter = true ) { $collection = $this->getUsedProductCollection($product); + if ($skipStockFilter) { $collection->setFlag('has_stock_status_filter', true); } + $collection - ->addAttributeToSelect($this->getCatalogConfig()->getProductAttributes()) + ->addAttributeToSelect($this->getAttributesForCollection($product)) ->addFilterByRequiredOptions() ->setStoreId($product->getStoreId()); - $requiredAttributes = ['name', 'price', 'weight', 'image', 'thumbnail', 'status', 'media_gallery']; - foreach ($requiredAttributes as $attributeCode) { - $collection->addAttributeToSelect($attributeCode); - } - foreach ($this->getUsedProductAttributes($product) as $usedProductAttribute) { - $collection->addAttributeToSelect($usedProductAttribute->getAttributeCode()); - } $collection->addMediaGalleryData(); $collection->addTierPriceData(); + return $collection; } + + /** + * @return array + */ + private function getAttributesForCollection(\Magento\Catalog\Model\Product $product) + { + $productAttributes = $this->getCatalogConfig()->getProductAttributes(); + + $requiredAttributes = [ + 'name', + 'price', + 'weight', + 'image', + 'thumbnail', + 'status', + 'visibility', + 'media_gallery' + ]; + + $usedAttributes = array_map( + function($attr) { + return $attr->getAttributeCode(); + }, + $this->getUsedProductAttributes($product) + ); + + return array_unique(array_merge($productAttributes, $requiredAttributes, $usedAttributes)); + } } diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php index ccff85dd9717f..3611d95f0c6ac 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php @@ -173,10 +173,13 @@ public function getChildrenIds($parentId, $required = true) $parentId ); - $childrenIds = [0 => []]; - foreach ($this->getConnection()->fetchAll($select) as $row) { - $childrenIds[0][$row['product_id']] = $row['product_id']; - } + $childrenIds = [ + 0 => array_column( + $this->getConnection()->fetchAll($select), + 'product_id', + 'product_id' + ) + ]; return $childrenIds; } diff --git a/app/code/Magento/ConfigurableProduct/Plugin/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php b/app/code/Magento/ConfigurableProduct/Plugin/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php index efddb278df36c..8a7e846c0e9f1 100644 --- a/app/code/Magento/ConfigurableProduct/Plugin/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php +++ b/app/code/Magento/ConfigurableProduct/Plugin/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php @@ -44,9 +44,7 @@ public function afterIsSalable( \Magento\Framework\Pricing\SaleableInterface $salableItem ) { if ($salableItem->getTypeId() == 'configurable' && $result) { - if (!$this->lowestPriceOptionsProvider->getProducts($salableItem)) { - $result = false; - } + $result = $salableItem->isSalable(); } return $result; diff --git a/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php b/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php index dc64f03a42d19..b27c7c8976b4c 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php @@ -60,19 +60,9 @@ public function afterCreateBatchBaseSelect(Gallery $originalResourceModel, Selec 'value.store_id = value_video.store_id', ] ), - [ - 'video_provider' => 'provider', - 'video_url' => 'url', - 'video_title' => 'title', - 'video_description' => 'description', - 'video_metadata' => 'metadata' - ] + [] )->joinLeft( - [ - 'default_value_video' => $originalResourceModel->getTable( - 'catalog_product_entity_media_gallery_value_video' - ) - ], + ['default_value_video' => $originalResourceModel->getTable('catalog_product_entity_media_gallery_value_video')], implode( ' AND ', [ @@ -80,14 +70,24 @@ public function afterCreateBatchBaseSelect(Gallery $originalResourceModel, Selec 'default_value.store_id = default_value_video.store_id', ] ), - [ - 'video_provider_default' => 'provider', - 'video_url_default' => 'url', - 'video_title_default' => 'title', - 'video_description_default' => 'description', - 'video_metadata_default' => 'metadata', - ] - ); + [] + )->columns([ + 'video_provider' => $originalResourceModel->getConnection() + ->getIfNullSql('`value_video`.`provider`', '`default_value_video`.`provider`'), + 'video_url' => $originalResourceModel->getConnection() + ->getIfNullSql('`value_video`.`url`', '`default_value_video`.`url`'), + 'video_title' => $originalResourceModel->getConnection() + ->getIfNullSql('`value_video`.`title`', '`default_value_video`.`title`'), + 'video_description' => $originalResourceModel->getConnection() + ->getIfNullSql('`value_video`.`description`', '`default_value_video`.`description`'), + 'video_metadata' => $originalResourceModel->getConnection() + ->getIfNullSql('`value_video`.`metadata`', '`default_value_video`.`metadata`'), + 'video_provider_default' => 'default_value_video.provider', + 'video_url_default' => 'default_value_video.url', + 'video_title_default' => 'default_value_video.title', + 'video_description_default' => 'default_value_video.description', + 'video_metadata_default' => 'default_value_video.metadata', + ]); return $select; } From b1c944b69b3a7504fa3cda663694481debac362a Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 13 Nov 2018 10:44:18 +0200 Subject: [PATCH 258/310] MAGETWO-96118: Few optimizations on category & product pages --- .../Model/Product/Gallery/ReadHandler.php | 8 ++++++++ .../Model/ResourceModel/Product/Gallery.php | 20 +++++++++++++++++-- .../Product/Type/Configurable.php | 3 +++ .../Pricing/Renderer/SalableResolver.php | 3 +-- .../Plugin/ExternalVideoResourceBackend.php | 10 +++++++++- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php index 4ad275bc70f90..a3726207b3024 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php @@ -47,6 +47,8 @@ public function __construct( } /** + * Execute read handler for catalog product gallery + * * @param Product $entity * @param array $arguments * @return object @@ -69,6 +71,8 @@ public function execute($entity, $arguments = []) } /** + * Add media data to product + * * @param Product $product * @param array $mediaEntries * @return void @@ -86,6 +90,8 @@ public function addMediaDataToProduct(Product $product, array $mediaEntries) } /** + * Get attribute + * * @return \Magento\Catalog\Api\Data\ProductAttributeInterface * @since 101.0.0 */ @@ -99,6 +105,8 @@ public function getAttribute() } /** + * Find default value + * * @param string $key * @param string[] &$image * @return string diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php index b68c43e40ff2f..635715a60742f 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php @@ -49,7 +49,8 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ protected function _construct() @@ -58,7 +59,8 @@ protected function _construct() } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function getConnection() @@ -67,6 +69,8 @@ public function getConnection() } /** + * Load data from table by valueId + * * @param string $tableNameAlias * @param array $ids * @param int|null $storeId @@ -111,6 +115,8 @@ public function loadDataFromTableByValueId( } /** + * Load product gallery by attributeId + * * @param \Magento\Catalog\Model\Product $product * @param int $attributeId * @return array @@ -132,6 +138,8 @@ public function loadProductGalleryByAttributeId($product, $attributeId) } /** + * Create base load select + * * @param int $entityId * @param int $storeId * @param int $attributeId @@ -151,6 +159,8 @@ protected function createBaseLoadSelect($entityId, $storeId, $attributeId) } /** + * Create batch base select + * * @param int $storeId * @param int $attributeId * @return \Magento\Framework\DB\Select @@ -247,6 +257,8 @@ protected function removeDuplicates(&$result) } /** + * Get main table alias + * * @return string * @since 101.0.0 */ @@ -256,6 +268,8 @@ public function getMainTableAlias() } /** + * Bind value to entity + * * @param int $valueId * @param int $entityId * @return int @@ -273,6 +287,8 @@ public function bindValueToEntity($valueId, $entityId) } /** + * Save data row + * * @param string $table * @param array $data * @param array $fields diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php index 3611d95f0c6ac..feffd22a0fb3d 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable.php @@ -19,6 +19,9 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Adapter\AdapterInterface; +/** + * Configurable product resource model. + */ class Configurable extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { /** diff --git a/app/code/Magento/ConfigurableProduct/Plugin/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php b/app/code/Magento/ConfigurableProduct/Plugin/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php index 8a7e846c0e9f1..df8782ae422b4 100644 --- a/app/code/Magento/ConfigurableProduct/Plugin/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php +++ b/app/code/Magento/ConfigurableProduct/Plugin/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php @@ -27,8 +27,7 @@ public function __construct( } /** - * Performs an additional check whether given configurable product has - * at least one configuration in-stock. + * Performs an additional check whether given configurable product has at least one configuration in-stock. * * @param \Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver $subject * @param bool $result diff --git a/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php b/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php index b27c7c8976b4c..04a3d868d14a6 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/ExternalVideoResourceBackend.php @@ -27,6 +27,8 @@ public function __construct(\Magento\ProductVideo\Model\ResourceModel\Video $vid } /** + * Plugin for after duplicate action + * * @param Gallery $originalResourceModel * @param array $valueIdMap * @return array @@ -45,6 +47,8 @@ public function afterDuplicate(Gallery $originalResourceModel, array $valueIdMap } /** + * Plugin for after create batch base select action + * * @param Gallery $originalResourceModel * @param Select $select * @return Select @@ -62,7 +66,11 @@ public function afterCreateBatchBaseSelect(Gallery $originalResourceModel, Selec ), [] )->joinLeft( - ['default_value_video' => $originalResourceModel->getTable('catalog_product_entity_media_gallery_value_video')], + [ + 'default_value_video' => $originalResourceModel->getTable( + 'catalog_product_entity_media_gallery_value_video' + ) + ], implode( ' AND ', [ From 4328497e27f38c9317b4a2731b08fbd61243a5ae Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 13 Nov 2018 10:37:02 +0100 Subject: [PATCH 259/310] Adjusted API-functional tests for new limits --- .../Framework/QueryComplexityLimiterTest.php | 132 ++++++++++++++++-- 1 file changed, 121 insertions(+), 11 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php index 3304d9e6198f4..9bf223068e226 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php @@ -24,7 +24,7 @@ public function testQueryComplexityIsLimited() $query = <<<QUERY { -category(id: 2) { + category(id: 2) { products { items { name @@ -52,9 +52,15 @@ public function testQueryComplexityIsLimited() new_from_date tier_price manufacturer - thumbnail + thumbnail { + url + label + } sku - image + image { + url + label + } canonical_url updated_at created_at @@ -76,12 +82,18 @@ public function testQueryComplexityIsLimited() special_from_date special_to_date new_to_date - thumbnail + thumbnail { + url + label + } new_from_date tier_price manufacturer sku - image + image { + url + label + } canonical_url updated_at created_at @@ -111,9 +123,15 @@ public function testQueryComplexityIsLimited() new_from_date tier_price manufacturer - thumbnail + thumbnail { + url + label + } sku - image + image { + url + label + } canonical_url updated_at created_at @@ -139,7 +157,10 @@ public function testQueryComplexityIsLimited() tier_price manufacturer sku - image + image { + url + label + } canonical_url updated_at created_at @@ -160,13 +181,101 @@ public function testQueryComplexityIsLimited() name special_from_date special_to_date + price { + minimalPrice { + amount { + value + currency + } + } + maximalPrice { + amount { + value + currency + } + } + regularPrice { + amount { + value + currency + } + } + } + tier_price + special_price + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } new_to_date new_from_date tier_price manufacturer sku - image - thumbnail + image { + url + label + } + thumbnail { + url + label + } canonical_url updated_at created_at @@ -221,9 +330,10 @@ public function testQueryComplexityIsLimited() } } } + QUERY; - self::expectExceptionMessageRegExp('/Max query complexity should be 160 but got 169/'); + self::expectExceptionMessageRegExp('/Max query complexity should be 250 but got 252/'); $this->graphQlQuery($query); } From 436e3d3a6bf8179220747bef5cbdaeaaab4b730f Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Tue, 13 Nov 2018 12:58:32 +0300 Subject: [PATCH 260/310] MAGETWO-91725: Reward Points Balance Update Emails are not being sent when balance change initiated by store front - Fix webapi test --- .../Customer/Api/CustomerMetadataTest.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerMetadataTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerMetadataTest.php index f2632aa1481e4..3b1d431342988 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerMetadataTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerMetadataTest.php @@ -9,6 +9,7 @@ use Magento\Customer\Api\Data\CustomerInterface as Customer; use Magento\Customer\Model\Data\AttributeMetadata; use Magento\TestFramework\TestCase\WebapiAbstract; +use Magento\TestFramework\Helper\Bootstrap; /** * Class CustomerMetadataTest @@ -19,6 +20,19 @@ class CustomerMetadataTest extends WebapiAbstract const SERVICE_VERSION = "V1"; const RESOURCE_PATH = "/V1/attributeMetadata/customer"; + /** + * @var CustomerMetadataInterface + */ + private $customerMetadata; + + /** + * Execute per test initialization. + */ + public function setUp() + { + $this->customerMetadata = Bootstrap::getObjectManager()->create(CustomerMetadataInterface::class); + } + /** * Test retrieval of attribute metadata for the customer entity type. * @@ -200,8 +214,7 @@ public function testGetCustomAttributesMetadata() $attributeMetadata = $this->_webApiCall($serviceInfo); - // There are no default custom attributes. - $this->assertCount(0, $attributeMetadata); + $this->assertCount(count($this->customerMetadata->getCustomAttributesMetadata()), $attributeMetadata); } /** From 19c1f871b6bb1853ad960edfc3f6c8372c8a3e4f Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 13 Nov 2018 11:40:06 +0100 Subject: [PATCH 261/310] API-functional tests coverage --- .../Catalog/CategoryProductsCountTest.php | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsCountTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsCountTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsCountTest.php new file mode 100644 index 0000000000000..eddd456a7b866 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsCountTest.php @@ -0,0 +1,143 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\Product\Attribute\Source\Status as productStatus; + +/** + * Class CategoryProductsCountTest + * + * Test for Magento\CatalogGraphQl\Model\Resolver\Category\ProductsCount resolver + */ +class CategoryProductsCountTest extends GraphQlAbstract +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $objectManager->create(ProductRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testCategoryWithSaleableProduct() + { + $categoryId = 2; + + $query = <<<QUERY +{ + category(id: {$categoryId}) { + id + product_count + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertEquals(1, $response['category']['product_count']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/category_product.php + */ + public function testCategoryWithInvisibleProduct() + { + $categoryId = 333; + $sku = 'simple333'; + + $product = $this->productRepository->get($sku); + $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); + $this->productRepository->save($product); + + $query = <<<QUERY +{ + category(id: {$categoryId}) { + id + product_count + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertEquals(0, $response['category']['product_count']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + */ + public function testCategoryWithOutOfStockProductManageStockEnabled() + { + $categoryId = 2; + + $query = <<<QUERY +{ + category(id: {$categoryId}) { + id + product_count + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertEquals(0, $response['category']['product_count']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/category_product.php + */ + public function testCategoryWithOutOfStockProductManageStockDisabled() + { + $categoryId = 333; + + $query = <<<QUERY +{ + category(id: {$categoryId}) { + id + product_count + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertEquals(1, $response['category']['product_count']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/category_product.php + */ + public function testCategoryWithDisabledProduct() + { + $categoryId = 333; + $sku = 'simple333'; + + $product = $this->productRepository->get($sku); + $product->setStatus(ProductStatus::STATUS_DISABLED); + $this->productRepository->save($product); + + $query = <<<QUERY +{ + category(id: {$categoryId}) { + id + product_count + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertEquals(0, $response['category']['product_count']); + } +} From c683dde7e44b30c81f55593c9275eb1f31dc7cb1 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 13 Nov 2018 13:09:09 +0200 Subject: [PATCH 262/310] MAGETWO-96118: Few optimizations on category & product pages --- app/code/Magento/Catalog/etc/db_schema.xml | 2 +- app/code/Magento/Catalog/etc/db_schema_whitelist.json | 2 +- .../Model/Product/Type/ConfigurableTest.php | 9 +++++++-- .../Framework/Interception/PluginList/PluginList.php | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 8dcc623dd64dd..17e3dddc41c3b 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -835,7 +835,7 @@ <index referenceId="CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID" indexType="btree"> <column name="value_id"/> </index> - <index referenceId="CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID_VALUE_ID_STORE_ID" indexType="btree"> + <index referenceId="CAT_PRD_ENTT_MDA_GLR_VAL_ENTT_ID_VAL_ID_STORE_ID" indexType="btree"> <column name="entity_id"/> <column name="value_id"/> <column name="store_id"/> diff --git a/app/code/Magento/Catalog/etc/db_schema_whitelist.json b/app/code/Magento/Catalog/etc/db_schema_whitelist.json index 31620e4e920f4..d4bd6927d4345 100644 --- a/app/code/Magento/Catalog/etc/db_schema_whitelist.json +++ b/app/code/Magento/Catalog/etc/db_schema_whitelist.json @@ -485,7 +485,7 @@ "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_STORE_ID": true, "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID": true, "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_VALUE_ID": true, - "CATALOG_PRODUCT_ENTITY_MEDIA_GALLERY_VALUE_ENTITY_ID_VALUE_ID_STORE_ID": true + "CAT_PRD_ENTT_MDA_GLR_VAL_ENTT_ID_VAL_ID_STORE_ID": true }, "constraint": { "PRIMARY": true, diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php index bdb36b93af21c..78fa4733a2562 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +// @codingStandardsIgnoreFile + namespace Magento\ConfigurableProduct\Model\Product\Type; use Magento\Catalog\Api\Data\ProductInterface; @@ -128,6 +130,10 @@ public function testGetUsedProductAttributes() $this->assertEquals($testConfigurable->getData(), $attributes[$attributeId]->getData()); } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ public function testGetConfigurableAttributes() { $collection = $this->model->getConfigurableAttributes($this->product); @@ -332,8 +338,7 @@ public function testGetSelectedAttributesInfo() $attribute = reset($attributes); $optionValueId = $attribute['values'][0]['value_index']; - $product->addCustomOption( - 'attributes', + $product->addCustomOption('attributes', $serializer->serialize([$attribute['attribute_id'] => $optionValueId]) ); diff --git a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php index e21841b48bc13..d20cd57f4e43f 100644 --- a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php +++ b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php @@ -264,7 +264,7 @@ public function getPlugin($type, $code) public function getNext($type, $method, $code = '__self') { $this->_loadScopedData(); - if (!isset($this->_inherited[$type]) && !array_key_exists($type, $this->_inherited)) { + if ($this->_inherited !== null && !isset($this->_inherited[$type]) && !array_key_exists($type, $this->_inherited)) { $this->_inheritPlugins($type); } $key = $type . '_' . lcfirst($method) . '_' . $code; From f4a0e6a6e0f484c6a5d4c469fdd84f027aa4aac8 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Tue, 13 Nov 2018 14:29:42 +0300 Subject: [PATCH 263/310] MAGETWO-62728: My Wishlist - quantity input box issue - Fixed static test; --- .../Wishlist/view/frontend/templates/item/column/cart.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml index 7578774fabc2c..848c6a76393f8 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml @@ -22,7 +22,7 @@ $allowedQty = $block->getMinMaxQty(); <div class="field qty"> <label class="label" for="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> - <input type="number" data-role="qty" id="qty[<?= /* @noEscape */ $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true, 'validate-item-quantity':{'minAllowed':<?= $allowedQty['minAllowed'] ?>,'maxAllowed':<?= $allowedQty['maxAllowed'] ?>}}" + <input type="number" data-role="qty" id="qty[<?= /* @noEscape */ $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true, 'validate-item-quantity':{'minAllowed':<?= /* @noEscape */ $allowedQty['minAllowed'] ?>,'maxAllowed':<?= /* @noEscape */ $allowedQty['maxAllowed'] ?>}}" name="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" value="<?= /* @noEscape */ (int)($block->getAddToCartQty($item) * 1) ?>"> </div> </div> From c4b394a43f6e2690c3706eb5c5768a69185584d5 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 13 Nov 2018 13:56:45 +0200 Subject: [PATCH 264/310] MAGETWO-96118: Few optimizations on category & product pages --- .../Magento/Framework/Interception/PluginList/PluginList.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php index d20cd57f4e43f..e21841b48bc13 100644 --- a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php +++ b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php @@ -264,7 +264,7 @@ public function getPlugin($type, $code) public function getNext($type, $method, $code = '__self') { $this->_loadScopedData(); - if ($this->_inherited !== null && !isset($this->_inherited[$type]) && !array_key_exists($type, $this->_inherited)) { + if (!isset($this->_inherited[$type]) && !array_key_exists($type, $this->_inherited)) { $this->_inheritPlugins($type); } $key = $type . '_' . lcfirst($method) . '_' . $code; From 91157807e4e9bbb055b61d0f949535a816c172b6 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 13 Nov 2018 13:59:36 +0200 Subject: [PATCH 265/310] MAGETWO-96118: Few optimizations on category & product pages --- .../Model/Product/Type/ConfigurableTest.php | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php index 5e9399ddd3d65..d1cf77f03a7bd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php @@ -27,6 +27,7 @@ * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) + * @codingStandardsIgnoreFile */ class ConfigurableTest extends \PHPUnit\Framework\TestCase { @@ -154,8 +155,7 @@ protected function setUp() ->setMethods(['create']) ->getMock(); $this->productCollectionFactory = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\CollectionFactory::class - ) + \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\CollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); @@ -197,11 +197,6 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->productFactory = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterfaceFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->salableProcessor = $this->createMock(SalableProcessor::class); $this->model = $this->objectHelper->getObject( @@ -287,8 +282,7 @@ public function testSave() ->method('getData') ->willReturnMap($dataMap); $attribute = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class - ) + \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class) ->disableOriginalConstructor() ->setMethods(['addData', 'setStoreId', 'setProductId', 'save', '__wakeup', '__sleep']) ->getMock(); @@ -385,7 +379,7 @@ public function testGetUsedProducts() ['_cache_instance_used_product_attributes', null, []] ] ); - + $this->catalogConfig->expects($this->any())->method('getProductAttributes')->willReturn([]); $productCollection->expects($this->atLeastOnce())->method('addAttributeToSelect')->willReturnSelf(); $productCollection->expects($this->once())->method('setProductFilter')->willReturnSelf(); $productCollection->expects($this->atLeastOnce())->method('setFlag')->willReturnSelf(); @@ -471,8 +465,7 @@ public function testGetConfigurableAttributesAsArray($productStore) $eavAttribute->expects($this->atLeastOnce())->method('getStoreLabel')->willReturn('Store Label'); $attribute = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class - ) + \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class) ->disableOriginalConstructor() ->setMethods(['getProductAttribute', '__wakeup', '__sleep']) ->getMock(); @@ -515,17 +508,34 @@ public function getConfigurableAttributesAsArrayDataProvider() ]; } - public function testGetConfigurableAttributes() + public function testGetConfigurableAttributesNewProduct() { $configurableAttributes = '_cache_instance_configurable_attributes'; /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->setMethods(['getData', 'hasData', 'setData']) + ->setMethods(['hasData', 'getId']) ->disableOriginalConstructor() ->getMock(); $product->expects($this->once())->method('hasData')->with($configurableAttributes)->willReturn(false); + $product->expects($this->once())->method('getId')->willReturn(null); + + $this->assertEquals([], $this->model->getConfigurableAttributes($product)); + } + + public function testGetConfigurableAttributes() + { + $configurableAttributes = '_cache_instance_configurable_attributes'; + + /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ + $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->setMethods(['getData', 'hasData', 'setData', 'getId']) + ->disableOriginalConstructor() + ->getMock(); + + $product->expects($this->once())->method('hasData')->with($configurableAttributes)->willReturn(false); + $product->expects($this->once())->method('getId')->willReturn(1); $attributeCollection = $this->getMockBuilder(Collection::class) ->setMethods(['setProductFilter', 'orderByPosition', 'load']) @@ -582,8 +592,7 @@ public function testHasOptionsConfigurableAttribute() ->disableOriginalConstructor() ->getMock(); $attributeMock = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class - ) + \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class) ->disableOriginalConstructor() ->getMock(); From cbd6203d6505c5e366928dea5249b926a6a8bbb3 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Tue, 13 Nov 2018 14:27:31 +0200 Subject: [PATCH 266/310] GraphQl-222: Apply changes from CR for PR 162 Fix static tests --- .../GraphQl/Exception/GraphQlAuthorizationException.php | 4 ++-- .../Framework/GraphQl/Exception/GraphQlInputException.php | 4 ++-- .../GraphQl/Exception/GraphQlNoSuchEntityException.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthorizationException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthorizationException.php index f71652edc63b3..f1232ebd4d14b 100644 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthorizationException.php +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlAuthorizationException.php @@ -37,7 +37,7 @@ public function __construct(Phrase $phrase, \Exception $cause = null, $code = 0, } /** - * {@inheritDoc} + * @inheritdoc */ public function isClientSafe() : bool { @@ -45,7 +45,7 @@ public function isClientSafe() : bool } /** - * {@inheritDoc} + * @inheritdoc */ public function getCategory() : string { diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlInputException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlInputException.php index 6f97f06261358..429b7c04b7475 100644 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlInputException.php +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlInputException.php @@ -37,7 +37,7 @@ public function __construct(Phrase $phrase, \Exception $cause = null, $code = 0, } /** - * {@inheritDoc} + * @inheritdoc */ public function isClientSafe() : bool { @@ -45,7 +45,7 @@ public function isClientSafe() : bool } /** - * {@inheritDoc} + * @inheritdoc */ public function getCategory() : string { diff --git a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlNoSuchEntityException.php b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlNoSuchEntityException.php index 4bd24d6cfd4a7..2a0b9d3bc2eaa 100644 --- a/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlNoSuchEntityException.php +++ b/lib/internal/Magento/Framework/GraphQl/Exception/GraphQlNoSuchEntityException.php @@ -37,7 +37,7 @@ public function __construct(Phrase $phrase, \Exception $cause = null, $code = 0, } /** - * {@inheritDoc} + * @inheritdoc */ public function isClientSafe() : bool { @@ -45,7 +45,7 @@ public function isClientSafe() : bool } /** - * {@inheritDoc} + * @inheritdoc */ public function getCategory() : string { From 6062a1eae3d1ced249379daf4c77de711410b349 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Tue, 13 Nov 2018 14:34:53 +0200 Subject: [PATCH 267/310] GraphQl: Missed PHPDoc argument headers in method graphQlQuery --- .../Magento/TestFramework/TestCase/GraphQlAbstract.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php index 9754a340900e2..790581c476da1 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php @@ -52,11 +52,14 @@ public function graphQlQuery( } /** + * Compose headers + * + * @param array $headers * @return string[] */ - private function composeHeaders($headers) + private function composeHeaders(array $headers): array { - $headersArray =[]; + $headersArray = []; foreach ($headers as $key => $value) { $headersArray[] = sprintf('%s: %s', $key, $value); } From 74fce4acb449bb2f3b31e4e93d876654c5f0f0ea Mon Sep 17 00:00:00 2001 From: Vlad Veselov <vlad.veselov@gmail.com> Date: Tue, 13 Nov 2018 14:50:30 +0200 Subject: [PATCH 268/310] Fix PHP Notice due to magento/magento2#18727 --- pub/index.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pub/index.php b/pub/index.php index 90b4778265447..d363951691d58 100644 --- a/pub/index.php +++ b/pub/index.php @@ -25,15 +25,14 @@ } $params = $_SERVER; -$params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] = array_replace_recursive( - $params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS], - [ +$params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] = + ($params[Bootstrap::INIT_PARAM_FILESYSTEM_DIR_PATHS] ?? []) + + [ DirectoryList::PUB => [DirectoryList::URL_PATH => ''], DirectoryList::MEDIA => [DirectoryList::URL_PATH => 'media'], DirectoryList::STATIC_VIEW => [DirectoryList::URL_PATH => 'static'], DirectoryList::UPLOAD => [DirectoryList::URL_PATH => 'media/upload'], - ] -); + ]; $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $params); /** @var \Magento\Framework\App\Http $app */ $app = $bootstrap->createApplication(\Magento\Framework\App\Http::class); From 1378ed33cad3ab8fb4d121b700bfe53d8733c408 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Tue, 13 Nov 2018 15:11:50 +0200 Subject: [PATCH 269/310] GraphQl-11: Alphabetize Schema Fields -- Fix static tests --- .../Magento/Framework/GraphQl/Config/GraphQlReaderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php index 3d3372429123a..7f8996daa6e97 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php @@ -194,7 +194,7 @@ enumValues(includeDeprecated: true) { $sortFields = ['inputFields', 'fields']; foreach ($sortFields as $sortField) { isset($searchTerm[$sortField]) && is_array($searchTerm[$sortField]) - ? usort($searchTerm[$sortField], function($a, $b) { + ? usort($searchTerm[$sortField], function ($a, $b) { $cmpField = 'name'; return isset($a[$cmpField]) && isset($b[$cmpField]) ? strcmp($a[$cmpField], $b[$cmpField]) : 0; From 7f61b6f9d20565d0338cd1f0a39afde7b9edaabd Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Tue, 13 Nov 2018 15:39:59 +0200 Subject: [PATCH 270/310] GraphQl-222: Apply changes from CR for PR 162 - Fix API-functional tests --- .../GraphQl/Customer/AccountInformationTest.php | 2 +- .../GraphQl/Customer/RevokeCustomerTokenTest.php | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php index 2caee84ba6f00..942e321a78718 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/AccountInformationTest.php @@ -238,7 +238,7 @@ public function testUpdateAccountInformationIfCustomerIsLocked() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException \Exception - * @expectedExceptionMessage For changing "email" you should provide current "password". + * @expectedExceptionMessage Provide the current "password" to change "email". */ public function testUpdateEmailIfPasswordIsMissed() { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php index 415a81f8cf45a..9bdbf3059eeaf 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php @@ -23,7 +23,9 @@ public function testRevokeCustomerTokenValidCredentials() { $query = <<<QUERY mutation { - revokeCustomerToken + revokeCustomerToken { + result + } } QUERY; @@ -35,7 +37,7 @@ public function testRevokeCustomerTokenValidCredentials() $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; $response = $this->graphQlQuery($query, [], '', $headerMap); - $this->assertTrue($response['revokeCustomerToken']); + $this->assertTrue($response['revokeCustomerToken']['result']); } /** @@ -46,7 +48,9 @@ public function testRevokeCustomerTokenForGuestCustomer() { $query = <<<QUERY mutation { - revokeCustomerToken + revokeCustomerToken { + result + } } QUERY; $this->graphQlQuery($query, [], ''); From 7dba16ba0d61af22b241d58d99f026d0a140222f Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Tue, 13 Nov 2018 15:55:47 +0200 Subject: [PATCH 271/310] GraphQl-39: Manage Shipping methods on Cart --- .../Address/AddressDataProvider.php | 2 +- .../{CartAddress.php => CartAddresses.php} | 24 ++----------------- .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 3 files changed, 4 insertions(+), 24 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/{Resolver => Cart}/Address/AddressDataProvider.php (98%) rename app/code/Magento/QuoteGraphQl/Model/Resolver/{CartAddress.php => CartAddresses.php} (59%) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php similarity index 98% rename from app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php index 80fe973c4e449..fb742477ec99b 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/Address/AddressDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Resolver\Address; +namespace Magento\QuoteGraphQl\Model\Cart\Address; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Quote\Api\Data\AddressInterface; diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php similarity index 59% rename from app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php rename to app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php index 54bd8fa2a5717..69544672bf12e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php @@ -11,14 +11,12 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Model\MaskedQuoteIdToQuoteId; -use Magento\QuoteGraphQl\Model\Resolver\Address\AddressDataProvider; +use Magento\QuoteGraphQl\Model\Cart\Address\AddressDataProvider; /** * @inheritdoc */ -class CartAddress implements ResolverInterface +class CartAddresses implements ResolverInterface { /** * @var AddressDataProvider @@ -26,29 +24,11 @@ class CartAddress implements ResolverInterface private $addressDataProvider; /** - * @var CartRepositoryInterface - */ - private $cartRepository; - - /** - * @var MaskedQuoteIdToQuoteId - */ - private $maskedQuoteIdToQuoteId; - - /** - * CartAddress constructor. - * - * @param MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId - * @param CartRepositoryInterface $cartRepository * @param AddressDataProvider $addressDataProvider */ public function __construct( - MaskedQuoteIdToQuoteId $maskedQuoteIdToQuoteId, - CartRepositoryInterface $cartRepository, AddressDataProvider $addressDataProvider ) { - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->cartRepository = $cartRepository; $this->addressDataProvider = $addressDataProvider; } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index ce982952f1aee..a6c56318d7a9a 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -95,7 +95,7 @@ type Cart { cart_id: String items: [CartItemInterface] applied_coupon: AppliedCoupon - addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddress") + addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddresses") } type CartAddress { From 4a8720485da929caa4301fdac430f91a3628d42d Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Tue, 13 Nov 2018 16:18:08 +0200 Subject: [PATCH 272/310] GraphQl-39: Manage Shipping methods on Cart - Fix static tests --- .../Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php index 7f945dca2fd76..a630b2d07c7df 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingMethodOnCart.php @@ -98,4 +98,4 @@ public function execute(Quote $cart, int $cartAddressId, string $carrierCode, st throw new GraphQlInputException(__($exception->getMessage())); } } -} \ No newline at end of file +} From 889824ea3bedd2295678d4bd631473a82105ae68 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 13 Nov 2018 15:31:05 +0100 Subject: [PATCH 273/310] Increased allowed query complexity to 300 --- app/code/Magento/GraphQl/etc/di.xml | 2 +- .../Framework/QueryComplexityLimiterTest.php | 63 ++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index 914dcc78e49e1..b2083ea758e56 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -100,7 +100,7 @@ <type name="Magento\Framework\GraphQl\Query\QueryComplexityLimiter"> <arguments> <argument name="queryDepth" xsi:type="number">20</argument> - <argument name="queryComplexity" xsi:type="number">250</argument> + <argument name="queryComplexity" xsi:type="number">300</argument> </arguments> </type> </config> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php index 9bf223068e226..352947714360a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php @@ -263,6 +263,66 @@ public function testQueryComplexityIsLimited() percentage_value website_id } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } + tier_prices { + customer_group_id + qty + percentage_value + website_id + } new_to_date new_from_date tier_price @@ -330,10 +390,9 @@ public function testQueryComplexityIsLimited() } } } - QUERY; - self::expectExceptionMessageRegExp('/Max query complexity should be 250 but got 252/'); + self::expectExceptionMessageRegExp('/Max query complexity should be 300 but got 302/'); $this->graphQlQuery($query); } From d84929a4241776521322ba3fc9521544639bd59c Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 13 Nov 2018 17:10:23 +0200 Subject: [PATCH 274/310] Fixed code style issue --- .../Test/Unit/Observer/CheckUserLoginBackendObserverTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php index 2d0a2a7e1c99a..415f022a7364d 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginBackendObserverTest.php @@ -107,7 +107,6 @@ public function requiredCaptchaDataProvider(): array ]; } - /** * Test check user login in backend with wrong captcha * From 770e0f84c8ef2965f71b732bd2d38c529a4ff442 Mon Sep 17 00:00:00 2001 From: Alexey Yakimovich <yakimovich@almagy.com> Date: Tue, 13 Nov 2018 18:23:00 +0300 Subject: [PATCH 275/310] MAGETWO-62728: My Wishlist - quantity input box issue - Fexed DOC section for add-to-wishlist.js file; --- .../Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index db5f77348b2c0..8a20d2925849a 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -188,7 +188,7 @@ define([ /** * Bind form submit. * - @param {boolean} isFileUploaded + * @param {boolean} isFileUploaded */ bindFormSubmit: function (isFileUploaded) { var self = this; From 9c9581306d7c92751f643101e482ab4e9e4a02fd Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 13 Nov 2018 16:40:54 +0100 Subject: [PATCH 276/310] Fixed travis test running condition --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 78b088c9d848d..bd2b94865cafd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,6 +64,6 @@ script: # The scripts for grunt/phpunit type tests - if [ $TEST_SUITE == "functional" ]; then dev/tests/functional/vendor/phpunit/phpunit/phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js"] && [ $TEST_SUITE != "api" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi + - if [ $TEST_SUITE != "functional" ] && [ $TEST_SUITE != "js"] && [ $TEST_SUITE != "graphql-api-functional" ]; then phpunit -c dev/tests/$TEST_SUITE $TEST_FILTER; fi - if [ $TEST_SUITE == "js" ]; then grunt $GRUNT_COMMAND; fi - if [ $TEST_SUITE == "graphql-api-functional" ]; then phpunit -c dev/tests/api-functional; fi From 702353a0f32bc0b5d7cca708c90ec1839c983a8e Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 13 Nov 2018 10:12:17 -0600 Subject: [PATCH 277/310] MAGETWO-95589: [FT][Temando] Tests fail with enabled Temando extension --- .../Test/Mftf/Section/CheckoutShippingMethodsSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml index 56ed42fbfbbea..ab4b59fd67d03 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingMethodsSection.xml @@ -13,6 +13,7 @@ <element name="firstShippingMethod" type="radio" selector="//*[@id='checkout-shipping-method-load']//input[@class='radio']"/> <element name="shippingMethodRow" type="text" selector=".form.methods-shipping table tbody tr"/> <element name="checkShippingMethodByName" type="radio" selector="//div[@id='checkout-shipping-method-load']//td[contains(., '{{var1}}')]/..//input" parameterized="true"/> + <element name="shippingMethodFlatRate" type="radio" selector="#checkout-shipping-method-load input[value='flatrate_flatrate']"/> <element name="shippingMethodRowByName" type="text" selector="//div[@id='checkout-shipping-method-load']//td[contains(., '{{var1}}')]/.." parameterized="true"/> <element name="shipHereButton" type="button" selector="//div/following-sibling::div/button[contains(@class, 'action-select-shipping-item')]"/> <element name="shippingMethodLoader" type="button" selector="//div[contains(@class, 'checkout-shipping-method')]/following-sibling::div[contains(@class, 'loading-mask')]"/> From 534da46ce569e8fd4c079bc5978c1959cb45f06c Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Tue, 13 Nov 2018 18:18:29 +0200 Subject: [PATCH 278/310] Travis integration tests timeout fix --- dev/travis/before_script.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index dbd9d1cd4fec1..20bc59d718f13 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -13,9 +13,9 @@ case $TEST_SUITE in test_set_list=$(find testsuite/* -maxdepth 1 -mindepth 1 -type d | sort) test_set_count=$(printf "$test_set_list" | wc -l) - test_set_size[1]=$(printf "%.0f" $(echo "$test_set_count*0.17" | bc)) #17% - test_set_size[2]=$(printf "%.0f" $(echo "$test_set_count*0.27" | bc)) #27% - test_set_size[3]=$((test_set_count-test_set_size[1]-test_set_size[2])) #56% + test_set_size[1]=$(printf "%.0f" $(echo "$test_set_count*0.13" | bc)) #13% + test_set_size[2]=$(printf "%.0f" $(echo "$test_set_count*0.30" | bc)) #30% + test_set_size[3]=$((test_set_count-test_set_size[1]-test_set_size[2])) #55% echo "Total = ${test_set_count}; Batch #1 = ${test_set_size[1]}; Batch #2 = ${test_set_size[2]}; Batch #3 = ${test_set_size[3]};"; echo "==> preparing integration testsuite on index $INTEGRATION_INDEX with set size of ${test_set_size[$INTEGRATION_INDEX]}" From afd128e53d475092d8fbdf0c2a65d9654fbef9a6 Mon Sep 17 00:00:00 2001 From: vitaliyboyko <v.boyko@atwix.com> Date: Tue, 13 Nov 2018 18:27:09 +0200 Subject: [PATCH 279/310] graphQl: removed redundant multishipping references --- .../Magento/QuoteGraphQl/etc/graphql/di.xml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml index e7417d657a0ea..86bc954ae4ac4 100644 --- a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml @@ -8,21 +8,4 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface" type="Magento\QuoteGraphQl\Model\Cart\SetShippingAddressOnCart" /> - <virtualType name="multishippingPaymentSpecification" type="Magento\Payment\Model\Method\Specification\Composite"> - <arguments> - <argument name="specifications" xsi:type="array"> - <item name="enabled" xsi:type="string">Magento\Multishipping\Model\Payment\Method\Specification\Enabled</item> - </argument> - </arguments> - </virtualType> - <type name="Magento\Multishipping\Block\Checkout\Billing"> - <arguments> - <argument name="paymentSpecification" xsi:type="object">multishippingPaymentSpecification</argument> - </arguments> - </type> - <type name="Magento\Multishipping\Model\Checkout\Type\Multishipping"> - <arguments> - <argument name="paymentSpecification" xsi:type="object">multishippingPaymentSpecification</argument> - </arguments> - </type> </config> From 702072502f924e380149709ecfe5247ff4a7d510 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Tue, 13 Nov 2018 19:16:27 +0200 Subject: [PATCH 280/310] GraphQL-43: [Query] My Account > My Wish List - Refactoring --- .../Model/ProductDataProvider.php} | 21 ++++- .../Model/Resolver/ProductResolver.php | 53 ++++++++++++ .../WishlistItemsProductsResolver.php | 56 ------------- .../Model/Resolver/WishlistItemsResolver.php | 81 ++++++++++++++----- .../Model/Resolver/WishlistResolver.php | 53 +++++++----- .../Model/WishlistDataProvider.php | 37 --------- .../Model/WishlistItemsDataProvider.php | 50 ------------ .../WishlistGraphQl/etc/schema.graphqls | 16 ++-- .../Magento/GraphQl/Wishlist/WishlistTest.php | 60 +++++++++----- 9 files changed, 214 insertions(+), 213 deletions(-) rename app/code/Magento/{WishlistGraphQl/Model/WishlistItemsProductDataProvider.php => CatalogGraphQl/Model/ProductDataProvider.php} (60%) create mode 100644 app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php delete mode 100644 app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php delete mode 100644 app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php delete mode 100644 app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php b/app/code/Magento/CatalogGraphQl/Model/ProductDataProvider.php similarity index 60% rename from app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php rename to app/code/Magento/CatalogGraphQl/Model/ProductDataProvider.php index 76df065a80702..0d38490407e7c 100644 --- a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsProductDataProvider.php +++ b/app/code/Magento/CatalogGraphQl/Model/ProductDataProvider.php @@ -5,23 +5,38 @@ */ declare(strict_types=1); -namespace Magento\WishlistGraphQl\Model; +namespace Magento\CatalogGraphQl\Model; use Magento\Catalog\Api\ProductRepositoryInterface; -class WishlistItemsProductDataProvider +/** + * Product data provider + * + * TODO: will be replaces on deferred mechanism + */ +class ProductDataProvider { /** * @var ProductRepositoryInterface */ private $productRepository; + /** + * @param ProductRepositoryInterface $productRepository + */ public function __construct(ProductRepositoryInterface $productRepository) { $this->productRepository = $productRepository; } - public function getProductDataById(int $productId) { + /** + * Get product data by id + * + * @param int $productId + * @return array + */ + public function getProductDataById(int $productId): array + { $product = $this->productRepository->getById($productId); $productData = $product->toArray(); $productData['model'] = $product; diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php new file mode 100644 index 0000000000000..b59582a189f60 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WishlistGraphQl\Model\Resolver; + +use Magento\CatalogGraphQl\Model\ProductDataProvider; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Wishlist\Model\Item; + +/** + * Fetches the Product data according to the GraphQL schema + */ +class ProductResolver implements ResolverInterface +{ + /** + * @var ProductDataProvider + */ + private $productDataProvider; + + /** + * @param ProductDataProvider $productDataProvider + */ + public function __construct(ProductDataProvider $productDataProvider) + { + $this->productDataProvider = $productDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['model'])) { + throw new LocalizedException(__('Missing key "model" in Wishlist Item value data')); + } + /** @var Item $wishlistItem */ + $wishlistItem = $value['model']; + + return $this->productDataProvider->getProductDataById($wishlistItem->getProductId()); + } +} diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php deleted file mode 100644 index 26ace0e849e3f..0000000000000 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsProductsResolver.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\WishlistGraphQl\Model\Resolver; - -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; -use Magento\Framework\GraphQl\Query\Resolver\Value; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\WishlistGraphQl\Model\WishlistItemsProductDataProvider; - -class WishlistItemsProductsResolver implements ResolverInterface -{ - /** - * @var WishlistItemsProductDataProvider - */ - private $productDataProvider; - - public function __construct(WishlistItemsProductDataProvider $productDataProvider) - { - $this->productDataProvider = $productDataProvider; - } - - - /** - * Fetches the data from persistence models and format it according to the GraphQL schema. - * - * @param \Magento\Framework\GraphQl\Config\Element\Field $field - * @param ContextInterface $context - * @param ResolveInfo $info - * @param array|null $value - * @param array|null $args - * @throws \Exception - * @return mixed|Value - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - if (!isset($value['product_id'])) { - throw new GraphQlInputException( - __('Missing key %1 in wishlist item data', ['product_id']) - ); - } - return $this->productDataProvider->getProductDataById($value['product_id']); - } -} diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php index 707618b10602d..033c63441e907 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php @@ -7,36 +7,46 @@ namespace Magento\WishlistGraphQl\Model\Resolver; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; -use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Wishlist\Model\ResourceModel\Item\Collection as WishlistItemCollection; +use Magento\Wishlist\Model\ResourceModel\Item\CollectionFactory as WishlistItemCollectionFactory; use Magento\Wishlist\Model\Item; -use Magento\WishlistGraphQl\Model\WishlistItemsDataProvider; +use Magento\Wishlist\Model\Wishlist; +/** + * Fetches the Wishlist Items data according to the GraphQL schema + */ class WishlistItemsResolver implements ResolverInterface { /** - * @var WishlistItemsDataProvider + * @var WishlistItemCollectionFactory */ - private $wishlistItemsDataProvider; + private $wishlistItemCollectionFactory; - public function __construct(WishlistItemsDataProvider $wishlistItemsDataProvider) - { - $this->wishlistItemsDataProvider = $wishlistItemsDataProvider; + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param WishlistItemCollectionFactory $wishlistItemCollectionFactory + * @param StoreManagerInterface $storeManager + */ + public function __construct( + WishlistItemCollectionFactory $wishlistItemCollectionFactory, + StoreManagerInterface $storeManager + ) { + $this->wishlistItemCollectionFactory = $wishlistItemCollectionFactory; + $this->storeManager = $storeManager; } /** - * Fetches the data from persistence models and format it according to the GraphQL schema. - * - * @param \Magento\Framework\GraphQl\Config\Element\Field $field - * @param ContextInterface $context - * @param ResolveInfo $info - * @param array|null $value - * @param array|null $args - * @throws \Exception - * @return mixed|Value + * @inheritdoc */ public function resolve( Field $field, @@ -45,14 +55,41 @@ public function resolve( array $value = null, array $args = null ) { - return array_map(function (Item $wishlistItem) { - return [ + if (!isset($value['model'])) { + throw new LocalizedException(__('Missing key "model" in Wishlist value data')); + } + /** @var Wishlist $wishlist */ + $wishlist = $value['model']; + + $wishlistItems = $this->getWishListItems($wishlist); + + $data = []; + foreach ($wishlistItems as $wishlistItem) { + $data[] = [ 'id' => $wishlistItem->getId(), 'qty' => $wishlistItem->getData('qty'), - 'description' => (string)$wishlistItem->getDescription(), + 'description' => $wishlistItem->getDescription(), 'added_at' => $wishlistItem->getAddedAt(), - 'product_id' => (int)$wishlistItem->getProductId() + 'model' => $wishlistItem, ]; - }, $this->wishlistItemsDataProvider->getWishlistItemsForCustomer($context->getUserId())); + } + return $data; + } + + /** + * @param Wishlist $wishlist + * @return Item[] + */ + private function getWishListItems(Wishlist $wishlist): array + { + /** @var WishlistItemCollection $wishlistItemCollection */ + $wishlistItemCollection = $this->wishlistItemCollectionFactory->create(); + $wishlistItemCollection + ->addWishlistFilter($wishlist) + ->addStoreFilter(array_map(function (StoreInterface $store) { + return $store->getId(); + }, $this->storeManager->getStores())) + ->setVisibilityFilter(); + return $wishlistItemCollection->getItems(); } } diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php index ba1a6e935c10e..e3a788af2ea7e 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php @@ -8,34 +8,39 @@ namespace Magento\WishlistGraphQl\Model\Resolver; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; -use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\WishlistGraphQl\Model\WishlistDataProvider; +use Magento\Wishlist\Model\ResourceModel\Wishlist as WishlistResourceModel; +use Magento\Wishlist\Model\Wishlist; +use Magento\Wishlist\Model\WishlistFactory; +/** + * Fetches the Wishlist data according to the GraphQL schema + */ class WishlistResolver implements ResolverInterface { /** - * @var WishlistDataProvider + * @var WishlistResourceModel + */ + private $wishlistResource; + + /** + * @var WishlistFactory */ - private $wishlistDataProvider; + private $wishlistFactory; - public function __construct(WishlistDataProvider $wishlistDataProvider) + /** + * @param WishlistResourceModel $wishlistResource + * @param WishlistFactory $wishlistFactory + */ + public function __construct(WishlistResourceModel $wishlistResource, WishlistFactory $wishlistFactory) { - $this->wishlistDataProvider = $wishlistDataProvider; + $this->wishlistResource = $wishlistResource; + $this->wishlistFactory = $wishlistFactory; } /** - * Fetches the data from persistence models and format it according to the GraphQL schema. - * - * @param \Magento\Framework\GraphQl\Config\Element\Field $field - * @param ContextInterface $context - * @param ResolveInfo $info - * @param array|null $value - * @param array|null $args - * @throws \Exception - * @return mixed|Value + * @inheritdoc */ public function resolve( Field $field, @@ -44,10 +49,22 @@ public function resolve( array $value = null, array $args = null ) { - $wishlist = $this->wishlistDataProvider->getWishlistForCustomer($context->getUserId()); + $customerId = $context->getUserId(); + + /** @var Wishlist $wishlist */ + $wishlist = $this->wishlistFactory->create(); + $this->wishlistResource->load($wishlist, $customerId, 'customer_id'); + + if (null === $wishlist->getId()) { + return []; + } + return [ 'sharing_code' => $wishlist->getSharingCode(), - 'updated_at' => $wishlist->getUpdatedAt() + 'updated_at' => $wishlist->getUpdatedAt(), + 'items_count' => $wishlist->getItemsCount(), + 'name' => $wishlist->getName(), + 'model' => $wishlist, ]; } } diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php b/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php deleted file mode 100644 index a62ddebd91120..0000000000000 --- a/app/code/Magento/WishlistGraphQl/Model/WishlistDataProvider.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\WishlistGraphQl\Model; - -use Magento\Wishlist\Model\ResourceModel\Wishlist; -use Magento\Wishlist\Model\WishlistFactory; - -class WishlistDataProvider -{ - /** - * @var Wishlist - */ - private $wishlistResource; - /** - * @var WishlistFactory - */ - private $wishlistFactory; - - public function __construct(Wishlist $wishlistResource, WishlistFactory $wishlistFactory) - { - $this->wishlistResource = $wishlistResource; - $this->wishlistFactory = $wishlistFactory; - } - - public function getWishlistForCustomer(int $customerId): \Magento\Wishlist\Model\Wishlist - { - /** @var \Magento\Wishlist\Model\Wishlist $wishlist */ - $wishlist = $this->wishlistFactory->create(); - $this->wishlistResource->load($wishlist, $customerId, 'customer_id'); - return $wishlist; - } -} diff --git a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php b/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php deleted file mode 100644 index ca87b23460fc7..0000000000000 --- a/app/code/Magento/WishlistGraphQl/Model/WishlistItemsDataProvider.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\WishlistGraphQl\Model; - -use Magento\Store\Api\Data\StoreInterface; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Wishlist\Model\Item; -use Magento\Wishlist\Model\ResourceModel\Item\CollectionFactory as WishlistItemCollectionFactory; - -class WishlistItemsDataProvider -{ - - /** - * @var WishlistItemCollectionFactory - */ - private $wishlistItemCollectionFactory; - /** - * @var StoreManagerInterface - */ - private $storeManager; - - public function __construct( - WishlistItemCollectionFactory $wishlistItemCollectionFactory, - StoreManagerInterface $storeManager - ) { - $this->wishlistItemCollectionFactory = $wishlistItemCollectionFactory; - $this->storeManager = $storeManager; - } - - /** - * @param int $customerId - * @return Item[] - */ - public function getWishlistItemsForCustomer(int $customerId): array - { - $wishlistItemCollection = $this->wishlistItemCollectionFactory->create(); - $wishlistItemCollection->addCustomerIdFilter($customerId); - $wishlistItemCollection->addStoreFilter(array_map(function (StoreInterface $store) { - return $store->getId(); - }, $this->storeManager->getStores())); - $wishlistItemCollection->setVisibilityFilter(); - $wishlistItemCollection->load(); - return $wishlistItemCollection->getItems(); - } -} diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index 5c6d701138560..a63ec6eff6f5b 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -7,14 +7,16 @@ type Query { type WishlistOutput { items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "todo"), - sharing_code: String! @doc(description: "todo"), - updated_at: String! @doc(description: "todo") + items_count: Int @doc(description: "todo"), + name: String @doc(description: "todo"), + sharing_code: String @doc(description: "todo"), + updated_at: String @doc(description: "todo") } type WishlistItem { - id: Int! @doc(description: "todo") - qty: Float! @doc(description: "todo"), - description: String! @doc(description: "todo"), - added_at: String! @doc(description: "todo"), - product: ProductInterface @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsProductsResolver") + id: Int @doc(description: "todo") + qty: Float @doc(description: "todo"), + description: String @doc(description: "todo"), + added_at: String @doc(description: "todo"), + product: ProductInterface @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\ProductResolver") } \ No newline at end of file diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php index 53577d02df50f..d570fc09b7714 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php @@ -8,40 +8,45 @@ namespace Magento\GraphQl\Wishlist; use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\ResourceModel\Wishlist as WishlistResourceModel; +use Magento\Wishlist\Model\WishlistFactory; class WishlistTest extends GraphQlAbstract { - /** - * @var \Magento\TestFramework\ObjectManager - */ - private $objectManager; /** * @var CustomerTokenServiceInterface */ private $customerTokenService; + /** + * @var WishlistFactory + */ + private $wishlistFactory; + + /** + * @var WishlistResourceModel + */ + private $wishlistResource; + protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + $this->wishlistFactory = Bootstrap::getObjectManager()->get(WishlistFactory::class); + $this->wishlistResource = Bootstrap::getObjectManager()->get(WishlistResourceModel::class); } /** - * Verify the fields of CMS Block selected by identifiers - * * @magentoApiDataFixture Magento/Wishlist/_files/wishlist.php - * @throws \Magento\Framework\Exception\AuthenticationException - * @throws \Exception */ - public function testGetCustomersWishlist(): void + public function testGetCustomerWishlist(): void { /** @var \Magento\Wishlist\Model\Wishlist $wishlist */ - $wishlist = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Wishlist\Model\Wishlist::class - ); - $wishlist->loadByCustomerId(1, true); + $wishlist = $this->wishlistFactory->create(); + $this->wishlistResource->load($wishlist, 1, 'customer_id'); + /** @var Item $wishlistItem */ $wishlistItem = $wishlist->getItemCollection()->getFirstItem(); $wishlistItemProduct = $wishlistItem->getProduct(); @@ -49,26 +54,41 @@ public function testGetCustomersWishlist(): void <<<QUERY { wishlist { + items_count + name + sharing_code + updated_at items { id qty + description + added_at product { sku name } - description - added_at } - sharing_code - updated_at } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders('customer@example.com', 'password')); + $response = $this->graphQlQuery( + $query, + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', 'password') + ); + + $this->assertEquals($wishlist->getItemsCount(), $response['wishlist']['items_count']); + $this->assertEquals($wishlist->getName(), $response['wishlist']['name']); $this->assertEquals($wishlist->getSharingCode(), $response['wishlist']['sharing_code']); + $this->assertEquals($wishlist->getUpdatedAt(), $response['wishlist']['updated_at']); + + $this->assertEquals($wishlistItem->getId(), $response['wishlist']['items'][0]['id']); $this->assertEquals($wishlistItem->getData('qty'), $response['wishlist']['items'][0]['qty']); $this->assertEquals($wishlistItem->getDescription(), $response['wishlist']['items'][0]['description']); + $this->assertEquals($wishlistItem->getAddedAt(), $response['wishlist']['items'][0]['added_at']); + $this->assertEquals($wishlistItemProduct->getSku(), $response['wishlist']['items'][0]['product']['sku']); $this->assertEquals($wishlistItemProduct->getName(), $response['wishlist']['items'][0]['product']['name']); } From d3078dbd147a6fb30592370256f34c84f078fa33 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Wed, 14 Nov 2018 10:17:15 +0300 Subject: [PATCH 281/310] MAGETWO-71022: After return "RMA" is complete in Admin, "remaining quantity" in customer account shows incorrect value - Stabilize test --- .../Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml index 4ab1e3327960c..00f290cd94e70 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml @@ -13,5 +13,6 @@ <element name="email" type="input" selector="#email"/> <element name="requiredGroup" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-group-id']"/> <element name="requiredEmail" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-email']"/> + <element name="defaultGeneral" type="text" selector="//*[text()='Default (General)']" time="15"/> </section> </sections> \ No newline at end of file From 0acab420008a958fcbbb635cd0317681a5f06309 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <Veronika_Kurochkina@epam.com> Date: Wed, 14 Nov 2018 11:14:58 +0300 Subject: [PATCH 282/310] MAGETWO-62728: My Wishlist - quantity input box issue - Fix static tests --- .../Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index 8a20d2925849a..cab130f7c2104 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -188,7 +188,7 @@ define([ /** * Bind form submit. * - * @param {boolean} isFileUploaded + * @param {Boolean} isFileUploaded */ bindFormSubmit: function (isFileUploaded) { var self = this; @@ -199,6 +199,7 @@ define([ if (!$($(self.options.qtyInfo).closest('form')).valid()) { event.stopPropagation(); event.preventDefault(); + return; } From 66fb58bc8acbf04083bf6d332b5d83a9906e42a6 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Wed, 14 Nov 2018 16:02:45 +0200 Subject: [PATCH 283/310] GraphQl-41: [Query] My Account > My Orders -- Update composer files --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 7ce9efeb65e6f..2e1b77fe0a3c9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "243bbfba7578f2084615fa0c092f87a8", + "content-hash": "4f2fd2e8ffcc003e0a4a4116ec84b780", "packages": [ { "name": "braintree/braintree_php", From ce025fba563445590da3ca6f4d4d6caa5692ff34 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Wed, 14 Nov 2018 16:22:55 +0200 Subject: [PATCH 284/310] GraphQL-43: [Query] My Account > My Wish List - Update composer files --- app/code/Magento/WishlistGraphQl/composer.json | 1 + composer.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/WishlistGraphQl/composer.json b/app/code/Magento/WishlistGraphQl/composer.json index 9226ea1754e6a..e0f342174f879 100644 --- a/app/code/Magento/WishlistGraphQl/composer.json +++ b/app/code/Magento/WishlistGraphQl/composer.json @@ -6,6 +6,7 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-catalog": "*", + "magento/module-catalog-graph-ql": "*", "magento/module-wishlist": "*", "magento/module-store": "*" }, diff --git a/composer.lock b/composer.lock index d4ac4fc091bbc..bbc15bd96073b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "78153b5c8150c0d145b3372a534a45eb", + "content-hash": "18b533cc6fd96cad7b777d61edf94bd4", "packages": [ { "name": "braintree/braintree_php", From 0a12f2facafd2eaea06a208173cf1e564e107a19 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi <mankivsk@adobe.com> Date: Wed, 14 Nov 2018 18:00:59 -0600 Subject: [PATCH 285/310] ENGCOM-3450: [Forwardport] Fixed tierprice discount not calculated correctly if has specialprice #19179 --- .../Catalog/Model/Product/Attribute/Backend/Tierprice.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php index 23b2dfa01bfbd..e346c912dccaa 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -13,6 +13,9 @@ use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; +/** + * Backend model for Tierprice attribute + */ class Tierprice extends \Magento\Catalog\Model\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice { /** @@ -186,6 +189,10 @@ protected function modifyPriceData($object, $data) } /** + * Update Price values in DB + * + * Updates price values in DB from array comparing to old values. Returns bool if updated + * * @param array $valuesToUpdate * @param array $oldValues * @return boolean From 03f0861a1be43c9d283c64ad31b804b715997d3a Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Thu, 15 Nov 2018 16:03:08 +0300 Subject: [PATCH 286/310] MAGETWO-71022: After return "RMA" is complete in Admin, "remaining quantity" in customer account shows incorrect value - Stabilize test --- .../Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml index 00f290cd94e70..11d973d1e19de 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml @@ -13,6 +13,6 @@ <element name="email" type="input" selector="#email"/> <element name="requiredGroup" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-group-id']"/> <element name="requiredEmail" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-email']"/> - <element name="defaultGeneral" type="text" selector="//*[text()='Default (General)']" time="15"/> + <element name="defaultGeneral" type="text" selector="//*[contains(text(),'General')]" time="15"/> </section> </sections> \ No newline at end of file From f8cbd49653e93a17a7d9a3997f0ea8d5b79aa48f Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Thu, 15 Nov 2018 15:14:41 +0200 Subject: [PATCH 287/310] GraphQL-43: [Query] My Account > My Wish List --- .../Magento/WishlistGraphQl/composer.json | 1 - .../WishlistGraphQl/etc/schema.graphqls | 20 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/WishlistGraphQl/composer.json b/app/code/Magento/WishlistGraphQl/composer.json index e0f342174f879..630ee97acc2eb 100644 --- a/app/code/Magento/WishlistGraphQl/composer.json +++ b/app/code/Magento/WishlistGraphQl/composer.json @@ -5,7 +5,6 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-catalog": "*", "magento/module-catalog-graph-ql": "*", "magento/module-wishlist": "*", "magento/module-store": "*" diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index a63ec6eff6f5b..f5b5034fb734f 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -2,21 +2,21 @@ # See COPYING.txt for license details. type Query { - wishlist: WishlistOutput @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistResolver") @doc(description: "todo") + wishlist: WishlistOutput @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistResolver") @doc(description: "The wishlist query returns the contents of a customer's wish list") } type WishlistOutput { - items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "todo"), - items_count: Int @doc(description: "todo"), - name: String @doc(description: "todo"), - sharing_code: String @doc(description: "todo"), - updated_at: String @doc(description: "todo") + items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "An array of items in the customer's wish list"), + items_count: Int @doc(description: "The number of items in the wish list"), + name: String @doc(description: "When multiple wish lists are enabled, the name the customer assigns to the wishlist"), + sharing_code: String @doc(description: "An encrypted code that Magento uses to link to the wish list"), + updated_at: String @doc(description: "The time of the last modification to the wish list") } type WishlistItem { - id: Int @doc(description: "todo") - qty: Float @doc(description: "todo"), - description: String @doc(description: "todo"), - added_at: String @doc(description: "todo"), + id: Int @doc(description: "The wish list item ID") + qty: Float @doc(description: "The quantity of this wish list item"), + description: String @doc(description: "The customer's comment about this item"), + added_at: String @doc(description: "The time when the customer added the item to the wish list"), product: ProductInterface @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\ProductResolver") } \ No newline at end of file From 31797246faf847433f9353ec61375f3adb51e531 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Thu, 15 Nov 2018 15:29:20 +0200 Subject: [PATCH 288/310] GraphQL-43: [Query] My Account > My Wish List - Update composer files --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 7ce9efeb65e6f..0af9477ff4ef9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "243bbfba7578f2084615fa0c092f87a8", + "content-hash": "b367db394f87f9f9006b40e36e5a4175", "packages": [ { "name": "braintree/braintree_php", From 52c5fba5b765dec8582ea527ce745fe77b7d2634 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Thu, 15 Nov 2018 13:24:42 +0300 Subject: [PATCH 289/310] MAGETWO-91496: Instantiating WYSIWYG in DynamicRows - Create parameter for suffix adding --- .../Magento/Ui/view/base/web/js/form/element/wysiwyg.js | 8 ++++++++ .../Magento/Framework/Data/Form/Element/Editor.php | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js index 235113b389fc7..070761fff53e3 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js @@ -80,6 +80,14 @@ define([ return config.name.replace(/(\.|-)/g, '_'); }, + /** + * @inheritdoc + */ + destroy: function () { + this._super(); + wysiwyg.removeEvents(this.wysiwygId); + }, + /** * * @returns {exports} diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php index dee0b6c842f5f..b5f2017501c01 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php @@ -548,6 +548,7 @@ protected function getInlineJs($jsSetupObject, $forceLoad) */ public function getHtmlId() { - return parent::getHtmlId() . '${ $.wysiwygUniqueSuffix }'; + $suffix = $this->getConfig('dynamic_id') ? '${ $.wysiwygUniqueSuffix }' : ''; + return parent::getHtmlId() . $suffix; } } From adda7da9743ef8eb90f1d060e72d65bcdc7d0b40 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 15 Nov 2018 08:59:26 -0600 Subject: [PATCH 290/310] Revert "MQE-1267: Unskip Timezone Tests in Magento 2.3" - This reverts commit 0e0c110 --- .../Test/AdminApplyCatalogRuleByCategoryTest.xml | 3 +++ .../Mftf/Test/AdminCreateCatalogPriceRuleTest.xml | 15 +++++++++++++++ .../Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml | 3 +++ .../Test/StorefrontInactiveCatalogRuleTest.xml | 3 +++ 4 files changed, 24 insertions(+) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml index 741da96179b8c..716a363ec5d78 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml @@ -16,6 +16,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-74"/> <group value="CatalogRule"/> + <skip> + <issueId value="DEVOPS-3618"/> + </skip> </annotations> <before> <createData entity="ApiCategory" stepKey="createCategoryOne"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index befe0b0ce7f98..072385c46645a 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-65"/> <group value="CatalogRule"/> + <skip> + <issueId value="DEVOPS-3618"/> + </skip> </annotations> <before> <!-- Create the simple product and category that it will be in --> @@ -74,6 +77,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-93"/> <group value="CatalogRule"/> + <skip> + <issueId value="DEVOPS-3618"/> + </skip> </annotations> <before> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> @@ -97,6 +103,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-69"/> <group value="CatalogRule"/> + <skip> + <issueId value="DEVOPS-3618"/> + </skip> </annotations> <before> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> @@ -120,6 +129,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-60"/> <group value="CatalogRule"/> + <skip> + <issueId value="DEVOPS-3618"/> + </skip> </annotations> <before> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> @@ -143,6 +155,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-71"/> <group value="CatalogRule"/> + <skip> + <issueId value="DEVOPS-3618"/> + </skip> </annotations> <before> <!-- Create a simple product and a category--> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml index d3546d06492be..83770ffff5eab 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-160"/> <group value="CatalogRule"/> + <skip> + <issueId value="DEVOPS-3618"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index e7be6e8443a36..55f775e40feaa 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-79"/> <group value="CatalogRule"/> + <skip> + <issueId value="DEVOPS-3618"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> From db851ff49c3e8e6c49dce7d10c0a56a57020f9c4 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Thu, 15 Nov 2018 17:35:20 +0200 Subject: [PATCH 291/310] GraphQL-43: [Query] My Account > My Wish List - Update composer files --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 2e1b77fe0a3c9..36b4dca023191 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4f2fd2e8ffcc003e0a4a4116ec84b780", + "content-hash": "7538e15ea4ff1378cf55983ce9acba82", "packages": [ { "name": "braintree/braintree_php", From a0a150fce54acdbb6fc8fc85820972cd33ebdaa9 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi <mankivsk@adobe.com> Date: Thu, 15 Nov 2018 12:00:53 -0600 Subject: [PATCH 292/310] #2340: Fixed static tests --- .../Magento/Bundle/Model/OptionRepository.php | 20 +++++++++++++------ .../Test/Unit/Model/OptionRepositoryTest.php | 3 ++- .../product_grouped_with_out_of_stock.php | 2 +- ...uct_grouped_with_out_of_stock_rollback.php | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Bundle/Model/OptionRepository.php b/app/code/Magento/Bundle/Model/OptionRepository.php index 46c44c83b5bb5..0b96ea8d5b789 100644 --- a/app/code/Magento/Bundle/Model/OptionRepository.php +++ b/app/code/Magento/Bundle/Model/OptionRepository.php @@ -90,7 +90,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function get($sku, $optionId) { @@ -123,7 +123,7 @@ public function get($sku, $optionId) } /** - * {@inheritdoc} + * @inheritdoc */ public function getList($sku) { @@ -132,6 +132,8 @@ public function getList($sku) } /** + * Return list of product options + * * @param ProductInterface $product * @return \Magento\Bundle\Api\Data\OptionInterface[] */ @@ -141,7 +143,7 @@ public function getListByProduct(ProductInterface $product) } /** - * {@inheritdoc} + * @inheritdoc */ public function delete(\Magento\Bundle\Api\Data\OptionInterface $option) { @@ -157,7 +159,7 @@ public function delete(\Magento\Bundle\Api\Data\OptionInterface $option) } /** - * {@inheritdoc} + * @inheritdoc */ public function deleteById($sku, $optionId) { @@ -169,7 +171,7 @@ public function deleteById($sku, $optionId) } /** - * {@inheritdoc} + * @inheritdoc */ public function save( \Magento\Catalog\Api\Data\ProductInterface $product, @@ -189,6 +191,9 @@ public function save( * @param \Magento\Catalog\Api\Data\ProductInterface $product * @param \Magento\Bundle\Api\Data\OptionInterface $option * @return $this + * @throws InputException + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException */ protected function updateOptionSelection( \Magento\Catalog\Api\Data\ProductInterface $product, @@ -228,9 +233,12 @@ protected function updateOptionSelection( } /** + * Retrieve product by SKU + * * @param string $sku * @return \Magento\Catalog\Api\Data\ProductInterface - * @throws \Magento\Framework\Exception\InputException + * @throws InputException + * @throws NoSuchEntityException */ private function getProduct($sku) { diff --git a/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php index c579d8289d1b6..2450f63c38933 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/OptionRepositoryTest.php @@ -280,7 +280,8 @@ public function testDeleteById() /** * Tests if NoSuchEntityException thrown when provided $optionId not found */ - public function testDeleteByIdException() { + public function testDeleteByIdException() + { $productSku = 'sku'; $optionId = null; diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php index 7aa62b149b8c0..369ce7d490eea 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock.php @@ -1,4 +1,4 @@ -\<?php +<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php index 48e7d495f8985..26a7487077e44 100644 --- a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_out_of_stock_rollback.php @@ -1,4 +1,4 @@ -\<?php +<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. From b7c25f3f60649c7f4f2f47e46d13a6f94aba552f Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Fri, 16 Nov 2018 16:23:12 +0200 Subject: [PATCH 293/310] GraphQL-43: [Query] My Account > My Wish List -- Fix static tests --- .../WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php | 2 ++ app/code/Magento/WishlistGraphQl/registration.php | 1 + 2 files changed, 3 insertions(+) diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php index 033c63441e907..dfbbf6543f66f 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php @@ -77,6 +77,8 @@ public function resolve( } /** + * Get wishlist items + * * @param Wishlist $wishlist * @return Item[] */ diff --git a/app/code/Magento/WishlistGraphQl/registration.php b/app/code/Magento/WishlistGraphQl/registration.php index f2047f225e5b6..c5d468421f96e 100644 --- a/app/code/Magento/WishlistGraphQl/registration.php +++ b/app/code/Magento/WishlistGraphQl/registration.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); use Magento\Framework\Component\ComponentRegistrar; From 370aaebbd95045ed4aaefa37592e4abf12869215 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Fri, 16 Nov 2018 18:31:05 +0200 Subject: [PATCH 294/310] GraphQL-43: [Query] My Account > My Wish List -- Fix API-functional tests --- .../Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php index b59582a189f60..65c8498fc89ad 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php @@ -48,6 +48,6 @@ public function resolve( /** @var Item $wishlistItem */ $wishlistItem = $value['model']; - return $this->productDataProvider->getProductDataById($wishlistItem->getProductId()); + return $this->productDataProvider->getProductDataById((int)$wishlistItem->getProductId()); } } From 0d58352a7924ba28c0ba159f5b806048dea974f8 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Fri, 16 Nov 2018 19:12:46 +0200 Subject: [PATCH 295/310] GraphQL-115: Travis api-functional tests automated execution introduced --- dev/travis/before_script.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/travis/before_script.sh b/dev/travis/before_script.sh index 9f1edbf6c8a50..5d091efbb30a3 100755 --- a/dev/travis/before_script.sh +++ b/dev/travis/before_script.sh @@ -152,17 +152,17 @@ case $TEST_SUITE in --admin-use-security-key=0 \ --admin-password="123123q" - echo "Enabling production mode" - php bin/magento deploy:mode:set production - echo "Prepare api-functional tests for running" cd dev/tests/api-functional - cp -r _files/Magento/* ../../../app/code/Magento # Deploy and enable test modules before running tests + cp -r _files/Magento/TestModuleGraphQl* ../../../app/code/Magento # Deploy and enable test modules before running tests cp ./phpunit_graphql.xml.dist ./phpunit.xml sed -e "s?magento.url?${MAGENTO_HOST_NAME}?g" --in-place ./phpunit.xml cd ../../.. php bin/magento setup:upgrade + + echo "Enabling production mode" + php bin/magento deploy:mode:set production ;; esac From 4694c061b74e2d4e5c169480de7e855c3abc0a85 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 14:52:29 +0200 Subject: [PATCH 296/310] GraphQL-39: Manage Shipping methods on Cart --- .../Model/Cart/SetShippingAddressOnCart.php | 30 ++++++++++--------- .../SetShippingAddressesOnCartInterface.php | 3 +- .../Resolver/SetShippingAddressesOnCart.php | 2 +- app/code/Magento/QuoteGraphQl/composer.json | 3 +- .../Quote/SetShippingAddressOnCartTest.php | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php index 960900682db4c..b9fd5c7807d2f 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php @@ -7,9 +7,8 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Authorization\Model\UserContextInterface; use Magento\Customer\Api\Data\AddressInterface; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Quote\Api\Data\CartInterface; @@ -37,19 +36,27 @@ class SetShippingAddressOnCart implements SetShippingAddressesOnCartInterface */ private $addressModel; + /** + * @var CheckCustomerAccount + */ + private $checkCustomerAccount; + /** * @param ShippingAddressManagementInterface $shippingAddressManagement * @param AddressRepositoryInterface $addressRepository * @param Address $addressModel + * @param CheckCustomerAccount $checkCustomerAccount */ public function __construct( ShippingAddressManagementInterface $shippingAddressManagement, AddressRepositoryInterface $addressRepository, - Address $addressModel + Address $addressModel, + CheckCustomerAccount $checkCustomerAccount ) { $this->shippingAddressManagement = $shippingAddressManagement; $this->addressRepository = $addressRepository; $this->addressModel = $addressModel; + $this->checkCustomerAccount = $checkCustomerAccount; } /** @@ -66,7 +73,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s $customerAddressId = $shippingAddress['customer_address_id'] ?? null; $addressInput = $shippingAddress['address'] ?? null; - if (!$customerAddressId && !$addressInput) { + if (null === $customerAddressId && null === $addressInput) { throw new GraphQlInputException( __('The shipping address must contain either "customer_address_id" or "address".') ); @@ -76,19 +83,14 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s __('The shipping address cannot contain "customer_address_id" and "address" at the same time.') ); } - if ($customerAddressId) { - if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { - throw new GraphQlAuthorizationException( - __( - 'Guest users cannot manage addresses.' - ) - ); - } + if (null === $customerAddressId) { + $shippingAddress = $this->addressModel->addData($addressInput); + } else { + $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); + /** @var AddressInterface $customerAddress */ $customerAddress = $this->addressRepository->getById($customerAddressId); $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress); - } else { - $shippingAddress = $this->addressModel->addData($addressInput); } $this->shippingAddressManagement->assign($cart->getId(), $shippingAddress); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php index dde9cce9d8693..c5da3db75add7 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php @@ -15,11 +15,10 @@ * Extension point for setting shipping addresses for a specified shopping cart * * All objects that are responsible for setting shipping addresses on a cart via GraphQl - *should implement this interface. + * should implement this interface. */ interface SetShippingAddressesOnCartInterface { - /** * Set shipping addresses for a specified shopping cart * diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php index 587ebb2b6db1f..b024e7b77af40 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php @@ -93,7 +93,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return [ 'cart' => [ 'cart_id' => $maskedCartId, - 'model' => $cart + 'model' => $cart, ] ]; } diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index 9d29cda3b872b..881450745b84a 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -9,8 +9,7 @@ "magento/module-checkout": "*", "magento/module-catalog": "*", "magento/module-store": "*", - "magento/module-customer": "*", - "magento/module-authorization": "*" + "magento/module-customer": "*" }, "suggest": { "magento/module-graph-ql": "*" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index 2b4223cf67a89..a023d37895c23 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -139,7 +139,7 @@ public function testSetSavedShippingAddressOnCartByGuest() } } QUERY; - self::expectExceptionMessage('Guest users cannot manage addresses.'); + self::expectExceptionMessage('The current customer isn\'t authorized.'); $this->graphQlQuery($query); } From ac93407e2e12cc9d953574bc448e1830390de380 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 15:22:56 +0200 Subject: [PATCH 297/310] GraphQL-189: Replace GraphQlInputException on LocalizedException --- .../BundleGraphQl/Model/Resolver/BundleItemLinks.php | 4 ++-- .../BundleGraphQl/Model/Resolver/Options/Label.php | 4 ++-- .../Model/LayerFilterItemTypeResolverComposite.php | 6 ++---- .../Model/ProductLinkTypeResolverComposite.php | 8 ++------ .../Magento/CatalogGraphQl/Model/Resolver/Categories.php | 4 ++-- .../Model/Resolver/Category/Breadcrumbs.php | 4 ++-- .../Model/Resolver/Product/CanonicalUrl.php | 4 ++-- .../Model/Resolver/Product/EntityIdToId.php | 4 ++-- .../Model/Resolver/Product/MediaGalleryEntries.php | 4 ++-- .../CatalogGraphQl/Model/Resolver/Product/NewFromTo.php | 4 ++-- .../CatalogGraphQl/Model/Resolver/Product/Options.php | 4 ++-- .../CatalogGraphQl/Model/Resolver/Product/Price.php | 4 ++-- .../Resolver/Product/ProductComplexTextAttribute.php | 4 ++-- .../Model/Resolver/Product/ProductLinks.php | 4 ++-- .../CatalogGraphQl/Model/Resolver/Product/TierPrices.php | 4 ++-- .../CatalogGraphQl/Model/Resolver/Product/Websites.php | 4 ++-- .../Model/Resolver/OnlyXLeftInStockResolver.php | 4 ++-- .../Model/Resolver/StockStatusProvider.php | 4 ++-- .../Model/Resolver/Product/DownloadableOptions.php | 4 ++-- .../EavGraphQl/Model/Resolver/AttributeOptions.php | 9 +++++---- .../Magento/EavGraphQl/Model/Resolver/Query/Type.php | 3 +-- app/code/Magento/GraphQl/Model/EntityAttributeList.php | 6 +++--- .../Model/Resolver/GroupedItems.php | 4 ++-- .../UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php | 4 ++-- 24 files changed, 51 insertions(+), 57 deletions(-) diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/BundleItemLinks.php b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleItemLinks.php index f55028a7d1a5b..184f7177a995c 100644 --- a/app/code/Magento/BundleGraphQl/Model/Resolver/BundleItemLinks.php +++ b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleItemLinks.php @@ -7,7 +7,7 @@ namespace Magento\BundleGraphQl\Model\Resolver; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\BundleGraphQl\Model\Resolver\Links\Collection; use Magento\Framework\GraphQl\Config\Element\Field; @@ -47,7 +47,7 @@ public function __construct( public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($value['option_id']) || !isset($value['parent_id'])) { - throw new GraphQlInputException(__('"option_id" and "parent_id" values should be specified')); + throw new LocalizedException(__('"option_id" and "parent_id" values should be specified')); } $this->linkCollection->addIdFilters((int)$value['option_id'], (int)$value['parent_id']); diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php b/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php index bcddd5d084629..de72b18982c12 100644 --- a/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php +++ b/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Label.php @@ -7,7 +7,7 @@ namespace Magento\BundleGraphQl\Model\Resolver\Options; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Deferred\Product as ProductDataProvider; use Magento\Framework\GraphQl\Config\Element\Field; @@ -50,7 +50,7 @@ public function resolve( array $args = null ) { if (!isset($value['sku'])) { - throw new GraphQlInputException(__('"sku" value should be specified')); + throw new LocalizedException(__('"sku" value should be specified')); } $this->product->addProductSku($value['sku']); diff --git a/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php b/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php index e04b5d1bf67ff..02594ecfaf8e8 100644 --- a/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php +++ b/app/code/Magento/CatalogGraphQl/Model/LayerFilterItemTypeResolverComposite.php @@ -31,7 +31,7 @@ public function __construct(array $typeResolvers = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function resolveType(array $data) : string { @@ -42,8 +42,6 @@ public function resolveType(array $data) : string return $resolvedType; } } - if (empty($resolvedType)) { - throw new \LogicException('Cannot resolve layered filter type'); - } + throw new GraphQlInputException(__('Cannot resolve layered filter type')); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php b/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php index 5f6d1a65519f8..fbce18dfb67a5 100644 --- a/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php +++ b/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php @@ -29,8 +29,7 @@ public function __construct(array $productLinksTypeNameResolvers = []) } /** - * {@inheritdoc} - * @throws GraphQlInputException + * @inheritdoc */ public function resolveType(array $data) : string { @@ -48,9 +47,6 @@ public function resolveType(array $data) : string return $resolvedType; } } - - if (!$resolvedType) { - throw new \LogicException('Cannot resolve type'); - } + throw new GraphQlInputException(__('Cannot resolve type')); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php index de4c7dd71ca36..cb392a7b2295d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Categories.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\ResourceModel\Category\Collection; @@ -85,7 +85,7 @@ public function __construct( public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var \Magento\Catalog\Model\Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php index 9e966a060e5c6..b93c7e279153d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Breadcrumbs.php @@ -8,7 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Category; use Magento\CatalogGraphQl\Model\Resolver\Category\DataProvider\Breadcrumbs as BreadcrumbsDataProvider; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -38,7 +38,7 @@ public function __construct( public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($value['path'])) { - throw new GraphQlInputException(__('"path" value should be specified')); + throw new LocalizedException(__('"path" value should be specified')); } $breadcrumbsData = $this->breadcrumbsDataProvider->getData($value['path']); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php index 0f1a7d8ee9dab..9047eaee4b568 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php @@ -8,8 +8,8 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Catalog\Model\Product; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -29,7 +29,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /* @var $product Product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php index a09510be8bc7d..962256ceff442 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; @@ -46,7 +46,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php index a54cb62c16527..785cd75585d5d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Framework\GraphQl\Config\Element\Field; @@ -31,7 +31,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php index 2fa47f86ecb9d..88ae457c9986f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Framework\GraphQl\Config\Element\Field; @@ -31,7 +31,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php index 7c7e4ef117a50..cb53ebdab4810 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Option; @@ -32,7 +32,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php index 6f4f8553a324a..3bc9057f06b01 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Catalog\Pricing\Price\FinalPrice; @@ -59,7 +59,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductComplexTextAttribute.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductComplexTextAttribute.php index 96519d6191eee..2573e92e564b9 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductComplexTextAttribute.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductComplexTextAttribute.php @@ -7,11 +7,11 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Catalog\Helper\Output as OutputHelper; /** @@ -44,7 +44,7 @@ public function resolve( array $args = null ): array { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /* @var $product Product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php index 4d5622bd5c6d0..b2a612d6ae02d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ProductLink\Link; @@ -39,7 +39,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php index 63599a88c79a0..cf1b621ad65b6 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\TierPrice; @@ -34,7 +34,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Websites.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Websites.php index 9fe64a16935c7..070c564713a96 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Websites.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Websites.php @@ -7,7 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; @@ -47,7 +47,7 @@ public function __construct( public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($value['entity_id'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } $this->productWebsitesCollection->addIdFilters((int)$value['entity_id']); $result = function () use ($value) { diff --git a/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php b/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php index 169456f7d4bbd..9e10f0b448504 100644 --- a/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php +++ b/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php @@ -11,7 +11,7 @@ use Magento\CatalogInventory\Api\StockRegistryInterface; use Magento\CatalogInventory\Model\Configuration; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -50,7 +50,7 @@ public function __construct( public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!array_key_exists('model', $value) || !$value['model'] instanceof ProductInterface) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /* @var $product ProductInterface */ diff --git a/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php b/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php index 2f3d520c2cb8f..354e053efa90f 100644 --- a/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php +++ b/app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php @@ -10,7 +10,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\CatalogInventory\Api\Data\StockStatusInterface; use Magento\CatalogInventory\Api\StockStatusRepositoryInterface; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -39,7 +39,7 @@ public function __construct(StockStatusRepositoryInterface $stockStatusRepositor public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!array_key_exists('model', $value) || !$value['model'] instanceof ProductInterface) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /* @var $product ProductInterface */ diff --git a/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php b/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php index 5141361fecc0e..0842c96ef4b1a 100644 --- a/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php +++ b/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php @@ -7,7 +7,7 @@ namespace Magento\DownloadableGraphQl\Model\Resolver\Product; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Downloadable\Helper\Data as DownloadableHelper; @@ -77,7 +77,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var Product $product */ diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php index 6ccd610bead0d..b0def15f3a0d4 100644 --- a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php +++ b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php @@ -9,6 +9,7 @@ use Magento\EavGraphQl\Model\Resolver\DataProvider\AttributeOptions as AttributeOptionsDataProvider; use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\StateException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; @@ -68,12 +69,12 @@ public function resolve( /** * @param array $value * @return int - * @throws GraphQlInputException + * @throws LocalizedException */ private function getEntityType(array $value): int { if (!isset($value['entity_type'])) { - throw new GraphQlInputException(__('"Entity type should be specified')); + throw new LocalizedException(__('"Entity type should be specified')); } return (int)$value['entity_type']; @@ -82,12 +83,12 @@ private function getEntityType(array $value): int /** * @param array $value * @return string - * @throws GraphQlInputException + * @throws LocalizedException */ private function getAttributeCode(array $value): string { if (!isset($value['attribute_code'])) { - throw new GraphQlInputException(__('"Attribute code should be specified')); + throw new LocalizedException(__('"Attribute code should be specified')); } return $value['attribute_code']; diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php b/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php index b1d7aae5df101..390279f2cfe2e 100644 --- a/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php +++ b/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php @@ -9,7 +9,6 @@ use Magento\Framework\Webapi\CustomAttributeTypeLocatorInterface; use Magento\Framework\Reflection\TypeProcessor; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; /** @@ -71,7 +70,7 @@ public function getType(string $attributeCode, string $entityType) : string try { $type = $this->typeProcessor->translateTypeName($type); } catch (\InvalidArgumentException $exception) { - throw new \LogicException('Cannot resolve EAV type'); + throw new GraphQlInputException(__('Cannot resolve EAV type')); } } else { $type = $type === 'double' ? 'float' : $type; diff --git a/app/code/Magento/GraphQl/Model/EntityAttributeList.php b/app/code/Magento/GraphQl/Model/EntityAttributeList.php index 6b8a4f477069e..3802b74f3ec13 100644 --- a/app/code/Magento/GraphQl/Model/EntityAttributeList.php +++ b/app/code/Magento/GraphQl/Model/EntityAttributeList.php @@ -14,7 +14,7 @@ use Magento\Framework\Api\MetadataServiceInterface; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; /** * Iterate through all attribute sets to retrieve attributes for any given entity type @@ -69,7 +69,7 @@ public function __construct( * @param string $entityCode * @param MetadataServiceInterface $metadataService * @return boolean[] - * @throws GraphQlInputException + * @throws GraphQlNoSuchEntityException */ public function getDefaultEntityAttributes( string $entityCode, @@ -93,7 +93,7 @@ public function getDefaultEntityAttributes( $this->attributeManagement->getAttributes($entityCode, $attributeSet->getAttributeSetId()) ); } catch (NoSuchEntityException $exception) { - throw new GraphQlInputException(__('Entity code %1 does not exist.', [$entityCode])); + throw new GraphQlNoSuchEntityException(__('Entity code %1 does not exist.', [$entityCode])); } } $attributeCodes = []; diff --git a/app/code/Magento/GroupedProductGraphQl/Model/Resolver/GroupedItems.php b/app/code/Magento/GroupedProductGraphQl/Model/Resolver/GroupedItems.php index fee4063affef1..d51b22ffe21df 100644 --- a/app/code/Magento/GroupedProductGraphQl/Model/Resolver/GroupedItems.php +++ b/app/code/Magento/GroupedProductGraphQl/Model/Resolver/GroupedItems.php @@ -7,7 +7,7 @@ namespace Magento\GroupedProductGraphQl\Model\Resolver; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Deferred\Product; use Magento\Framework\GraphQl\Config\Element\Field; @@ -44,7 +44,7 @@ public function resolve( array $args = null ) { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } $data = []; diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 0c4c78b582941..fb7bbd634d11f 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -7,7 +7,7 @@ namespace Magento\UrlRewriteGraphQl\Model\Resolver; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -45,7 +45,7 @@ public function resolve( array $args = null ): array { if (!isset($value['model'])) { - throw new GraphQlInputException(__('"model" value should be specified')); + throw new LocalizedException(__('"model" value should be specified')); } /** @var AbstractModel $entity */ From c498773af5dc671b511534d759645808c7ae04c3 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 15:28:46 +0200 Subject: [PATCH 298/310] GraphQL-39: Manage Shipping methods on Cart --- app/code/Magento/QuoteGraphQl/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/composer.json b/app/code/Magento/QuoteGraphQl/composer.json index 881450745b84a..1bf4d581a5fe3 100644 --- a/app/code/Magento/QuoteGraphQl/composer.json +++ b/app/code/Magento/QuoteGraphQl/composer.json @@ -9,7 +9,8 @@ "magento/module-checkout": "*", "magento/module-catalog": "*", "magento/module-store": "*", - "magento/module-customer": "*" + "magento/module-customer": "*", + "magento/module-customer-graph-ql": "*" }, "suggest": { "magento/module-graph-ql": "*" From 645cdd2fe463d0b200a2e87848eb79da3c716f8d Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 15:41:04 +0200 Subject: [PATCH 299/310] GraphQL-189: Replace GraphQlInputException on LocalizedException -- fix static tests --- .../Model/ProductLinkTypeResolverComposite.php | 2 +- .../Model/Resolver/Product/EntityIdToId.php | 4 ++-- .../Resolver/Product/MediaGalleryEntries.php | 11 ++++++++++- .../Model/Resolver/Product/NewFromTo.php | 10 +++++++++- .../Model/Resolver/Product/Options.php | 11 ++++++++++- .../Model/Resolver/Product/Price.php | 11 ++++++++++- .../Model/Resolver/Product/ProductLinks.php | 15 ++++++++++++--- .../Model/Resolver/Product/TierPrices.php | 15 ++++++++++++--- .../Resolver/Product/DownloadableOptions.php | 15 ++++++++++++--- .../Model/Resolver/AttributeOptions.php | 8 +++++++- .../EavGraphQl/Model/Resolver/Query/Type.php | 2 +- 11 files changed, 86 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php b/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php index fbce18dfb67a5..c1bf5c0b7bb1c 100644 --- a/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php +++ b/app/code/Magento/CatalogGraphQl/Model/ProductLinkTypeResolverComposite.php @@ -11,7 +11,7 @@ use Magento\Framework\GraphQl\Query\Resolver\TypeResolverInterface; /** - * {@inheritdoc} + * @inheritdoc */ class ProductLinkTypeResolverComposite implements TypeResolverInterface { diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php index 962256ceff442..b3e76ea78d183 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php @@ -16,9 +16,9 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * Fixed the id related data in the product data - * * {@inheritdoc} + * + * Fixed the id related data in the product data */ class EntityIdToId implements ResolverInterface { diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php index 785cd75585d5d..f9bb3071ff378 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php @@ -8,6 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Framework\GraphQl\Config\Element\Field; @@ -19,9 +20,17 @@ class MediaGalleryEntries implements ResolverInterface { /** + * {@inheritdoc} + * * Format product's media gallery entry data to conform to GraphQL schema * - * {@inheritdoc} + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return array */ public function resolve( Field $field, diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php index 88ae457c9986f..eb9f0d401e3a1 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php @@ -19,9 +19,17 @@ class NewFromTo implements ResolverInterface { /** + * {@inheritdoc} + * * Transfer data from legacy news_from_date and news_to_date to new names corespondent fields * - * {@inheritdoc} + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return null|array */ public function resolve( Field $field, diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php index cb53ebdab4810..bb386833b5b75 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php @@ -8,6 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Option; @@ -20,9 +21,17 @@ class Options implements ResolverInterface { /** + * {@inheritdoc} + * * Format product's option data to conform to GraphQL schema * - * {@inheritdoc} + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return null|array */ public function resolve( Field $field, diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php index 3bc9057f06b01..ca29b46aae9f7 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php @@ -8,6 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Catalog\Pricing\Price\FinalPrice; @@ -47,9 +48,17 @@ public function __construct( } /** + * {@inheritdoc} + * * Format product's tier price data to conform to GraphQL schema * - * {@inheritdoc} + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return array */ public function resolve( Field $field, diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php index b2a612d6ae02d..4a0fcfaa413fa 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php @@ -8,6 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ProductLink\Link; @@ -15,9 +16,9 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * Format the product links information to conform to GraphQL schema representation - * * {@inheritdoc} + * + * Format the product links information to conform to GraphQL schema representation */ class ProductLinks implements ResolverInterface { @@ -27,9 +28,17 @@ class ProductLinks implements ResolverInterface private $linkTypes = ['related', 'upsell', 'crosssell']; /** + * {@inheritdoc} + * * Format product links data to conform to GraphQL schema * - * {@inheritdoc} + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return null|array */ public function resolve( Field $field, diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php index cf1b621ad65b6..e4a1b2889d470 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php @@ -8,6 +8,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Product; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\TierPrice; @@ -15,16 +16,24 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * Format a product's tier price information to conform to GraphQL schema representation - * * {@inheritdoc} + * + * Format a product's tier price information to conform to GraphQL schema representation */ class TierPrices implements ResolverInterface { /** + * {@inheritdoc} + * * Format product's tier price data to conform to GraphQL schema * - * {@inheritdoc} + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return null|array */ public function resolve( Field $field, diff --git a/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php b/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php index 0842c96ef4b1a..4d0bdb4c61f3e 100644 --- a/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php +++ b/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php @@ -8,6 +8,7 @@ namespace Magento\DownloadableGraphQl\Model\Resolver\Product; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Catalog\Model\Product; use Magento\Downloadable\Helper\Data as DownloadableHelper; @@ -20,9 +21,9 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * Format for downloadable product types - * * {@inheritdoc} + * + * Format for downloadable product types */ class DownloadableOptions implements ResolverInterface { @@ -65,9 +66,17 @@ public function __construct( } /** + * {@inheritdoc} + * * Add downloadable options to configurable types * - * {@inheritdoc} + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return null|array */ public function resolve( Field $field, diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php index b0def15f3a0d4..e4c27adc60247 100644 --- a/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php +++ b/app/code/Magento/EavGraphQl/Model/Resolver/AttributeOptions.php @@ -47,7 +47,7 @@ public function __construct( } /** - * @inheritDoc + * @inheritdoc */ public function resolve( Field $field, @@ -67,6 +67,8 @@ public function resolve( } /** + * Get entity type + * * @param array $value * @return int * @throws LocalizedException @@ -81,6 +83,8 @@ private function getEntityType(array $value): int } /** + * Get attribute code + * * @param array $value * @return string * @throws LocalizedException @@ -95,6 +99,8 @@ private function getAttributeCode(array $value): string } /** + * Get attribute options data + * * @param int $entityType * @param string $attributeCode * @return array diff --git a/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php b/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php index 390279f2cfe2e..ef21a26f1f62e 100644 --- a/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php +++ b/app/code/Magento/EavGraphQl/Model/Resolver/Query/Type.php @@ -36,7 +36,7 @@ class Type /** * @param CustomAttributeTypeLocatorInterface $typeLocator * @param TypeProcessor $typeProcessor - * @param $customTypes + * @param array $customTypes */ public function __construct( CustomAttributeTypeLocatorInterface $typeLocator, From a54fed5ce35eb5d6c6d1c95b4b5b94dd37a111b1 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 16:15:33 +0200 Subject: [PATCH 300/310] GraphQL-189: Replace GraphQlInputException on LocalizedException -- fix static tests --- .../CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php | 2 +- .../Model/Resolver/Product/MediaGalleryEntries.php | 4 +++- .../CatalogGraphQl/Model/Resolver/Product/NewFromTo.php | 4 +++- .../Magento/CatalogGraphQl/Model/Resolver/Product/Options.php | 2 +- .../Magento/CatalogGraphQl/Model/Resolver/Product/Price.php | 2 +- .../CatalogGraphQl/Model/Resolver/Product/ProductLinks.php | 2 +- .../CatalogGraphQl/Model/Resolver/Product/TierPrices.php | 4 ++-- .../Model/Resolver/Product/DownloadableOptions.php | 4 ++-- 8 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php index b3e76ea78d183..ada3caad5f9f8 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php @@ -16,7 +16,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * {@inheritdoc} + * @inheritdoc * * Fixed the id related data in the product data */ diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php index f9bb3071ff378..c8f167da583d3 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGalleryEntries.php @@ -15,12 +15,14 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** + * @inheritdoc + * * Format a product's media gallery information to conform to GraphQL schema representation */ class MediaGalleryEntries implements ResolverInterface { /** - * {@inheritdoc} + * @inheritdoc * * Format product's media gallery entry data to conform to GraphQL schema * diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php index eb9f0d401e3a1..12016282a3081 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/NewFromTo.php @@ -14,12 +14,14 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** + * @inheritdoc + * * Format the new from and to typo of legacy fields news_from_date and news_to_date */ class NewFromTo implements ResolverInterface { /** - * {@inheritdoc} + * @inheritdoc * * Transfer data from legacy news_from_date and news_to_date to new names corespondent fields * diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php index bb386833b5b75..76602288039c5 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Options.php @@ -21,7 +21,7 @@ class Options implements ResolverInterface { /** - * {@inheritdoc} + * @inheritdoc * * Format product's option data to conform to GraphQL schema * diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php index ca29b46aae9f7..55d930101fb60 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/Price.php @@ -48,7 +48,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc * * Format product's tier price data to conform to GraphQL schema * diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php index 4a0fcfaa413fa..f72f873fc8e55 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php @@ -28,7 +28,7 @@ class ProductLinks implements ResolverInterface private $linkTypes = ['related', 'upsell', 'crosssell']; /** - * {@inheritdoc} + * @inheritdoc * * Format product links data to conform to GraphQL schema * diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php index e4a1b2889d470..726ef91c56880 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/TierPrices.php @@ -16,14 +16,14 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * {@inheritdoc} + * @inheritdoc * * Format a product's tier price information to conform to GraphQL schema representation */ class TierPrices implements ResolverInterface { /** - * {@inheritdoc} + * @inheritdoc * * Format product's tier price data to conform to GraphQL schema * diff --git a/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php b/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php index 4d0bdb4c61f3e..a1e25663a9c3d 100644 --- a/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php +++ b/app/code/Magento/DownloadableGraphQl/Model/Resolver/Product/DownloadableOptions.php @@ -21,7 +21,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * {@inheritdoc} + * @inheritdoc * * Format for downloadable product types */ @@ -66,7 +66,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc * * Add downloadable options to configurable types * From 39225d4aceee4f6f6cf2d8da09235333c8b8961d Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 16:21:09 +0200 Subject: [PATCH 301/310] GraphQL-39: Manage Shipping methods on Cart -- fixes after merge --- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 3e622f9fa5412..edc643973ce77 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -6,7 +6,7 @@ type Query { } type Mutation { - createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart\\CreateEmptyCart") @doc(description:"Creates an empty shopping cart for a guest or logged in user") + createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates an empty shopping cart for a guest or logged in user") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart") From 7f41544848b0496779a4a8d07ccb8869d9ef435b Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 16:23:51 +0200 Subject: [PATCH 302/310] GraphQL-189: Replace GraphQlInputException on LocalizedException -- fix static tests --- .../CatalogGraphQl/Model/Resolver/Product/ProductLinks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php index f72f873fc8e55..4d1b11a74b9d4 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductLinks.php @@ -16,7 +16,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; /** - * {@inheritdoc} + * @inheritdoc * * Format the product links information to conform to GraphQL schema representation */ From 6d26686321561fb0460f8024d89af2483a2dfda9 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 19 Nov 2018 17:35:28 +0200 Subject: [PATCH 303/310] MAGETWO-94424: Wrong product and shipping prices are applying on admin orders --- app/code/Magento/Store/Model/Store.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 32c9a78448428..c92d62cd0bbe3 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -412,7 +412,7 @@ public function __construct( } /** - * @return string[] + * @inheritdoc */ public function __sleep() { @@ -786,7 +786,7 @@ public function isFrontUrlSecure() } /** - * @return bool + * @inheritdoc */ public function isUrlSecure() { @@ -1338,6 +1338,8 @@ public function getIdentities() } /** + * Get store path + * * @return string */ public function getStorePath() @@ -1347,8 +1349,7 @@ public function getStorePath() } /** - * {@inheritdoc} - * @since 100.1.0 + * @inheritdoc */ public function getScopeType() { @@ -1356,8 +1357,7 @@ public function getScopeType() } /** - * {@inheritdoc} - * @since 100.1.0 + * @inheritdoc */ public function getScopeTypeName() { @@ -1365,7 +1365,7 @@ public function getScopeTypeName() } /** - * {@inheritdoc} + * @inheritdoc */ public function getExtensionAttributes() { @@ -1373,8 +1373,7 @@ public function getExtensionAttributes() } /** - * @param \Magento\Store\Api\Data\StoreExtensionInterface $extensionAttributes - * @return $this + * @inheritdoc */ public function setExtensionAttributes( \Magento\Store\Api\Data\StoreExtensionInterface $extensionAttributes From faa85353df4be28674a07d406ff87e422284b985 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 18:48:55 +0200 Subject: [PATCH 304/310] GraphQL-202: Products: Email to a Friend --- .../Model/Resolver/SendEmailToFriend.php | 179 +++++++++++------- .../Model/Validation/Validation.php | 69 ------- .../Magento/SendFriendGraphQl/composer.json | 26 +++ .../SendFriendGraphQl/etc/schema.graphqls | 9 +- composer.json | 1 + composer.lock | 2 +- 6 files changed, 145 insertions(+), 141 deletions(-) delete mode 100644 app/code/Magento/SendFriendGraphQl/Model/Validation/Validation.php create mode 100644 app/code/Magento/SendFriendGraphQl/composer.json diff --git a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php index 56f6d20b20f18..06c5da94819df 100644 --- a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php +++ b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php @@ -7,38 +7,60 @@ namespace Magento\SendFriendGraphQl\Model\Resolver; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\DataObjectFactory; +use Magento\Framework\Event\ManagerInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\SendFriend\Model\SendFriend; +use Magento\SendFriend\Model\SendFriendFactory; +/** + * @inheritdoc + */ class SendEmailToFriend implements ResolverInterface { /** - * @var SendFriend + * @var SendFriendFactory */ - private $sendFriend; + private $sendFriendFactory; + /** * @var ProductRepositoryInterface */ private $productRepository; + /** * @var DataObjectFactory */ private $dataObjectFactory; + /** + * @var ManagerInterface + */ + private $eventManager; + + /** + * @param SendFriendFactory $sendFriendFactory + * @param ProductRepositoryInterface $productRepository + * @param DataObjectFactory $dataObjectFactory + * @param ManagerInterface $eventManager + */ public function __construct( - SendFriend $sendFriend, + SendFriendFactory $sendFriendFactory, ProductRepositoryInterface $productRepository, - DataObjectFactory $dataObjectFactory + DataObjectFactory $dataObjectFactory, + ManagerInterface $eventManager ) { - $this->sendFriend = $sendFriend; + $this->sendFriendFactory = $sendFriendFactory; $this->productRepository = $productRepository; $this->dataObjectFactory = $dataObjectFactory; + $this->eventManager = $eventManager; } /** @@ -46,102 +68,131 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - if ($this->sendFriend->getMaxSendsToFriend() && $this->sendFriend->isExceedLimit()) { + /** @var SendFriend $sendFriend */ + $sendFriend = $this->sendFriendFactory->create(); + + if ($sendFriend->getMaxSendsToFriend() && $sendFriend->isExceedLimit()) { throw new GraphQlInputException(__('You can\'t send messages more than %1 times an hour.', - $this->sendFriend->getMaxSendsToFriend() + $sendFriend->getMaxSendsToFriend() )); } - $product = $this->getProductFromRepository($args['input']['params']['product_id']); - $senderArray = $this->getSenderArrayFromArgs($args); - $recipientsArray = $this->getRecipientsArray($args); - //@todo clarify if event should be dispatched - //$this->_eventManager->dispatch('sendfriend_product', ['product' => $product]); - $this->sendFriend->setSender($senderArray); - $this->sendFriend->setRecipients($recipientsArray); - $this->sendFriend->setProduct($product); - - $this->prepareDataForValidation($args, $recipientsArray); - $validationResult = $this->sendFriend->validate(); - $this->addRecipientNameValidation($args); - if ($validationResult !== true) { - throw new GraphQlInputException(__(implode($validationResult))); - } + $product = $this->getProduct($args['input']['product_id']); + $this->eventManager->dispatch('sendfriend_product', ['product' => $product]); + $sendFriend->setProduct($product); - $this->sendFriend->send(); + $senderData = $this->extractSenderData($args); + $sendFriend->setSender($senderData); - return array_merge($senderArray, $recipientsArray); - } + $recipientsData = $this->extractRecipientsData($args); + $sendFriend->setRecipients($recipientsData); - private function prepareDataForValidation(array $args, array $recipientsArray): void - { - $sender = $this->dataObjectFactory->create()->setData([ - 'name' => $args['input']['sender']['name'], - 'email'=> $args['input']['sender']['email'], - 'message' => $args['input']['sender']['message'], - ]); - $emails = []; - foreach ($recipientsArray['recipients'] as $recipient) { - $emails[] = $recipient['email']; - } - $recipients = $this->dataObjectFactory->create()->setData('emails', $emails); + $this->validateSendFriendModel($sendFriend, $senderData, $recipientsData); + $sendFriend->send(); - $this->sendFriend->setData('_sender', $sender); - $this->sendFriend->setData('_recipients', $recipients); + return array_merge($senderData, $recipientsData); } /** - * @param array $args + * Validate send friend model + * + * @param SendFriend $sendFriend + * @param array $senderData + * @param array $recipientsData + * @return void * @throws GraphQlInputException */ - private function addRecipientNameValidation(array $args): void + private function validateSendFriendModel(SendFriend $sendFriend, array $senderData, array $recipientsData): void { - foreach ($args['input']['recipients'] as $recipient) { - if (empty($recipient['name'])) { - throw new GraphQlInputException( - __('Please Provide Name for Recipient with this Email Address: ' . $recipient['email'] - )); - } + $sender = $this->dataObjectFactory->create()->setData($senderData['sender']); + $sendFriend->setData('_sender', $sender); + + $emails = array_column($recipientsData['recipients'], 'email'); + $recipients = $this->dataObjectFactory->create()->setData('emails', $emails); + $sendFriend->setData('_recipients', $recipients); + + $validationResult = $sendFriend->validate(); + if ($validationResult !== true) { + throw new GraphQlInputException(__(implode($validationResult))); } } /** + * Get product + * * @param int $productId - * @return bool|\Magento\Catalog\Api\Data\ProductInterface + * @return ProductInterface + * @throws GraphQlNoSuchEntityException */ - private function getProductFromRepository(int $productId) + private function getProduct(int $productId): ProductInterface { try { $product = $this->productRepository->getById($productId); if (!$product->isVisibleInCatalog()) { - return false; + throw new GraphQlNoSuchEntityException( + __("The product that was requested doesn't exist. Verify the product and try again.") + ); } - } catch (NoSuchEntityException $noEntityException) { - return false; + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); } - return $product; } - private function getRecipientsArray(array $args): array + /** + * Extract recipients data + * + * @param array $args + * @return array + * @throws GraphQlInputException + */ + private function extractRecipientsData(array $args): array { - $recipientsArray = []; + $recipients = []; foreach ($args['input']['recipients'] as $recipient) { - $recipientsArray[] = [ + if (empty($recipient['name'])) { + throw new GraphQlInputException(__('Please provide Name for all of recipients.')); + } + + if (empty($recipient['email'])) { + throw new GraphQlInputException(__('Please provide Email for all of recipients.')); + } + + $recipients[] = [ 'name' => $recipient['name'], 'email' => $recipient['email'], ]; } - return ['recipients' => $recipientsArray]; + return ['recipients' => $recipients]; } - private function getSenderArrayFromArgs(array $args): array + /** + * Extract sender data + * + * @param array $args + * @return array + * @throws GraphQlInputException + */ + private function extractSenderData(array $args): array { - return ['sender' => [ - 'name' => $args['input']['sender']['name'], - 'email' => $args['input']['sender']['email'], - 'message' => $args['input']['sender']['message'], - ] - ]; + if (empty($args['input']['sender']['name'])) { + throw new GraphQlInputException(__('Please provide Name of sender.')); + } + + if (empty($args['input']['sender']['email'])) { + throw new GraphQlInputException(__('Please provide Email of sender.')); + } + + if (empty($args['input']['sender']['message'])) { + throw new GraphQlInputException(__('Please provide Message.')); + } + + return [ + 'sender' => [ + 'name' => $args['input']['sender']['name'], + 'email' => $args['input']['sender']['email'], + 'message' => $args['input']['sender']['message'], + ], + ]; } } diff --git a/app/code/Magento/SendFriendGraphQl/Model/Validation/Validation.php b/app/code/Magento/SendFriendGraphQl/Model/Validation/Validation.php deleted file mode 100644 index 49f7feeaba489..0000000000000 --- a/app/code/Magento/SendFriendGraphQl/Model/Validation/Validation.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\SendFriendGraphQl\Model\Validation; - -use Magento\Framework\DataObjectFactory; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\SendFriend\Model\SendFriend; - -class Validation -{ - /** - * @var SendFriend - */ - private $sendFriend; - /** - * @var DataObjectFactory - */ - private $dataObjectFactory; - - public function __construct( - DataObjectFactory $dataObjectFactory, - SendFriend $sendFriend - ) { - $this->sendFriend = $sendFriend; - $this->dataObjectFactory = $dataObjectFactory; - } - - /** - * @param $args - * @param array $recipientsArray - * @throws GraphQlInputException - */ - public function validate($args, array $recipientsArray): void - { - $this->prepareDataForSendFriendValidation($args, $recipientsArray); - $validationResult = $this->sendFriend->validate(); - if ($validationResult !== true) { - throw new GraphQlInputException(__(implode($validationResult))); - } - if ($this->sendFriend->getMaxSendsToFriend() && $this->sendFriend->isExceedLimit()) { - throw new GraphQlInputException(__('You can\'t send messages more than %1 times an hour.', - $this->sendFriend->getMaxSendsToFriend() - )); - } - } - - private function prepareDataForSendFriendValidation(array $args, array $recipientsArray): void - { - $sender = $this->dataObjectFactory->create()->setData([ - 'name' => $args['input']['sender']['name'], - 'email'=> $args['input']['sender']['email'], - 'message' => $args['input']['sender']['message'], - ]); - $emails = []; - foreach ($recipientsArray['recipients'] as $recipient) { - $emails[] = $recipient['email']; - } - $recipients = $this->dataObjectFactory->create()->setData('emails', $emails); - - $this->sendFriend->setData('_sender', $sender); - $this->sendFriend->setData('_recipients', $recipients); - } - -} \ No newline at end of file diff --git a/app/code/Magento/SendFriendGraphQl/composer.json b/app/code/Magento/SendFriendGraphQl/composer.json new file mode 100644 index 0000000000000..10d7e63bfc770 --- /dev/null +++ b/app/code/Magento/SendFriendGraphQl/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-send-friend-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-catalog": "*", + "magento/module-send-friend": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\SendFriendGraphGl\\": "" + } + } +} diff --git a/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls b/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls index 4123387650838..3915d48681ab1 100644 --- a/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SendFriendGraphQl/etc/schema.graphqls @@ -6,9 +6,9 @@ type Mutation { } input SendEmailToFriendSenderInput { + product_id: Int! sender: Sender! - params: Params! - recipients: [Recipient] + recipients: [Recipient!]! } type Sender { @@ -16,17 +16,12 @@ type Sender { email: String! message: String! } -type Params { - product_id: Int! - category_id: Int! -} type Recipient { name: String! email: String! } - type SendEmailToFriendOutput { sender: Sender recipients: [Recipient] diff --git a/composer.json b/composer.json index 3f8f0a033c893..99f5beeb9c7e7 100644 --- a/composer.json +++ b/composer.json @@ -208,6 +208,7 @@ "magento/module-search": "*", "magento/module-security": "*", "magento/module-send-friend": "*", + "magento/module-send-friend-graph-ql": "*", "magento/module-shipping": "*", "magento/module-signifyd": "*", "magento/module-sitemap": "*", diff --git a/composer.lock b/composer.lock index 1d101c8aaaf15..a3e7a72e69026 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d6640ddfd342feceaec44c406c056f57", + "content-hash": "886f5497f18a46a42d05cc672f1b8f2f", "packages": [ { "name": "braintree/braintree_php", From b6e07229fbd610a6617d47ab4c73cae0c335bb8f Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 18:53:31 +0200 Subject: [PATCH 305/310] GraphQL-202: Products: Email to a Friend --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 36b4dca023191..f26b71f1cc392 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7538e15ea4ff1378cf55983ce9acba82", + "content-hash": "93418bd14ad2d9f54f4059550ded7061", "packages": [ { "name": "braintree/braintree_php", From a653c2baf540fa0a7c42f84a8d72e9228aaeb9d4 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 20:18:31 +0200 Subject: [PATCH 306/310] GraphQL-202: Products: Email to a Friend --- app/code/Magento/SendFriendGraphQl/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 app/code/Magento/SendFriendGraphQl/README.md diff --git a/app/code/Magento/SendFriendGraphQl/README.md b/app/code/Magento/SendFriendGraphQl/README.md new file mode 100644 index 0000000000000..d8051922ddad7 --- /dev/null +++ b/app/code/Magento/SendFriendGraphQl/README.md @@ -0,0 +1,3 @@ +# SendFriendGraphQl + +**SendFriendGraphQl** provides support of GraphQL for SendFriend functionality. From 0bdc2fd3918e9fbd6ab48dc5cd97b270b17d163a Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 20:35:53 +0200 Subject: [PATCH 307/310] GraphQL-202: Products: Email to a Friend -- fix static tests --- .../SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php | 3 ++- app/code/Magento/SendFriendGraphQl/registration.php | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php index 06c5da94819df..2b183ca209c0c 100644 --- a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php +++ b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php @@ -72,7 +72,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $sendFriend = $this->sendFriendFactory->create(); if ($sendFriend->getMaxSendsToFriend() && $sendFriend->isExceedLimit()) { - throw new GraphQlInputException(__('You can\'t send messages more than %1 times an hour.', + throw new GraphQlInputException( + __('You can\'t send messages more than %1 times an hour.', $sendFriend->getMaxSendsToFriend() )); } diff --git a/app/code/Magento/SendFriendGraphQl/registration.php b/app/code/Magento/SendFriendGraphQl/registration.php index c41607a0dc864..5988738268147 100644 --- a/app/code/Magento/SendFriendGraphQl/registration.php +++ b/app/code/Magento/SendFriendGraphQl/registration.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); use Magento\Framework\Component\ComponentRegistrar; From b6d9adeed9bd275e0e819c33af35dff134b17834 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 20:40:10 +0200 Subject: [PATCH 308/310] GraphQL-202: Products: Email to a Friend -- fix static tests --- app/code/Magento/SendFriendGraphQl/composer.json | 2 +- app/code/Magento/SendFriendGraphQl/etc/module.xml | 2 +- app/code/Magento/SendFriendGraphQl/registration.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/SendFriendGraphQl/composer.json b/app/code/Magento/SendFriendGraphQl/composer.json index 10d7e63bfc770..d401f57b2257a 100644 --- a/app/code/Magento/SendFriendGraphQl/composer.json +++ b/app/code/Magento/SendFriendGraphQl/composer.json @@ -20,7 +20,7 @@ "registration.php" ], "psr-4": { - "Magento\\SendFriendGraphGl\\": "" + "Magento\\SendFriendGraphQl\\": "" } } } diff --git a/app/code/Magento/SendFriendGraphQl/etc/module.xml b/app/code/Magento/SendFriendGraphQl/etc/module.xml index 14d792198cd5f..3df33266cac6e 100644 --- a/app/code/Magento/SendFriendGraphQl/etc/module.xml +++ b/app/code/Magento/SendFriendGraphQl/etc/module.xml @@ -7,5 +7,5 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_SendFriendGraphGl"/> + <module name="Magento_SendFriendGraphQl"/> </config> diff --git a/app/code/Magento/SendFriendGraphQl/registration.php b/app/code/Magento/SendFriendGraphQl/registration.php index 5988738268147..13ec47b16abdb 100644 --- a/app/code/Magento/SendFriendGraphQl/registration.php +++ b/app/code/Magento/SendFriendGraphQl/registration.php @@ -7,4 +7,4 @@ use Magento\Framework\Component\ComponentRegistrar; -ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SendFriendGraphGl', __DIR__); +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SendFriendGraphQl', __DIR__); From 05c1c8a02790974b5574f66a3ba81f721c325785 Mon Sep 17 00:00:00 2001 From: Valeriy Nayda <vnayda@magento.com> Date: Mon, 19 Nov 2018 21:00:42 +0200 Subject: [PATCH 309/310] GraphQL-202: Products: Email to a Friend -- fix static tests --- .../SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php index 2b183ca209c0c..c0c01c71df764 100644 --- a/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php +++ b/app/code/Magento/SendFriendGraphQl/Model/Resolver/SendEmailToFriend.php @@ -73,9 +73,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if ($sendFriend->getMaxSendsToFriend() && $sendFriend->isExceedLimit()) { throw new GraphQlInputException( - __('You can\'t send messages more than %1 times an hour.', - $sendFriend->getMaxSendsToFriend() - )); + __('You can\'t send messages more than %1 times an hour.', $sendFriend->getMaxSendsToFriend()) + ); } $product = $this->getProduct($args['input']['product_id']); From 3694c65e95fa12258afdfc561ff29ad23fb00969 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 20 Nov 2018 12:12:37 +0100 Subject: [PATCH 310/310] Added model to category data upon the hydration --- app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php b/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php index e783c749fdc6e..d2c1fc8f7be9f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/Hydrator.php @@ -56,6 +56,7 @@ public function hydrateCategory(Category $category, $basicFieldsOnly = false) : $categoryData['id'] = $category->getId(); $categoryData['children'] = []; $categoryData['available_sort_by'] = $category->getAvailableSortBy(); + $categoryData['model'] = $category; return $this->flattener->flatten($categoryData); } }