diff --git a/.travis.yml b/.travis.yml index 95eb5ecb62bca..74e374412fff4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ addons: - postfix language: php php: - - 5.6 + - 5.6.29 - 7.0 env: global: diff --git a/app/code/Magento/Captcha/Test/Unit/Helper/DataTest.php b/app/code/Magento/Captcha/Test/Unit/Helper/DataTest.php index 93379cff52f2c..40558b3cae8a5 100644 --- a/app/code/Magento/Captcha/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Helper/DataTest.php @@ -53,10 +53,6 @@ protected function setUp() */ public function testGetCaptcha() { - if (!function_exists("imageftbbox")) { - $this->markTestSkipped('imageftbbox is not available on the test environment'); - } - $this->configMock->expects( $this->once() )->method( diff --git a/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php b/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php index 26984af3df32c..649bce192614b 100644 --- a/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Model/DefaultTest.php @@ -88,9 +88,6 @@ class DefaultTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - if (!function_exists("imageftbbox")) { - $this->markTestSkipped('imageftbbox is not available on the test environment'); - } $this->session = $this->_getSessionStub(); $this->_storeManager = $this->getMock( diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php index c4d8e4fd9be34..3a6f833a5a09c 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php @@ -8,6 +8,8 @@ namespace Magento\Catalog\Model\Indexer\Category\Product; +use Magento\Framework\DB\Query\BatchIteratorInterface as BatchIteratorInterface; +use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; @@ -102,20 +104,29 @@ abstract class AbstractAction */ protected $tempTreeIndexTableName; + /** + * @var QueryGenerator + */ + private $queryGenerator; + /** * @param ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Model\Config $config + * @param QueryGenerator $queryGenerator */ public function __construct( \Magento\Framework\App\ResourceConnection $resource, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Catalog\Model\Config $config + \Magento\Catalog\Model\Config $config, + QueryGenerator $queryGenerator = null ) { $this->resource = $resource; $this->connection = $resource->getConnection(); $this->storeManager = $storeManager; $this->config = $config; + $this->queryGenerator = $queryGenerator ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(QueryGenerator::class); } /** @@ -309,15 +320,26 @@ protected function isRangingNeeded() * @param int $range * @return \Magento\Framework\DB\Select[] */ - protected function prepareSelectsByRange(\Magento\Framework\DB\Select $select, $field, $range = self::RANGE_CATEGORY_STEP) - { - return $this->isRangingNeeded() ? $this->connection->selectsByRange( - $field, - $select, - $range - ) : [ - $select - ]; + protected function prepareSelectsByRange( + \Magento\Framework\DB\Select $select, + $field, + $range = self::RANGE_CATEGORY_STEP + ) { + if($this->isRangingNeeded()) { + $iterator = $this->queryGenerator->generate( + $field, + $select, + $range, + \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR + ); + + $queries = []; + foreach ($iterator as $query) { + $queries[] = $query; + } + return $queries; + } + return [$select]; } /** diff --git a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php index 917a47b637cea..eb4e385993ac2 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php +++ b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php @@ -48,9 +48,21 @@ public function getCollection(\Magento\Catalog\Model\Product $product, $type) $products = $this->providers[$type]->getLinkedProducts($product); $converter = $this->converterPool->getConverter($type); $output = []; + $sorterItems = []; foreach ($products as $item) { $output[$item->getId()] = $converter->convert($item); } - return $output; + + foreach ($output as $item) { + $itemPosition = $item['position']; + if (!isset($sorterItems[$itemPosition])) { + $sorterItems[$itemPosition] = $item; + } else { + $newPosition = $itemPosition + 1; + $sorterItems[$newPosition] = $item; + } + } + ksort($sorterItems); + return $sorterItems; } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php new file mode 100644 index 0000000000000..b543c92ac0875 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php @@ -0,0 +1,110 @@ +productMock = $this->getMock(Product::class, [], [], '', false, false); + $this->converterPoolMock = $this->getMock(ConverterPool::class, [], [], '', false, false); + $this->providerMock = $this->getMock(CollectionProviderInterface::class); + $this->converterMock = $this->getMock(ConverterInterface::class); + + $this->model = new CollectionProvider($this->converterPoolMock, ['crosssell' => $this->providerMock]); + } + + /** + * Test sort order of linked products based on configured item position. + */ + public function testGetCollection() + { + $linkedProductOneMock = $this->getMock(Product::class, [], [], '', false, false); + $linkedProductTwoMock = $this->getMock(Product::class, [], [], '', false, false); + $linkedProductThreeMock = $this->getMock(Product::class, [], [], '', false, false); + + $linkedProductOneMock->expects($this->once())->method('getId')->willReturn(1); + $linkedProductTwoMock->expects($this->once())->method('getId')->willReturn(2); + $linkedProductThreeMock->expects($this->once())->method('getId')->willReturn(3); + + $this->converterPoolMock->expects($this->once()) + ->method('getConverter') + ->with('crosssell') + ->willReturn($this->converterMock); + + $map = [ + [$linkedProductOneMock, ['name' => 'Product One', 'position' => 10]], + [$linkedProductTwoMock, ['name' => 'Product Two', 'position' => 2]], + [$linkedProductThreeMock, ['name' => 'Product Three', 'position' => 2]], + ]; + + $this->converterMock->expects($this->exactly(3))->method('convert')->willReturnMap($map); + + $this->providerMock->expects($this->once()) + ->method('getLinkedProducts') + ->with($this->productMock) + ->willReturn( + [ + $linkedProductOneMock, + $linkedProductTwoMock, + $linkedProductThreeMock + ] + ); + + $expectedResult = [ + 2 => ['name' => 'Product Two', 'position' => 2], + 3 => ['name' => 'Product Three', 'position' => 2], + 10 => ['name' => 'Product One', 'position' => 10], + ]; + + $actualResult = $this->model->getCollection($this->productMock, 'crosssell'); + + $this->assertEquals($expectedResult, $actualResult, 'Sort order of linked products in incorrect'); + } + + /** + * Test exception when collection provider is not configured for product link type. + * + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Collection provider is not registered + */ + public function testGetCollectionWithMissingProviders() + { + $this->model->getCollection($this->productMock, 'upsell'); + } +} diff --git a/app/code/Magento/Catalog/etc/eav_attributes.xml b/app/code/Magento/Catalog/etc/eav_attributes.xml index 133849a28e048..005402937232f 100644 --- a/app/code/Magento/Catalog/etc/eav_attributes.xml +++ b/app/code/Magento/Catalog/etc/eav_attributes.xml @@ -30,6 +30,7 @@ + diff --git a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php index 1b5e4f54f552c..7e52e2bdb4702 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Wysiwyg/Images/Tree.php @@ -26,20 +26,30 @@ class Tree extends \Magento\Backend\Block\Template */ protected $_cmsWysiwygImages = null; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Cms\Helper\Wysiwyg\Images $cmsWysiwygImages * @param \Magento\Framework\Registry $registry * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @throws \RuntimeException */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Cms\Helper\Wysiwyg\Images $cmsWysiwygImages, \Magento\Framework\Registry $registry, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { $this->_coreRegistry = $registry; $this->_cmsWysiwygImages = $cmsWysiwygImages; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); parent::__construct($context, $data); } @@ -65,7 +75,7 @@ public function getTreeJson() 'cls' => 'folder', ]; } - return \Zend_Json::encode($jsonArray); + return $this->serializer->serialize($jsonArray); } /** diff --git a/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php b/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php index a0f94e1b8f733..eefec4c35679c 100644 --- a/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php +++ b/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php @@ -15,16 +15,26 @@ class AuthenticationPopup extends \Magento\Framework\View\Element\Template */ protected $jsLayout; + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param array $data + * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @throws \RuntimeException */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { parent::__construct($context, $data); $this->jsLayout = isset($data['jsLayout']) && is_array($data['jsLayout']) ? $data['jsLayout'] : []; + $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\Json::class); } /** @@ -32,7 +42,7 @@ public function __construct( */ public function getJsLayout() { - return \Zend_Json::encode($this->jsLayout); + return $this->serializer->serialize($this->jsLayout); } /** @@ -50,6 +60,18 @@ public function getConfig() ]; } + /** + * Returns popup config in JSON format. + * + * Added in scope of https://github.com/magento/magento2/pull/8617 + * + * @return bool|string + */ + public function getSerializedConfig() + { + return $this->serializer->serialize($this->getConfig()); + } + /** * Is autocomplete enabled for storefront * diff --git a/app/code/Magento/Customer/Test/Unit/Block/Account/AuthenticationPopupTest.php b/app/code/Magento/Customer/Test/Unit/Block/Account/AuthenticationPopupTest.php index 67a3fb8f3a576..40f770ab9fbd4 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Account/AuthenticationPopupTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Account/AuthenticationPopupTest.php @@ -31,6 +31,9 @@ class AuthenticationPopupTest extends \PHPUnit_Framework_TestCase /** @var UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ private $urlBuilderMock; + /** @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject */ + private $serializerMock; + protected function setUp() { $this->contextMock = $this->getMockBuilder(Context::class) @@ -72,8 +75,13 @@ function ($string) { ->method('getEscaper') ->willReturn($escaperMock); + $this->serializerMock = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) + ->getMock(); + $this->model = new AuthenticationPopup( - $this->contextMock + $this->contextMock, + [], + $this->serializerMock ); } @@ -83,6 +91,7 @@ function ($string) { * @param string $registerUrl * @param string $forgotUrl * @param array $result + * @throws \PHPUnit_Framework_Exception * * @dataProvider dataProviderGetConfig */ @@ -172,4 +181,51 @@ public function dataProviderGetConfig() ], ]; } + + /** + * @param mixed $isAutocomplete + * @param string $baseUrl + * @param string $registerUrl + * @param string $forgotUrl + * @param array $result + * @throws \PHPUnit_Framework_Exception + * + * @dataProvider dataProviderGetConfig + */ + public function testGetSerializedConfig($isAutocomplete, $baseUrl, $registerUrl, $forgotUrl, array $result) + { + $this->scopeConfigMock->expects($this->any()) + ->method('getValue') + ->with(Form::XML_PATH_ENABLE_AUTOCOMPLETE, ScopeInterface::SCOPE_STORE, null) + ->willReturn($isAutocomplete); + + /** @var StoreInterface||\PHPUnit_Framework_MockObject_MockObject $storeMock */ + $storeMock = $this->getMockBuilder(StoreInterface::class) + ->setMethods(['getBaseUrl']) + ->getMockForAbstractClass(); + + $this->storeManagerMock->expects($this->any()) + ->method('getStore') + ->with(null) + ->willReturn($storeMock); + + $storeMock->expects($this->any()) + ->method('getBaseUrl') + ->willReturn($baseUrl); + + $this->urlBuilderMock->expects($this->any()) + ->method('getUrl') + ->willReturnMap( + [ + ['customer/account/create', [], $registerUrl], + ['customer/account/forgotpassword', [], $forgotUrl], + ] + ); + $this->serializerMock->expects($this->any())->method('serialize') + ->willReturn( + json_encode($this->model->getConfig()) + ); + + $this->assertEquals(json_encode($result), $this->model->getSerializedConfig()); + } } diff --git a/app/code/Magento/Customer/view/frontend/templates/account/authentication-popup.phtml b/app/code/Magento/Customer/view/frontend/templates/account/authentication-popup.phtml index bcf0cc92bc912..20ea488931398 100644 --- a/app/code/Magento/Customer/view/frontend/templates/account/authentication-popup.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/account/authentication-popup.phtml @@ -10,7 +10,7 @@ ?>