diff --git a/app/code/Magento/Backend/Block/Store/Switcher.php b/app/code/Magento/Backend/Block/Store/Switcher.php index 21c066fbef056..1dccfa05b1757 100644 --- a/app/code/Magento/Backend/Block/Store/Switcher.php +++ b/app/code/Magento/Backend/Block/Store/Switcher.php @@ -155,11 +155,7 @@ public function getWebsites() { $websites = $this->_storeManager->getWebsites(); if ($websiteIds = $this->getWebsiteIds()) { - foreach (array_keys($websites) as $websiteId) { - if (!in_array($websiteId, $websiteIds)) { - unset($websites[$websiteId]); - } - } + $websites = array_intersect_key($websites, array_flip($websiteIds)); } return $websites; } diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php index 7dddeba08a304..fc8ddffb3a916 100644 --- a/app/code/Magento/Backend/Model/Auth/Session.php +++ b/app/code/Magento/Backend/Model/Auth/Session.php @@ -177,14 +177,10 @@ public function isLoggedIn() */ public function prolong() { - $lifetime = $this->_config->getValue(self::XML_PATH_SESSION_LIFETIME); - $currentTime = time(); - - $this->setUpdatedAt($currentTime); $cookieValue = $this->cookieManager->getCookie($this->getName()); if ($cookieValue) { + $this->setUpdatedAt(time()); $cookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata() - ->setDuration($lifetime) ->setPath($this->sessionConfig->getCookiePath()) ->setDomain($this->sessionConfig->getCookieDomain()) ->setSecure($this->sessionConfig->getCookieSecure()) diff --git a/app/code/Magento/Backend/Test/Unit/Block/Store/SwitcherTest.php b/app/code/Magento/Backend/Test/Unit/Block/Store/SwitcherTest.php new file mode 100644 index 0000000000000..a4ba16ea1bdaa --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Block/Store/SwitcherTest.php @@ -0,0 +1,53 @@ +storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class); + $objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $context = $objectHelper->getObject( + \Magento\Backend\Block\Template\Context::class, + [ + 'storeManager' => $this->storeManagerMock, + ] + ); + + $this->switcherBlock = $objectHelper->getObject( + \Magento\Backend\Block\Store\Switcher::class, + ['context' => $context] + ); + } + + public function testGetWebsites() + { + $websiteMock = $this->getMock(\Magento\Store\Model\Website::class, [], [], '', false); + $websites = [0 => $websiteMock, 1 => $websiteMock]; + $this->storeManagerMock->expects($this->once())->method('getWebsites')->will($this->returnValue($websites)); + $this->assertEquals($websites, $this->switcherBlock->getWebsites()); + } + + public function testGetWebsitesIfSetWebsiteIds() + { + $websiteMock = $this->getMock(\Magento\Store\Model\Website::class, [], [], '', false); + $websites = [0 => $websiteMock, 1 => $websiteMock]; + $this->storeManagerMock->expects($this->once())->method('getWebsites')->will($this->returnValue($websites)); + + $this->switcherBlock->setWebsiteIds([1]); + $expected = [1 => $websiteMock]; + $this->assertEquals($expected, $this->switcherBlock->getWebsites()); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php b/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php index b05e8b31f390c..e12e8b55f38b0 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php @@ -164,17 +164,11 @@ public function testProlong() { $name = session_name(); $cookie = 'cookie'; - $lifetime = 900; $path = '/'; $domain = 'magento2'; $secure = true; $httpOnly = true; - $cookieMetadata = $this->getMock('Magento\Framework\Stdlib\Cookie\PublicCookieMetadata'); - $cookieMetadata->expects($this->once()) - ->method('setDuration') - ->with($lifetime) - ->will($this->returnSelf()); $cookieMetadata->expects($this->once()) ->method('setPath') ->with($path) @@ -191,11 +185,9 @@ public function testProlong() ->method('setHttpOnly') ->with($httpOnly) ->will($this->returnSelf()); - $this->cookieMetadataFactory->expects($this->once()) ->method('createPublicCookieMetadata') ->will($this->returnValue($cookieMetadata)); - $this->cookieManager->expects($this->once()) ->method('getCookie') ->with($name) @@ -203,11 +195,6 @@ public function testProlong() $this->cookieManager->expects($this->once()) ->method('setPublicCookie') ->with($name, $cookie, $cookieMetadata); - - $this->config->expects($this->once()) - ->method('getValue') - ->with(\Magento\Backend\Model\Auth\Session::XML_PATH_SESSION_LIFETIME) - ->will($this->returnValue($lifetime)); $this->sessionConfig->expects($this->once()) ->method('getCookiePath') ->will($this->returnValue($path)); @@ -220,9 +207,7 @@ public function testProlong() $this->sessionConfig->expects($this->once()) ->method('getCookieHttpOnly') ->will($this->returnValue($httpOnly)); - $this->session->prolong(); - $this->assertLessThanOrEqual(time(), $this->session->getUpdatedAt()); } diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php index 5d8d7c2bba8a1..9a289fdc0c9cc 100644 --- a/app/code/Magento/Bundle/Model/Product/Type.php +++ b/app/code/Magento/Bundle/Model/Product/Type.php @@ -716,6 +716,12 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p if (!empty($selectionIds)) { $selections = $this->getSelectionsByIds($selectionIds, $product); + if (count($selections->getItems()) !== count($selectionIds)) { + throw new \Magento\Framework\Exception\LocalizedException( + __('The options you selected are not available.') + ); + } + // Check if added selections are still on sale $this->checkSelectionsIsSale( $selections, @@ -888,12 +894,6 @@ public function getSelectionsByIds($selectionIds, $product) ->addFilterByRequiredOptions() ->setSelectionIdsFilter($selectionIds); - if (count($usedSelections->getItems()) !== count($selectionIds)) { - throw new \Magento\Framework\Exception\LocalizedException( - __('The options you selected are not available.') - ); - } - if (!$this->_catalogData->isPriceGlobal() && $storeId) { $websiteId = $this->_storeManager->getStore($storeId) ->getWebsiteId(); diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php index 106c9879fbb7b..c60c0f33d189c 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php @@ -377,7 +377,7 @@ function ($key) use ($optionCollection, $selectionCollection) { $resultValue = $selectionCollection; break; case '_cache_instance_used_selections_ids': - $resultValue = [2, 5, 14]; + $resultValue = [5]; break; } @@ -402,7 +402,7 @@ function ($key) use ($optionCollection, $selectionCollection) { ->method('setStoreFilter'); $buyRequest->expects($this->once()) ->method('getBundleOption') - ->willReturn([3 => 5, 10 => [7 => 2, 11 => 14]]); + ->willReturn([3 => 5]); $selectionCollection->expects($this->any()) ->method('getItems') ->willReturn([$selection]); @@ -431,13 +431,13 @@ function ($key) use ($optionCollection, $selectionCollection) { ->willReturn($productType); $option->expects($this->at(3)) ->method('getId') - ->willReturn(10); + ->willReturn(3); $option->expects($this->at(9)) ->method('getId') - ->willReturn(10); + ->willReturn(3); $option->expects($this->once()) ->method('getRequired') - ->willReturn(true); + ->willReturn(false); $option->expects($this->once()) ->method('isMultiSelection') ->willReturn(true); @@ -608,7 +608,7 @@ function ($key) use ($optionCollection, $selectionCollection) { $resultValue = $selectionCollection; break; case '_cache_instance_used_selections_ids': - $resultValue = [2, 5, 14]; + $resultValue = [5]; break; } @@ -633,7 +633,7 @@ function ($key) use ($optionCollection, $selectionCollection) { ->method('setStoreFilter'); $buyRequest->expects($this->once()) ->method('getBundleOption') - ->willReturn([3 => 5, 10 => [7 => 2, 11 => 14]]); + ->willReturn([3 => 5]); $selectionCollection->expects($this->any()) ->method('getItems') ->willReturn([$selection]); @@ -662,13 +662,13 @@ function ($key) use ($optionCollection, $selectionCollection) { ->willReturn($productType); $option->expects($this->at(3)) ->method('getId') - ->willReturn(10); + ->willReturn(3); $option->expects($this->at(9)) ->method('getId') - ->willReturn(10); + ->willReturn(3); $option->expects($this->once()) ->method('getRequired') - ->willReturn(true); + ->willReturn(false); $option->expects($this->once()) ->method('isMultiSelection') ->willReturn(true); @@ -827,7 +827,7 @@ function ($key) use ($optionCollection, $selectionCollection) { $resultValue = $selectionCollection; break; case '_cache_instance_used_selections_ids': - $resultValue = [2, 5, 14]; + $resultValue = [5]; break; } @@ -852,7 +852,7 @@ function ($key) use ($optionCollection, $selectionCollection) { ->method('setStoreFilter'); $buyRequest->expects($this->once()) ->method('getBundleOption') - ->willReturn([3 => 5, 10 => [7 => 2, 11 => 14]]); + ->willReturn([3 => 5]); $selectionCollection->expects($this->any()) ->method('getItems') ->willReturn([$selection]); @@ -881,13 +881,13 @@ function ($key) use ($optionCollection, $selectionCollection) { ->willReturn($productType); $option->expects($this->at(3)) ->method('getId') - ->willReturn(10); + ->willReturn(3); $option->expects($this->at(9)) ->method('getId') - ->willReturn(10); + ->willReturn(3); $option->expects($this->once()) ->method('getRequired') - ->willReturn(true); + ->willReturn(false); $option->expects($this->once()) ->method('isMultiSelection') ->willReturn(true); @@ -1112,41 +1112,29 @@ function ($key) use ($optionCollection, $selectionCollection) { $resultValue = $selectionCollection; break; case '_cache_instance_used_selections_ids': - $resultValue = [2, 5, 14]; + $resultValue = [5]; break; } return $resultValue; } ); - $optionCollection->expects($this->once()) - ->method('getItemById') - ->willReturn($option); $optionCollection->expects($this->once()) ->method('appendSelections'); $productType->expects($this->once()) ->method('setStoreFilter'); $buyRequest->expects($this->once()) ->method('getBundleOption') - ->willReturn([3 => 5, 10 => [7 => 2, 11 => 14]]); + ->willReturn([3 => 5]); $selectionCollection->expects($this->at(0)) ->method('getItems') ->willReturn([$selection]); $selectionCollection->expects($this->at(1)) ->method('getItems') ->willReturn([]); - $selection->expects($this->once()) - ->method('isSalable') - ->willReturn(false); - $option->expects($this->at(3)) + $option->expects($this->any()) ->method('getId') - ->willReturn(10); - $option->expects($this->once()) - ->method('getRequired') - ->willReturn(true); - $option->expects($this->once()) - ->method('isMultiSelection') - ->willReturn(true); + ->willReturn(3); $result = $this->model->prepareForCartAdvanced($buyRequest, $product); $this->assertEquals('Please specify product option(s).', $result); @@ -1255,7 +1243,7 @@ function ($key) use ($optionCollection, $selectionCollection) { $buyRequest->expects($this->once()) ->method('getBundleOption') ->willReturn([3 => 5]); - $selectionCollection->expects($this->once()) + $selectionCollection->expects($this->any()) ->method('getItems') ->willReturn([$selection]); $selection->expects($this->once()) @@ -1901,8 +1889,7 @@ public function testGetSelectionsByIds() 'setPositionOrder', 'addFilterByRequiredOptions', 'setSelectionIdsFilter', - 'joinPrices', - 'getItems' + 'joinPrices' ] ) ->disableOriginalConstructor() @@ -1971,9 +1958,6 @@ public function testGetSelectionsByIds() ->method('setSelectionIdsFilter') ->with($selectionIds) ->will($this->returnSelf()); - $usedSelectionsMock->expects($this->once()) - ->method('getItems') - ->willReturn($usedSelectionsIds); $usedSelectionsMock->expects($this->once()) ->method('joinPrices') @@ -1987,96 +1971,6 @@ public function testGetSelectionsByIds() $this->model->getSelectionsByIds($selectionIds, $productMock); } - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage The options you selected are not available. - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testGetSelectionsByIdsException() - { - $selectionIds = [1, 2, 3]; - $usedSelectionsIds = [4, 5]; - $storeId = 2; - $storeFilter = 'store_filter'; - $productMock = $this->getMockBuilder('Magento\Catalog\Model\Product') - ->disableOriginalConstructor() - ->getMock(); - $usedSelectionsMock = $this->getMockBuilder('Magento\Bundle\Model\ResourceModel\Selection\Collection') - ->setMethods( - [ - 'addAttributeToSelect', - 'setFlag', - 'addStoreFilter', - 'setStoreId', - 'setPositionOrder', - 'addFilterByRequiredOptions', - 'setSelectionIdsFilter', - 'joinPrices', - 'getItems' - ] - ) - ->disableOriginalConstructor() - ->getMock(); - $productGetMap = [ - ['_cache_instance_used_selections', null, null], - ['_cache_instance_used_selections_ids', null, $usedSelectionsIds], - ['_cache_instance_store_filter', null, $storeFilter], - ]; - $productMock->expects($this->any()) - ->method('getData') - ->will($this->returnValueMap($productGetMap)); - $productSetMap = [ - ['_cache_instance_used_selections', $usedSelectionsMock, $productMock], - ['_cache_instance_used_selections_ids', $selectionIds, $productMock], - ]; - $productMock->expects($this->any()) - ->method('setData') - ->will($this->returnValueMap($productSetMap)); - $productMock->expects($this->once()) - ->method('getStoreId') - ->will($this->returnValue($storeId)); - - $this->bundleCollection->expects($this->once()) - ->method('create') - ->will($this->returnValue($usedSelectionsMock)); - - $usedSelectionsMock->expects($this->once()) - ->method('addAttributeToSelect') - ->with('*') - ->will($this->returnSelf()); - $flagMap = [ - ['require_stock_items', true, $usedSelectionsMock], - ['product_children', true, $usedSelectionsMock], - ]; - $usedSelectionsMock->expects($this->any()) - ->method('setFlag') - ->will($this->returnValueMap($flagMap)); - $usedSelectionsMock->expects($this->once()) - ->method('addStoreFilter') - ->with($storeFilter) - ->will($this->returnSelf()); - $usedSelectionsMock->expects($this->once()) - ->method('setStoreId') - ->with($storeId) - ->will($this->returnSelf()); - $usedSelectionsMock->expects($this->once()) - ->method('setPositionOrder') - ->will($this->returnSelf()); - $usedSelectionsMock->expects($this->once()) - ->method('addFilterByRequiredOptions') - ->will($this->returnSelf()); - $usedSelectionsMock->expects($this->once()) - ->method('setSelectionIdsFilter') - ->with($selectionIds) - ->will($this->returnSelf()); - $usedSelectionsMock->expects($this->once()) - ->method('getItems') - ->willReturn($usedSelectionsIds); - - - $this->model->getSelectionsByIds($selectionIds, $productMock); - } /** * @return void */ diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index d7af88edabd5f..944dd302b8c6b 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -2171,6 +2171,7 @@ public function addPriceDataFieldFilter($comparisonFormat, $fields) */ public function addMediaGalleryData() { + if ($this->getFlag('media_gallery_added')) { return $this; } @@ -2186,7 +2187,11 @@ public function addMediaGalleryData() $this->getStoreId(), $attribute->getAttributeId() ); - + + $select->where('entity.entity_id IN (?)', array_map(function ($item) { + return $item->getId(); + }, $this->getItems())); + foreach ($this->getConnection()->fetchAll($select) as $row) { $mediaGalleries[$row['entity_id']][] = $row; } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index 7536dbf1b3a4d..5d80d2bab5443 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -28,6 +28,16 @@ class CollectionTest extends \PHPUnit_Framework_TestCase */ protected $collection; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $galleryResourceMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $entityMock; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -98,18 +108,22 @@ public function setUp() ->disableOriginalConstructor() ->getMock(); - $entityMock = $this->getMockBuilder('Magento\Eav\Model\Entity\AbstractEntity') + $this->entityMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\AbstractEntity::class) ->disableOriginalConstructor() ->getMock(); + $this->galleryResourceMock = $this->getMockBuilder( + \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Media::class + )->disableOriginalConstructor()->getMock(); + $storeManager->expects($this->any())->method('getId')->willReturn(1); $storeManager->expects($this->any())->method('getStore')->willReturnSelf(); $universalFactory->expects($this->exactly(1))->method('create')->willReturnOnConsecutiveCalls( - $entityMock + $this->entityMock ); - $entityMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); - $entityMock->expects($this->once())->method('getDefaultAttributes')->willReturn([]); - $entityMock->expects($this->any())->method('getTable')->willReturnArgument(0); + $this->entityMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); + $this->entityMock->expects($this->once())->method('getDefaultAttributes')->willReturn([]); + $this->entityMock->expects($this->any())->method('getTable')->willReturnArgument(0); $this->connectionMock->expects($this->atLeastOnce())->method('select')->willReturn($this->selectMock); $helper = new ObjectManager($this); $this->collection = $helper->getObject( @@ -138,6 +152,11 @@ public function setUp() ] ); $this->collection->setConnection($this->connectionMock); + + $reflection = new \ReflectionClass(get_class($this->collection)); + $reflectionProperty = $reflection->getProperty('mediaGalleryResource'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->collection, $this->galleryResourceMock); } public function testAddProductCategoriesFilter() @@ -165,4 +184,42 @@ public function testAddProductCategoriesFilter() )->willReturnSelf(); $this->collection->addCategoriesFilter([$conditionType => $values]); } + + public function testAddMediaGalleryData() + { + $attributeId = 42; + $itemId = 4242; + $mediaGalleriesMock = [['entity_id' => $itemId]]; + $itemMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->disableOriginalConstructor() + ->getMock(); + $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $backendMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Attribute\Backend\Media::class) + ->disableOriginalConstructor() + ->getMock(); + $this->collection->addItem($itemMock); + $reflection = new \ReflectionClass(get_class($this->collection)); + $reflectionProperty = $reflection->getProperty('_isCollectionLoaded'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->collection, true); + + $this->galleryResourceMock->expects($this->once())->method('createBatchBaseSelect')->willReturn($selectMock); + $attributeMock->expects($this->once())->method('getAttributeId')->willReturn($attributeId); + $this->entityMock->expects($this->once())->method('getAttribute')->willReturn($attributeMock); + $itemMock->expects($this->atLeastOnce())->method('getId')->willReturn($itemId); + $selectMock->expects($this->once())->method('where')->with('entity.entity_id IN (?)', [$itemId]); + + $this->connectionMock->expects($this->once())->method('fetchAll')->with($selectMock)->willReturn( + [['entity_id' => $itemId]] + ); + $attributeMock->expects($this->once())->method('getBackend')->willReturn($backendMock); + $backendMock->expects($this->once())->method('addMediaDataToProduct')->with($itemMock, $mediaGalleriesMock); + + $this->assertSame($this->collection, $this->collection->addMediaGalleryData()); + } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml index f6dd2a0033fd4..41f0684a43126 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml @@ -40,9 +40,11 @@ diff --git a/app/code/Magento/Swatches/view/frontend/web/js/SwatchRenderer.js b/app/code/Magento/Swatches/view/frontend/web/js/SwatchRenderer.js index 2549d4d9b62cf..9859a3c74236a 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/SwatchRenderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/SwatchRenderer.js @@ -191,6 +191,7 @@ define(['jquery', 'underscore', 'jquery/ui'], function ($, _) { _init: function () { if (this.options.jsonConfig != '' && this.options.jsonSwatchConfig != '') { this._RenderControls(); + $(this.element).trigger('swatch.initialized'); } else { console.log('SwatchRenderer: No input data received'); } @@ -834,6 +835,30 @@ define(['jquery', 'underscore', 'jquery/ui'], function ($, _) { }); }, + /** + * Emulate mouse click or selection change on all swatches that should be selected + * @param {Object} [selectedAttributes] + * @private + */ + _EmulateSelectedByAttributeId: function (selectedAttributes) { + $.each(selectedAttributes, $.proxy(function (attributeId, optionId) { + var elem = this.element.find('.' + this.options.classes.attributeClass + + '[attribute-id="' + attributeId + '"] [option-id="' + optionId + '"]'), + parentInput = elem.parent(); + + if (elem.hasClass('selected')) { + return; + } + + if (parentInput.hasClass(this.options.classes.selectClass)) { + parentInput.val(optionId); + parentInput.trigger('change'); + } else { + elem.trigger('click'); + } + }, this)); + }, + /** * Returns an array/object's length * @param obj @@ -851,4 +876,5 @@ define(['jquery', 'underscore', 'jquery/ui'], function ($, _) { return size; } }); + return $.custom.SwatchRenderer; }); diff --git a/app/code/Magento/Swatches/view/frontend/web/js/configurable-customer-data.js b/app/code/Magento/Swatches/view/frontend/web/js/configurable-customer-data.js new file mode 100644 index 0000000000000..7ae2477513cf6 --- /dev/null +++ b/app/code/Magento/Swatches/view/frontend/web/js/configurable-customer-data.js @@ -0,0 +1,33 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +require([ + 'jquery', + 'Magento_ConfigurableProduct/js/options-updater' +], function ($, Updater) { + 'use strict'; + + var selectors = { + formSelector: '#product_addtocart_form', + swatchSelector: '.swatch-opt' + }, + swatchWidgetName = 'customSwatchRenderer', + widgetInitEvent = 'swatch.initialized', + + /** + * Sets all configurable swatch attribute's selected values + */ + updateSwatchOptions = function () { + var swatchWidget = $(selectors.swatchSelector).data(swatchWidgetName); + + if (!swatchWidget || !swatchWidget._EmulateSelectedByAttributeId) { + return; + } + swatchWidget._EmulateSelectedByAttributeId(this.productOptions); + }, + updater = new Updater(widgetInitEvent, updateSwatchOptions); + + updater.listen(); +}); diff --git a/app/code/Magento/Tax/Api/TaxClassRepositoryInterface.php b/app/code/Magento/Tax/Api/TaxClassRepositoryInterface.php index bdbd968635880..af08e14325e94 100644 --- a/app/code/Magento/Tax/Api/TaxClassRepositoryInterface.php +++ b/app/code/Magento/Tax/Api/TaxClassRepositoryInterface.php @@ -25,6 +25,10 @@ public function get($taxClassId); /** * Retrieve tax classes which match a specific criteria. * + * This call returns an array of objects, but detailed information about each object’s attributes might not be + * included. See http://devdocs.magento.com/codelinks/attributes.html#TaxClassRepositoryInterface to + * determine which call to use to get detailed information about all attributes for an object. + * * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria * @return \Magento\Tax\Api\Data\TaxClassSearchResultsInterface containing Data\TaxClassInterface * @throws \Magento\Framework\Exception\InputException diff --git a/app/code/Magento/Tax/Api/TaxRateRepositoryInterface.php b/app/code/Magento/Tax/Api/TaxRateRepositoryInterface.php index 88ec624cdbdd3..b4fa0d9ef64fd 100644 --- a/app/code/Magento/Tax/Api/TaxRateRepositoryInterface.php +++ b/app/code/Magento/Tax/Api/TaxRateRepositoryInterface.php @@ -45,6 +45,10 @@ public function deleteById($rateId); /** * Search TaxRates * + * This call returns an array of objects, but detailed information about each object’s attributes might not be + * included. See http://devdocs.magento.com/codelinks/attributes.html#TaxRateRepositoryInterface to + * determine which call to use to get detailed information about all attributes for an object. + * * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria * @return \Magento\Tax\Api\Data\TaxRateSearchResultsInterface containing Data\TaxRateInterface objects * @throws \Magento\Framework\Exception\InputException If there is a problem with the input diff --git a/app/code/Magento/Tax/Api/TaxRuleRepositoryInterface.php b/app/code/Magento/Tax/Api/TaxRuleRepositoryInterface.php index 29ff3d423e17b..5f477f33bae2e 100644 --- a/app/code/Magento/Tax/Api/TaxRuleRepositoryInterface.php +++ b/app/code/Magento/Tax/Api/TaxRuleRepositoryInterface.php @@ -53,6 +53,10 @@ public function deleteById($ruleId); /** * Search TaxRules * + * This call returns an array of objects, but detailed information about each object’s attributes might not be + * included. See http://devdocs.magento.com/codelinks/attributes.html#TaxRuleRepositoryInterface to + * determine which call to use to get detailed information about all attributes for an object. + * * @param \Magento\Framework\Api\SearchCriteria $searchCriteria * @return \Magento\Tax\Api\Data\TaxRuleSearchResultsInterface containing TaxRuleInterface objects * @throws \Magento\Framework\Exception\InputException If there is a problem with the input diff --git a/app/code/Magento/Theme/Model/Theme/ThemeProvider.php b/app/code/Magento/Theme/Model/Theme/ThemeProvider.php index 0d488e9ad0225..bc6a3a6f27e81 100644 --- a/app/code/Magento/Theme/Model/Theme/ThemeProvider.php +++ b/app/code/Magento/Theme/Model/Theme/ThemeProvider.php @@ -17,6 +17,11 @@ class ThemeProvider implements \Magento\Framework\View\Design\Theme\ThemeProvide */ protected $themeFactory; + /** + * @var \Magento\Framework\View\Design\ThemeInterface[] + */ + private $themes; + /** * @param \Magento\Theme\Model\ResourceModel\Theme\CollectionFactory $collectionFactory * @param \Magento\Theme\Model\ThemeFactory $themeFactory @@ -34,9 +39,16 @@ public function __construct( */ public function getThemeByFullPath($fullPath) { + if (isset($this->themes[$fullPath])) { + return $this->themes[$fullPath]; + } + /** @var $themeCollection \Magento\Theme\Model\ResourceModel\Theme\Collection */ $themeCollection = $this->collectionFactory->create(); - return $themeCollection->getThemeByFullPath($fullPath); + $item = $themeCollection->getThemeByFullPath($fullPath); + $this->themes[$fullPath] = $item; + + return $item; } /** @@ -57,8 +69,16 @@ public function getThemeCustomizations( */ public function getThemeById($themeId) { + if (isset($this->themes[$themeId])) { + return $this->themes[$themeId]; + } /** @var $themeModel \Magento\Framework\View\Design\ThemeInterface */ $themeModel = $this->themeFactory->create(); - return $themeModel->load($themeId); + $themeModel->load($themeId); + if ($themeModel->getId()) { + $this->themes[$themeId] = $themeModel; + } + + return $themeModel; } } diff --git a/app/code/Magento/Theme/etc/config.xml b/app/code/Magento/Theme/etc/config.xml index bd0db014a781b..b55b2c1d2735b 100644 --- a/app/code/Magento/Theme/etc/config.xml +++ b/app/code/Magento/Theme/etc/config.xml @@ -62,5 +62,10 @@ Disallow: /*SID= + + + 1 + + diff --git a/app/code/Magento/Theme/view/frontend/web/js/view/messages.js b/app/code/Magento/Theme/view/frontend/web/js/view/messages.js index 2969fdedd7d41..ef4fe41a3b908 100644 --- a/app/code/Magento/Theme/view/frontend/web/js/view/messages.js +++ b/app/code/Magento/Theme/view/frontend/web/js/view/messages.js @@ -5,9 +5,10 @@ define([ 'jquery', 'uiComponent', + 'underscore', 'Magento_Customer/js/customer-data', 'jquery/jquery-storageapi' -], function ($, Component, customerData) { +], function ($, Component, _, customerData) { 'use strict'; return Component.extend({ @@ -15,12 +16,24 @@ define([ cookieMessages: [], messages: [] }, + + /** @inheritdoc */ initialize: function () { this._super(); this.cookieMessages = $.cookieStorage.get('mage-messages'); - this.messages = customerData.get('messages').extend({disposableCustomerData: 'messages'}); - $.cookieStorage.setConf({path: '/', expires: -1}).set('mage-messages', null); + this.messages = customerData.get('messages').extend({ + disposableCustomerData: 'messages' + }); + + if (!_.isEmpty(this.messages().messages)) { + customerData.set('messages', {}); + } + + $.cookieStorage.setConf({ + path: '/', + expires: -1 + }).set('mage-messages', null); } }); }); 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 e6b8fb53a4441..0c9b5c1e37768 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 @@ -4,11 +4,12 @@ */ define([ './utils', + 'underscore', 'jquery', 'jquery/validate', 'jquery/ui', 'mage/translate' -], function (utils, $) { +], function (utils, _, $) { 'use strict'; /** @@ -47,13 +48,13 @@ define([ return { "min_text_length": [ function (value, params) { - return value.length == 0 || value.length >= +params; + return _.isUndefined(value) || value.length == 0 || value.length >= +params; }, $.mage.__('Please enter more or equal than {0} symbols.') ], "max_text_length": [ function (value, params) { - return value.length <= +params; + return !_.isUndefined(value) && value.length <= +params; }, $.mage.__('Please enter less or equal than {0} symbols.') ], diff --git a/app/code/Magento/Wishlist/Helper/Data.php b/app/code/Magento/Wishlist/Helper/Data.php index d64d1a185f0a2..736786e6e30b3 100644 --- a/app/code/Magento/Wishlist/Helper/Data.php +++ b/app/code/Magento/Wishlist/Helper/Data.php @@ -446,7 +446,13 @@ public function getSharedAddAllToCartUrl() */ protected function _getCartUrlParameters($item) { - return ['item' => is_string($item) ? $item : $item->getWishlistItemId()]; + $params = [ + 'item' => is_string($item) ? $item : $item->getWishlistItemId(), + ]; + if ($item instanceof \Magento\Wishlist\Model\Item) { + $params['qty'] = $item->getQty(); + } + return $params; } /** diff --git a/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php b/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php index 079aa695b8abb..f2bb346cca407 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Helper/DataTest.php @@ -124,6 +124,7 @@ public function setUp() ->setMethods([ 'getProduct', 'getWishlistItemId', + 'getQty', ]) ->getMock(); @@ -217,6 +218,7 @@ public function testGetAddToCartParams() $url = 'result url'; $storeId = 1; $wishlistItemId = 1; + $wishlistItemQty = 1; $this->wishlistItem->expects($this->once()) ->method('getProduct') @@ -224,6 +226,9 @@ public function testGetAddToCartParams() $this->wishlistItem->expects($this->once()) ->method('getWishlistItemId') ->willReturn($wishlistItemId); + $this->wishlistItem->expects($this->once()) + ->method('getQty') + ->willReturn($wishlistItemQty); $this->product->expects($this->once()) ->method('isVisibleInSiteVisibility') @@ -243,9 +248,13 @@ public function testGetAddToCartParams() ->with('wishlist/index/cart') ->willReturn($url); + $expected = [ + 'item' => $wishlistItemId, + 'qty' => $wishlistItemQty, + ]; $this->postDataHelper->expects($this->once()) ->method('getPostData') - ->with($url, ['item' => $wishlistItemId]) + ->with($url, $expected) ->willReturn($url); $this->assertEquals($url, $this->model->getAddToCartParams($this->wishlistItem)); @@ -256,6 +265,7 @@ public function testGetAddToCartParamsWithReferer() $url = 'result url'; $storeId = 1; $wishlistItemId = 1; + $wishlistItemQty = 1; $referer = 'referer'; $refererEncoded = 'referer_encoded'; @@ -265,6 +275,9 @@ public function testGetAddToCartParamsWithReferer() $this->wishlistItem->expects($this->once()) ->method('getWishlistItemId') ->willReturn($wishlistItemId); + $this->wishlistItem->expects($this->once()) + ->method('getQty') + ->willReturn($wishlistItemQty); $this->product->expects($this->once()) ->method('isVisibleInSiteVisibility') @@ -288,9 +301,14 @@ public function testGetAddToCartParamsWithReferer() ->with('wishlist/index/cart') ->willReturn($url); + $expected = [ + 'item' => $wishlistItemId, + ActionInterface::PARAM_NAME_URL_ENCODED => $refererEncoded, + 'qty' => $wishlistItemQty, + ]; $this->postDataHelper->expects($this->once()) ->method('getPostData') - ->with($url, ['item' => $wishlistItemId, ActionInterface::PARAM_NAME_URL_ENCODED => $refererEncoded]) + ->with($url, $expected) ->willReturn($url); $this->assertEquals($url, $this->model->getAddToCartParams($this->wishlistItem, true)); @@ -363,6 +381,7 @@ public function testGetSharedAddToCartUrl() $url = 'result url'; $storeId = 1; $wishlistItemId = 1; + $wishlistItemQty = 1; $this->wishlistItem->expects($this->once()) ->method('getProduct') @@ -370,6 +389,9 @@ public function testGetSharedAddToCartUrl() $this->wishlistItem->expects($this->once()) ->method('getWishlistItemId') ->willReturn($wishlistItemId); + $this->wishlistItem->expects($this->once()) + ->method('getQty') + ->willReturn($wishlistItemQty); $this->product->expects($this->once()) ->method('isVisibleInSiteVisibility') @@ -383,9 +405,13 @@ public function testGetSharedAddToCartUrl() ->with('wishlist/shared/cart') ->willReturn($url); + $exptected = [ + 'item' => $wishlistItemId, + 'qty' => $wishlistItemQty, + ]; $this->postDataHelper->expects($this->once()) ->method('getPostData') - ->with($url, ['item' => $wishlistItemId]) + ->with($url, $exptected) ->willReturn($url); $this->assertEquals($url, $this->model->getSharedAddToCartUrl($this->wishlistItem)); diff --git a/app/code/Magento/Wishlist/view/frontend/web/wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/wishlist.js index a4fdc178c704f..0d6e510e5f5e9 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/wishlist.js @@ -47,6 +47,7 @@ define([ event.preventDefault(); $.mage.dataPost().postData($(event.currentTarget).data('post-remove')); }, this)) + .on('click', this.options.addToCartSelector, $.proxy(this._beforeAddToCart, this)) .on('click', this.options.addAllToCartSelector, $.proxy(this._addAllWItemsToCart, this)) .on('focusin focusout', this.options.commentInputType, $.proxy(this._focusComment, this)); } @@ -59,6 +60,27 @@ define([ }); }, + /** + * Process data before add to cart + * + * - update item's qty value. + * + * @param {Event} event + * @private + */ + _beforeAddToCart: function(event) { + var elem = $(event.currentTarget), + itemId = elem.data(this.options.dataAttribute), + qtyName = $.validator.format(this.options.nameFormat, itemId), + qtyValue = elem.parents().find('[name="' + qtyName + '"]').val(), + params = elem.data('post'); + + if (params) { + params.data = $.extend({}, params.data, {'qty': qtyValue}); + elem.data('post', params); + } + }, + /** * Add wish list items to cart. * @private diff --git a/dev/tests/functional/composer.json b/dev/tests/functional/composer.json index 532ccf5e82912..257c29200b6a7 100644 --- a/dev/tests/functional/composer.json +++ b/dev/tests/functional/composer.json @@ -1,6 +1,6 @@ { "require": { - "magento/mtf": "1.0.0-rc37", + "magento/mtf": "2.0.x-dev", "php": "~5.5.0|~5.6.0|~7.0.0", "phpunit/phpunit": "4.1.0", "phpunit/phpunit-selenium": ">=1.2" diff --git a/dev/tests/functional/etc/repository_replacer.xml b/dev/tests/functional/etc/repository_replacer.xml new file mode 100644 index 0000000000000..0674f5c7d5d34 --- /dev/null +++ b/dev/tests/functional/etc/repository_replacer.xml @@ -0,0 +1,200 @@ + + + + + + + 4 + + + + + + 113 + + + + 172 + + + + + + 1 + 2 + + + + 1 + + + + + + 1 + + + + 3 + + + + 2 + + + + 0 + + + + 0 + + + + 32000 + + + + + + 1 + + + + 1 + + + + 0 + + + + + + 1 + + + + + + 1 + + + + 0 + + + + 1 + + + + + + 2 + 2 + + + + 3 + 3 + + + + 0 + + + + + + 1 + 12 + + + + 23 + + + + 2 + 43 + + + + 12 + + + + 43 + + + + 12 + + + + 12 + + + + 43 + + + + 43 + + + + 12 + + + + 12 + + + + 57 + + + + 58 + + + + 57 + + + + 43 + + + + 12 + + + + + + + 1 + + + + + + 1 + + + + + + 1 + + + diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/ModuleResolver/SequenceSorter.php b/dev/tests/functional/lib/Magento/Mtf/Util/ModuleResolver/SequenceSorter.php new file mode 100644 index 0000000000000..cf8eae8a18629 --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Util/ModuleResolver/SequenceSorter.php @@ -0,0 +1,73 @@ +initObjectManager(); + } + /** + * Initialize Magento ObjectManager. + * + * @return void + */ + protected function initObjectManager() + { + if (!$this->magentoObjectManager) { + $objectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory( + BP, + $_SERVER + ); + $this->magentoObjectManager = $objectManagerFactory->create($_SERVER); + } + } + /** + * Get Magento module sequence load. + * + * @return array + */ + protected function getModuleSequence() + { + return $this->magentoObjectManager->create(\Magento\Framework\Module\ModuleList\Loader::class)->load(); + } + /** + * Sort files according to specified sequence. + * + * @param array $paths + * @return array + */ + public function sort(array $paths) + { + $sortedPaths = []; + $modules = array_keys($this->getModuleSequence()); + foreach ($modules as $module) { + foreach ($paths as $key => $path) { + $modulePath = realpath(MTF_TESTS_PATH . str_replace('_', DIRECTORY_SEPARATOR, $module)); + $path = realpath($path); + if (strpos($path, $modulePath) !== false) { + $sortedPaths[] = $path; + unset($paths[$key]); + } + } + } + $sortedPaths = array_merge($sortedPaths, $paths); + return $sortedPaths; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogAttributeSet.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogAttributeSet.xml index 975d3b3ce8d86..a62bef0680a9e 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogAttributeSet.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogAttributeSet.xml @@ -8,8 +8,8 @@ + %id% Default - 4 diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductAttribute.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductAttribute.xml index cb10ceb146c3d..06a22f90f2eb1 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductAttribute.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductAttribute.xml @@ -14,7 +14,7 @@ - 113 + %id% Quantity quantity_and_stock_status Dropdown @@ -34,7 +34,7 @@ - 172 + %id% Tax Class tax_class_id Dropdown diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml index 17c2b5922e5c6..b739978eebf8c 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml @@ -18,10 +18,10 @@ + %id% + %id% Default Category - 1 Yes - 2 @@ -46,8 +46,8 @@ + %id% RootCategory%isolation% - 1 Yes Yes diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/CustomerGroup.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/CustomerGroup.xml index 2201844677f5d..4e95c89a89ad5 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/CustomerGroup.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/CustomerGroup.xml @@ -15,7 +15,7 @@ - 1 + %id% General retail_customer @@ -23,7 +23,7 @@ - 3 + %id% Retailer retail_customer @@ -31,7 +31,7 @@ - 2 + %id% Wholesale retail_customer @@ -39,12 +39,12 @@ - 0 + %id% All Customer Groups - 0 + %id% NOT LOGGED IN retail_customer diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/StoreGroup.xml b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/StoreGroup.xml index 1cff6e7d845d6..b0715e62d7fcd 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/StoreGroup.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/StoreGroup.xml @@ -8,11 +8,11 @@ + %id% main_website Main Website Store - 1 default_category diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Website.xml b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Website.xml index 8b90722866ae9..5c2ec69efca98 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Website.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/Repository/Website.xml @@ -8,21 +8,21 @@ + %id% Main Website base - 1 + %id% All Websites - 0 + %id% Main Website base 0 - 1 diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxClass.xml b/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxClass.xml index 35ece9e1fa394..061bc05c07891 100644 --- a/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxClass.xml +++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxClass.xml @@ -12,17 +12,17 @@ - 2 + %id% + %id% Taxable Goods PRODUCT - 2 - 3 + %id% + %id% Retail Customer CUSTOMER - 3 @@ -36,9 +36,9 @@ + %id% None PRODUCT - 0 diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxRate.xml b/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxRate.xml index ac6a00f22ff91..600436dc5f1b7 100644 --- a/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxRate.xml +++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxRate.xml @@ -16,9 +16,9 @@ - 1 + %id% + %id% US - 12 * US-CA-*-Rate 1 8.2500 @@ -30,9 +30,9 @@ - 2 + %id% + %id% US - 43 * US-NY-*-Rate 1 8.3750 @@ -45,33 +45,33 @@ Tax Rate %isolation% + %id% 8.25 United States 90230 - California + %id% Tax Rate %isolation% 8.25 United States * - California Tax Rate %isolation% + %id% 8.375 United States * - California Tax Rate %isolation% + %id% 8.375 United States - New York * @@ -79,15 +79,15 @@ Tax Rate %isolation% 8.25 United States - New York + %id% * US-NY-*-%isolation% + %id% 8.1 United States - New York * @@ -96,7 +96,7 @@ 8.25 United States 95131 - California + %id% @@ -119,7 +119,7 @@ 90001 96162 United States - California + %id% 15.5 @@ -127,7 +127,7 @@ TaxIdentifier%isolation% * United States - Texas + %id% 20 @@ -135,7 +135,7 @@ TaxIdentifier%isolation% 84001 United States - Utah + %id% 20 @@ -143,7 +143,7 @@ TaxIdentifier%isolation% * United States - Texas + %id% 10 @@ -151,7 +151,7 @@ TaxIdentifier%isolation% * United States - New York + %id% 20 @@ -159,7 +159,7 @@ TaxIdentifier%isolation% * United States - California + %id% 30 diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxRule.xml b/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxRule.xml index 69c8f0d945760..33f0a7105ef34 100644 --- a/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxRule.xml +++ b/dev/tests/functional/tests/app/Magento/Tax/Test/Repository/TaxRule.xml @@ -32,7 +32,7 @@ Tax Rule %isolation% - US-CA-Rate_1 + us_ca_rate_8_25_no_zip us_ny_rate_8_1 diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Repository/User.xml b/dev/tests/functional/tests/app/Magento/User/Test/Repository/User.xml index 7aa989352a656..25b812bf77665 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/Repository/User.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/Repository/User.xml @@ -8,13 +8,13 @@ + %id% admin FirstName%isolation% LastName%isolation% email%isolation%@example.com 123123q 123123q - 1 %current_password% diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/TypeTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/TypeTest.php index 03abeaf837ff7..e2d835c07d4b2 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/TypeTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/TypeTest.php @@ -62,4 +62,97 @@ public function testPrepareProductIndexForBundleProduct() $result = $this->connectionMock->fetchAll($select); $this->assertCount(1, $result); } + + /** + * Test that having valid buyRequest it can be successfully prepared fro shopping cart + * + * @magentoDataFixture Magento/Bundle/_files/product_with_multiple_options.php + * @magentoAppArea frontend + * @magentoAppIsolation enabled + */ + public function testPrepareForCart() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get('bundle-product'); + + /** @var \Magento\Bundle\Api\ProductOptionRepositoryInterface $optionsRepository */ + $optionsRepository = $this->objectManager->create(\Magento\Bundle\Api\ProductOptionRepositoryInterface::class); + $options = $optionsRepository->getList($product->getSku()); + + $data = [ + 'id' => '10', + 'product' => '3', + 'selected_configurable_option' => '', + 'related_product' => '', + 'bundle_option' => [], + 'bundle_option_qty' => [], + 'qty' => '1', + 'options' => [], + 'reset_count' => true, + ]; + + foreach ($options as $option) { + /** @var \Magento\Bundle\Api\Data\LinkInterface $link */ + $link = current($option->getProductLinks()); + $data['bundle_option'][$option->getOptionId()] = $link->getId(); + $data['bundle_option_qty'][$option->getOptionId()] = 1; + } + + $request = $this->objectManager->create(\Magento\Framework\DataObject::class, ['data' => $data]); + /** @var \Magento\Bundle\Model\Product\Type $typeInstance */ + $typeInstance = $product->getTypeInstance(); + + $result = $typeInstance->prepareForCart($request, $product); + $this->assertEquals(count($data['bundle_option']) + 1, count($result), 'Incorrect product count'); + } + + /** + * Test that having invalid selection option in buyRequest + * prepareForCart method will return meaningful error message + * + * @magentoDataFixture Magento/Bundle/_files/product_with_multiple_options.php + * @magentoAppArea frontend + * @magentoAppIsolation enabled + */ + public function testPrepareForCartWithUnavailableOption() + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get('bundle-product'); + + /** @var \Magento\Bundle\Api\ProductOptionRepositoryInterface $optionsRepository */ + $optionsRepository = $this->objectManager->create(\Magento\Bundle\Api\ProductOptionRepositoryInterface::class); + $options = $optionsRepository->getList($product->getSku()); + + $data = [ + 'id' => '10', + 'product' => '3', + 'selected_configurable_option' => '', + 'related_product' => '', + 'bundle_option' => [], + 'bundle_option_qty' => [], + 'qty' => '1', + 'options' => [], + 'reset_count' => true, + ]; + + $option = null; + foreach ($options as $option) { + /** @var \Magento\Bundle\Api\Data\LinkInterface $link */ + $link = current($option->getProductLinks()); + $data['bundle_option'][$option->getOptionId()] = $link->getId(); + $data['bundle_option_qty'][$option->getOptionId()] = 1; + } + + /** Set latest option selection to unavailable option */ + $data['bundle_option'][$option->getOptionId()] = 300; + + $buyRequest = $this->objectManager->create(\Magento\Framework\DataObject::class, ['data' => $data]); + /** @var \Magento\Bundle\Model\Product\Type $typeInstance */ + $typeInstance = $product->getTypeInstance(); + + $result = $typeInstance->prepareForCart($buyRequest, $product); + $this->assertEquals('The options you selected are not available.', $result); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ImageTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ImageTest.php index 04dac68782f81..9f2196ac19ca1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ImageTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ImageTest.php @@ -46,7 +46,7 @@ public function testSaveFilePlaceholder($model) public function testGetUrlPlaceholder($model) { $this->assertStringMatchesFormat( - 'http://localhost/pub/static/frontend/%s/Magento_Catalog/images/product/placeholder/image.jpg', + 'http://localhost/pub/static/%s/frontend/%s/Magento_Catalog/images/product/placeholder/image.jpg', $model->getUrl() ); } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 84023f86cbcd2..3fd3419b5554e 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -14,13 +14,14 @@ */ namespace Magento\CatalogImportExport\Model\Import; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\ImportExport\Model\Import; /** * Class ProductTest - * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php */ class ProductTest extends \PHPUnit_Framework_TestCase @@ -946,4 +947,62 @@ public function testValidateUrlKeysMultipleStores() $this->assertTrue($errors->getErrorsCount() == 0); } + + /** + * @magentoDataFixture Magento/Store/_files/website.php + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testProductWithMultipleStoresInDifferentBunches() + { + $products = [ + 'simple1', + 'simple2', + 'simple3' + ]; + + $importExportData = $this->getMockBuilder(\Magento\ImportExport\Helper\Data::class) + ->disableOriginalConstructor() + ->getMock(); + $importExportData->expects($this->atLeastOnce()) + ->method('getBunchSize') + ->willReturn(1); + $this->_model = $this->objectManager->create( + \Magento\CatalogImportExport\Model\Import\Product::class, + ['importExportData' => $importExportData] + ); + + $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + \Magento\ImportExport\Model\Import\Source\Csv::class, + [ + 'file' => __DIR__ . '/_files/products_to_import_with_multiple_store.csv', + 'directory' => $directory + ] + ); + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] + )->setSource( + $source + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + + $this->_model->importData(); + $productCollection = $this->objectManager + ->create(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); + $this->assertCount(3, $productCollection->getItems()); + $actualProductSkus = array_map( + function(ProductInterface $item) { + return $item->getSku(); + }, + $productCollection->getItems() + ); + $this->assertEquals( + $products, + array_values($actualProductSkus) + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_multiple_store.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_multiple_store.csv new file mode 100644 index 0000000000000..a4ad5adb7b0f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_multiple_store.csv @@ -0,0 +1,6 @@ +sku,product_type,store_view_code,name,price,attribute_set_code,categories +simple1,simple,fixturestore,"simple 1",25,Default,"Default Category/Category 1" +simple1,simple,,"simple 1",25,Default,"Default Category/Category 1" +simple2,simple,fixturestore,"simple 2",34,Default,"Default Category/Category 1" +simple2,simple,,"simple 2",34,Default,"Default Category/Category 1" +simple3,simple,,"simple 3",58,Default,"Default Category/Category 1" diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php index 07b55f5e9aeaf..fdb63a875282e 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/ConfigTest.php @@ -42,7 +42,7 @@ public function testGetConfig() public function testGetConfigCssUrls() { $config = $this->_model->getConfig(); - $publicPathPattern = 'http://localhost/pub/static/adminhtml/Magento/backend/en_US/mage/%s'; + $publicPathPattern = 'http://localhost/pub/static/%s/adminhtml/Magento/backend/en_US/mage/%s'; $this->assertStringMatchesFormat($publicPathPattern, $config->getPopupCss()); $this->assertStringMatchesFormat($publicPathPattern, $config->getContentCss()); } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php index a3baf43c54949..523058759fa87 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php @@ -57,7 +57,7 @@ public function testGetFilesCollection() $this->assertInstanceOf('Magento\Framework\DataObject', $item); $this->assertStringEndsWith('/1.swf', $item->getUrl()); $this->assertStringMatchesFormat( - 'http://%s/static/adminhtml/%s/%s/Magento_Cms/images/placeholder_thumbnail.jpg', + 'http://%s/static/%s/adminhtml/%s/%s/Magento_Cms/images/placeholder_thumbnail.jpg', $item->getThumbUrl() ); return; 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 0b4c736b40991..eb942ad392d19 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 @@ -239,7 +239,8 @@ public function testGetSelectedAttributesInfo() $this->_product->addCustomOption('attributes', serialize([$attribute['attribute_id'] => $optionValueId])); $info = $this->_model->getSelectedAttributesInfo($this->_product); - $this->assertEquals([['label' => 'Test Configurable', 'value' => 'Option 1']], $info); + $this->assertEquals('Test Configurable', $info[0]['label']); + $this->assertEquals('Option 1', $info[0]['value']); } /** @@ -259,7 +260,8 @@ public function testGetSelectedAttributesInfoForStore() $attribute->getProductAttribute()->setStoreLabel('store label'); $info = $this->_model->getSelectedAttributesInfo($this->_product); - $this->assertEquals([['label' => 'store label', 'value' => 'Option 1']], $info); + $this->assertEquals('store label', $info[0]['label']); + $this->assertEquals('Option 1', $info[0]['value']); } /** @@ -302,10 +304,8 @@ public function testGetOrderOptions() $result = $this->_model->getOrderOptions($this->_product); $this->assertArrayHasKey('info_buyRequest', $result); $this->assertArrayHasKey('attributes_info', $result); - $this->assertEquals( - [['label' => 'Test Configurable', 'value' => 'Option 1']], - $result['attributes_info'] - ); + $this->assertEquals('Test Configurable', $result['attributes_info'][0]['label']); + $this->assertEquals('Option 1', $result['attributes_info'][0]['value']); $this->assertArrayHasKey('product_calculations', $result); $this->assertArrayHasKey('shipment_type', $result); $this->assertEquals( diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php index 6a101a201051f..8cb9e4a38bfe0 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php @@ -112,11 +112,8 @@ public function testImportData() /** @var $objectManager \Magento\TestFramework\ObjectManager */ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $existingCustomer = $objectManager->get( - 'Magento\Framework\Registry' - )->registry( - '_fixture/Magento_ImportExport_Customer' - ); + $existingCustomer = $objectManager->get('Magento\Framework\Registry') + ->registry('_fixture/Magento_ImportExport_Customer'); $updatedCustomer = $customers[$existingCustomer->getId()]; @@ -137,6 +134,12 @@ public function testImportData() $updatedCustomer->getCreatedAt(), 'Creation date must be changed' ); + + $this->assertEquals( + $existingCustomer->getGender(), + $updatedCustomer->getGender(), + 'Gender must be not changed' + ); } /** diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php b/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php index f330749fa6418..a4ee39948e118 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php @@ -283,6 +283,7 @@ public function cssDirectiveDataProvider() * @magentoComponentsDir Magento/Email/Model/_files/design * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @magentoConfigFixture default_store dev/static/sign 0 * @dataProvider inlinecssDirectiveDataProvider * * @param string $templateText diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/TemplateTest.php b/dev/tests/integration/testsuite/Magento/Email/Model/TemplateTest.php index a47c1aa0d8d9a..dab0750661eec 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/TemplateTest.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/TemplateTest.php @@ -122,7 +122,7 @@ public function testGetProcessedTemplate() ->getArea(Area::AREA_FRONTEND) ->load(); - $expectedViewUrl = 'static/frontend/Magento/blank/en_US/Magento_Theme/favicon.ico'; + $expectedViewUrl = '/frontend/Magento/blank/en_US/Magento_Theme/favicon.ico'; $this->model->setDesignConfig([ 'area' => 'frontend', 'store' => $this->objectManager->get('Magento\Store\Model\StoreManagerInterface') @@ -578,7 +578,6 @@ public function testGetProcessedTemplateSubject() ->getArea(Area::AREA_FRONTEND) ->load(); - $expectedViewUrl = 'static/frontend/Magento/blank/en_US/Magento_Theme/favicon.ico'; $this->model->setTemplateSubject('{{view url="Magento_Theme::favicon.ico"}}'); $this->model->setDesignConfig([ 'area' => 'frontend', @@ -588,10 +587,16 @@ public function testGetProcessedTemplateSubject() ]); $this->setNotDefaultThemeForFixtureStore(); - $this->assertStringEndsNotWith($expectedViewUrl, $this->model->getProcessedTemplateSubject([])); + $this->assertStringMatchesFormat( + '%s/frontend/Magento/luma/en_US/Magento_Theme/favicon.ico', + $this->model->getProcessedTemplateSubject([]) + ); $this->setDefaultThemeForFixtureStore(); - $this->assertStringEndsWith($expectedViewUrl, $this->model->getProcessedTemplateSubject([])); + $this->assertStringMatchesFormat( + '%s/frontend/Magento/blank/en_US/Magento_Theme/favicon.ico', + $this->model->getProcessedTemplateSubject([]) + ); } /** @@ -605,7 +610,7 @@ public function testGetDefaultEmailLogo() ->load(); $this->assertStringEndsWith( - 'static/frontend/Magento/luma/en_US/Magento_Email/logo_email.png', + '/frontend/Magento/luma/en_US/Magento_Email/logo_email.png', $this->model->getDefaultEmailLogo() ); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/Asset/MinifierTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/Asset/MinifierTest.php index 3cfeb69f24532..c94677f98f5f7 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/Asset/MinifierTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/Asset/MinifierTest.php @@ -14,6 +14,7 @@ * * @magentoComponentsDir Magento/Framework/View/_files/static/theme * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MinifierTest extends \PHPUnit_Framework_TestCase { @@ -139,43 +140,44 @@ protected function _testCssMinification($requestedUri, $assertionCallback) } /** - * @magentoConfigFixture current_store dev/css/minify_files 1 + * @magentoConfigFixture current_store dev/css/minify_files 0 + * @magentoAppIsolation enabled */ - public function testCssMinification() + public function testCssMinificationOff() { $this->_testCssMinification( - '/frontend/FrameworkViewMinifier/default/en_US/css/styles.min.css', + '/frontend/FrameworkViewMinifier/default/en_US/css/styles.css', function ($path) { - $this->assertEquals( + $content = file_get_contents($path); + $this->assertNotEmpty($content); + $this->assertContains('FrameworkViewMinifier/frontend', $content); + $this->assertNotEquals( file_get_contents( dirname(__DIR__) . '/_files/static/expected/styles.magento.min.css' ), - file_get_contents($path), - 'Minified files are not equal or minification did not work for requested CSS' + $content, + 'CSS is minified when minification turned off' ); } ); } /** - * @magentoConfigFixture current_store dev/css/minify_files 0 + * @magentoConfigFixture current_store dev/css/minify_files 1 */ - public function testCssMinificationOff() + public function testCssMinification() { $this->_testCssMinification( - '/frontend/FrameworkViewMinifier/default/en_US/css/styles.css', + '/frontend/FrameworkViewMinifier/default/en_US/css/styles.min.css', function ($path) { - $content = file_get_contents($path); - $this->assertNotEmpty($content); - $this->assertContains('FrameworkViewMinifier/frontend', $content); - $this->assertNotEquals( + $this->assertEquals( file_get_contents( dirname(__DIR__) . '/_files/static/expected/styles.magento.min.css' ), - $content, - 'CSS is minified when minification turned off' + file_get_contents($path), + 'Minified files are not equal or minification did not work for requested CSS' ); } ); @@ -233,13 +235,13 @@ public function testDeploymentWithMinifierEnabled() ] )); - /** @var \Magento\Deploy\Model\Deployer $deployer */ + /** @var \Magento\Deploy\Model\Deploy\LocaleDeploy $deployer */ $deployer = $this->objectManager->create( - 'Magento\Deploy\Model\Deployer', - ['filesUtil' => $filesUtil, 'output' => $output, 'isDryRun' => false] + \Magento\Deploy\Model\Deploy\LocaleDeploy::class, + ['filesUtil' => $filesUtil, 'output' => $output] ); - $deployer->deploy($omFactory, ['en_US']); + $deployer->deploy('frontend', 'FrameworkViewMinifier/default', 'en_US', []); $this->assertFileExists($fileToBePublished); $this->assertEquals( diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php index de6031e9db0e3..260311b48b1ec 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php @@ -477,7 +477,10 @@ public function testGetUrl() public function testGetViewFileUrl() { $actualResult = $this->_block->getViewFileUrl('css/styles.css'); - $this->assertStringMatchesFormat('http://localhost/pub/static/frontend/%s/en_US/css/styles.css', $actualResult); + $this->assertStringMatchesFormat( + 'http://localhost/pub/static/%s/frontend/%s/en_US/css/styles.css', + $actualResult + ); } public function testGetModuleName() diff --git a/dev/tests/integration/testsuite/Magento/Review/Controller/ProductTest.php b/dev/tests/integration/testsuite/Magento/Review/Controller/ProductTest.php index 57cf616fd7c7a..967650a85271e 100644 --- a/dev/tests/integration/testsuite/Magento/Review/Controller/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Review/Controller/ProductTest.php @@ -16,7 +16,7 @@ public function testListActionDesign($productId, $expectedDesign) $this->getRequest()->setParam('id', $productId); $this->dispatch('review/product/listAction'); $result = $this->getResponse()->getBody(); - $this->assertContains("static/frontend/{$expectedDesign}/en_US/Magento_Theme/favicon.ico", $result); + $this->assertContains("/frontend/{$expectedDesign}/en_US/Magento_Theme/favicon.ico", $result); } /** diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderItemTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderItemTest.php new file mode 100644 index 0000000000000..e23ecd978eb7c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderItemTest.php @@ -0,0 +1,37 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * Verify that serialized order item data was unserialized after load + * + * @magentoDataFixture Magento/Catalog/_files/order_item_with_product_and_custom_options.php + */ + public function testGetOrderItem() + { + /** @var \Magento\Sales\Model\Order $order */ + $order = $this->objectManager->create(\Magento\Sales\Model\Order::class); + $order->loadByIncrementId('100000001'); + $items = $order->getItemsCollection(); + $this->assertNotEquals(0, $items->getSize()); + foreach ($items as $item) { + $info = $item->getDataByKey('product_options'); + $this->assertTrue(is_array($info)); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php index ae02d11a597cf..9f8eccf92676c 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php @@ -38,15 +38,15 @@ public function testSetValidationFilter($couponCode, $expectedItems) public function setValidationFilterDataProvider() { return [ - 'Check type COUPON' => ['coupon_code', ['#1', '#5']], + 'Check type COUPON' => ['coupon_code', ['#1', '#2', '#5']], 'Check type NO_COUPON' => ['', ['#2', '#5']], - 'Check type COUPON_AUTO' => ['coupon_code_auto', ['#4', '#5']], - 'Check result with auto generated coupon' => ['autogenerated_3_1', ['#3', '#5']], + 'Check type COUPON_AUTO' => ['coupon_code_auto', ['#2', '#4', '#5']], + 'Check result with auto generated coupon' => ['autogenerated_3_1', ['#2', '#3', '#5']], 'Check result with non actual previously generated coupon' => [ 'autogenerated_2_1', ['#2', '#5'], ], - 'Check result with wrong code' => ['wrong_code', ['#5']] + 'Check result with wrong code' => ['wrong_code', ['#2', '#5']] ]; } diff --git a/dev/tests/integration/testsuite/Magento/Variable/Model/Variable/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Variable/Model/Variable/ConfigTest.php index 36cdd82450ec5..4828cb68190e1 100644 --- a/dev/tests/integration/testsuite/Magento/Variable/Model/Variable/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Variable/Model/Variable/ConfigTest.php @@ -21,15 +21,16 @@ class ConfigTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - 'Magento\Variable\Model\Variable\Config' + \Magento\Variable\Model\Variable\Config::class ); } public function testGetWysiwygJsPluginSrc() { $src = $this->_model->getWysiwygJsPluginSrc(); - $this->assertStringStartsWith('http://localhost/pub/static/adminhtml/Magento/backend/en_US/mage/adminhtml/', - $src); - $this->assertStringEndsWith('editor_plugin.js', $src); + $this->assertStringMatchesFormat( + 'http://localhost/pub/static/%s/adminhtml/Magento/backend/en_US/mage/adminhtml/%s/editor_plugin.js', + $src + ); } } diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Widget/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Widget/Model/Widget/ConfigTest.php index 42143e8a4ce4d..3e13f8daf5a60 100644 --- a/dev/tests/integration/testsuite/Magento/Widget/Model/Widget/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Widget/Model/Widget/ConfigTest.php @@ -43,8 +43,10 @@ public function testGetPluginSettings() $this->assertArrayHasKey('widget_window_url', $settings); $jsFilename = $settings['widget_plugin_src']; - $this->assertStringStartsWith('http://localhost/pub/static/adminhtml/Magento/backend/en_US/', $jsFilename); - $this->assertStringEndsWith('editor_plugin.js', $jsFilename); + $this->assertStringMatchesFormat( + 'http://localhost/pub/static/%s/adminhtml/Magento/backend/en_US/%s/editor_plugin.js', + $jsFilename + ); $this->assertInternalType('array', $settings['widget_placeholders']); diff --git a/lib/internal/Magento/Framework/App/Cache/Type/Dummy.php b/lib/internal/Magento/Framework/App/Cache/Type/Dummy.php new file mode 100644 index 0000000000000..ea6a3c3e92e7e --- /dev/null +++ b/lib/internal/Magento/Framework/App/Cache/Type/Dummy.php @@ -0,0 +1,67 @@ +getConnectionByName($connectionName); } + /** + * @param string $resourceName + * @return void + */ + public function closeConnection($resourceName = self::DEFAULT_CONNECTION) + { + $processConnectionName = $this->getProcessConnectionName($this->config->getConnectionName($resourceName)); + if (isset($this->connections[$processConnectionName])) { + $this->connections[$processConnectionName] = null; + } + } + /** * Retrieve connection by $connectionName * @@ -101,8 +113,9 @@ public function getConnection($resourceName = self::DEFAULT_CONNECTION) */ public function getConnectionByName($connectionName) { - if (isset($this->connections[$connectionName])) { - return $this->connections[$connectionName]; + $processConnectionName = $this->getProcessConnectionName($connectionName); + if (isset($this->connections[$processConnectionName])) { + return $this->connections[$processConnectionName]; } $connectionConfig = $this->deploymentConfig->get( @@ -115,10 +128,19 @@ public function getConnectionByName($connectionName) throw new \DomainException('Connection "' . $connectionName . '" is not defined'); } - $this->connections[$connectionName] = $connection; + $this->connections[$processConnectionName] = $connection; return $connection; } + /** + * @param string $connectionName + * @return string + */ + private function getProcessConnectionName($connectionName) + { + return $connectionName . '_process_' . getmypid(); + } + /** * Get resource table name, validated by db adapter * diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/ViewFactoryTest.php b/lib/internal/Magento/Framework/Config/Test/Unit/ViewFactoryTest.php new file mode 100644 index 0000000000000..7184c713bbd48 --- /dev/null +++ b/lib/internal/Magento/Framework/Config/Test/Unit/ViewFactoryTest.php @@ -0,0 +1,94 @@ +objectManager = $this->getMock(\Magento\Framework\ObjectManagerInterface::class); + $this->model = new \Magento\Framework\Config\ViewFactory($this->objectManager); + $this->theme = $this->getMock(\Magento\Framework\View\Design\ThemeInterface::class); + $this->view = $this->getMock(\Magento\Framework\Config\View::class, [], [], '', false); + } + + public function testCreate() + { + $this->objectManager->expects($this->once()) + ->method('create') + ->with(\Magento\Framework\Config\View::class, []) + ->willReturn($this->view); + $this->assertEquals($this->view, $this->model->create()); + } + + public function testCreateWithArguments() + { + /** @var \Magento\Theme\Model\View\Design|\PHPUnit_Framework_MockObject_MockObject $design */ + $design = $this->getMock(\Magento\Theme\Model\View\Design::class, [], [], '', false); + $design->expects($this->once()) + ->method('setDesignTheme') + ->with($this->theme, self::AREA); + + /** @var \Magento\Framework\Config\FileResolver|\PHPUnit_Framework_MockObject_MockObject $fileResolver */ + $fileResolver = $this->getMock(\Magento\Framework\Config\FileResolver::class, [], [], '', false); + + $valueMap = [ + [\Magento\Theme\Model\View\Design::class, [], $design], + [\Magento\Framework\Config\FileResolver::class, ['designInterface' => $design], $fileResolver], + [\Magento\Framework\Config\View::class, ['fileResolver' => $fileResolver], $this->view], + ]; + $this->objectManager->expects($this->exactly(3)) + ->method('create') + ->willReturnMap($valueMap); + + $this->assertEquals($this->view, $this->model->create($this->getArguments())); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage wrong theme doesn't implement ThemeInterface + */ + public function testCreateException() + { + $this->model->create([ + 'themeModel' => 'wrong theme', + 'area' => self::AREA + ]); + } + + /** + * @return array + */ + protected function getArguments() + { + return [ + 'themeModel' => $this->theme, + 'area' => self::AREA + ]; + } +} diff --git a/lib/internal/Magento/Framework/Config/ViewFactory.php b/lib/internal/Magento/Framework/Config/ViewFactory.php index 2df147b0fa626..7adff4f50cf59 100644 --- a/lib/internal/Magento/Framework/Config/ViewFactory.php +++ b/lib/internal/Magento/Framework/Config/ViewFactory.php @@ -26,12 +26,38 @@ public function __construct(ObjectManagerInterface $objectManager) } /** + * Create new view object + * + * @param array $arguments * @return \Magento\Framework\Config\View + * @throws \Magento\Framework\Exception\LocalizedException */ - public function create() + public function create(array $arguments = []) { + $viewConfigArguments = []; + + if (isset($arguments['themeModel']) && isset($arguments['area'])) { + if (!($arguments['themeModel'] instanceof \Magento\Framework\View\Design\ThemeInterface)) { + throw new \Magento\Framework\Exception\LocalizedException( + new \Magento\Framework\Phrase('%1 doesn\'t implement ThemeInterface', [$arguments['themeModel']]) + ); + } + /** @var \Magento\Theme\Model\View\Design $design */ + $design = $this->objectManager->create(\Magento\Theme\Model\View\Design::class); + $design->setDesignTheme($arguments['themeModel'], $arguments['area']); + /** @var \Magento\Framework\Config\FileResolver $fileResolver */ + $fileResolver = $this->objectManager->create( + \Magento\Framework\Config\FileResolver::class, + [ + 'designInterface' => $design, + ] + ); + $viewConfigArguments['fileResolver'] = $fileResolver; + } + return $this->objectManager->create( - 'Magento\Framework\Config\View' + \Magento\Framework\Config\View::class, + $viewConfigArguments ); } } diff --git a/lib/internal/Magento/Framework/Console/Cli.php b/lib/internal/Magento/Framework/Console/Cli.php index fa5857d472794..35ee879381742 100644 --- a/lib/internal/Magento/Framework/Console/Cli.php +++ b/lib/internal/Magento/Framework/Console/Cli.php @@ -21,6 +21,12 @@ */ class Cli extends SymfonyApplication { + /** + * Cli exit codes + */ + const RETURN_SUCCESS = 0; + const RETURN_FAILURE = 1; + /** * Name of input option */ diff --git a/lib/internal/Magento/Framework/Css/PreProcessor/Adapter/Less/Processor.php b/lib/internal/Magento/Framework/Css/PreProcessor/Adapter/Less/Processor.php index 6387ac46e038c..7883586d0b7e5 100644 --- a/lib/internal/Magento/Framework/Css/PreProcessor/Adapter/Less/Processor.php +++ b/lib/internal/Magento/Framework/Css/PreProcessor/Adapter/Less/Processor.php @@ -16,6 +16,7 @@ /** * Class Processor + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Processor implements ContentProcessorInterface { @@ -81,9 +82,11 @@ public function processContent(File $asset) } $tmpFilePath = $this->temporaryFile->createFile($path, $content); - $parser->parseFile($tmpFilePath, ''); + gc_disable(); + $parser->parseFile($tmpFilePath, ''); $content = $parser->getCss(); + gc_enable(); if (trim($content) === '') { $errorMessage = PHP_EOL . self::ERROR_MESSAGE_PREFIX . PHP_EOL . $path; diff --git a/lib/internal/Magento/Framework/Css/PreProcessor/Instruction/MagentoImport.php b/lib/internal/Magento/Framework/Css/PreProcessor/Instruction/MagentoImport.php index a07c7eea16aeb..6da9d71142453 100644 --- a/lib/internal/Magento/Framework/Css/PreProcessor/Instruction/MagentoImport.php +++ b/lib/internal/Magento/Framework/Css/PreProcessor/Instruction/MagentoImport.php @@ -5,15 +5,18 @@ */ namespace Magento\Framework\Css\PreProcessor\Instruction; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Css\PreProcessor\ErrorHandlerInterface; use Magento\Framework\View\Asset\File\FallbackContext; use Magento\Framework\View\Asset\LocalInterface; use Magento\Framework\View\Asset\PreProcessorInterface; +use Magento\Framework\View\Design\Theme\ThemeProviderInterface; use Magento\Framework\View\DesignInterface; use Magento\Framework\View\File\CollectorInterface; /** * @magento_import instruction preprocessor + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MagentoImport implements PreProcessorInterface { @@ -45,9 +48,15 @@ class MagentoImport implements PreProcessorInterface /** * @var \Magento\Framework\View\Design\Theme\ListInterface + * @deprecated */ protected $themeList; + /** + * @var ThemeProviderInterface + */ + private $themeProvider; + /** * @param DesignInterface $design * @param CollectorInterface $fileSource @@ -120,8 +129,23 @@ protected function getTheme(LocalInterface $asset) { $context = $asset->getContext(); if ($context instanceof FallbackContext) { - return $this->themeList->getThemeByFullPath($context->getAreaCode() . '/' . $context->getThemePath()); + return $this->getThemeProvider()->getThemeByFullPath( + $context->getAreaCode() . '/' . $context->getThemePath() + ); } return $this->design->getDesignTheme(); } + + /** + * @return ThemeProviderInterface + * @deprecated + */ + private function getThemeProvider() + { + if (null === $this->themeProvider) { + $this->themeProvider = ObjectManager::getInstance()->get(ThemeProviderInterface::class); + } + + return $this->themeProvider; + } } diff --git a/lib/internal/Magento/Framework/Css/Test/Unit/PreProcessor/Instruction/MagentoImportTest.php b/lib/internal/Magento/Framework/Css/Test/Unit/PreProcessor/Instruction/MagentoImportTest.php index ddaec4ef17e8b..a4f1c62d1bce5 100644 --- a/lib/internal/Magento/Framework/Css/Test/Unit/PreProcessor/Instruction/MagentoImportTest.php +++ b/lib/internal/Magento/Framework/Css/Test/Unit/PreProcessor/Instruction/MagentoImportTest.php @@ -8,6 +8,13 @@ namespace Magento\Framework\Css\Test\Unit\PreProcessor\Instruction; +use Magento\Framework\Css\PreProcessor\Instruction\MagentoImport; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Design\Theme\ThemeProviderInterface; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class MagentoImportTest extends \PHPUnit_Framework_TestCase { /** @@ -38,7 +45,7 @@ class MagentoImportTest extends \PHPUnit_Framework_TestCase /** * @var \Magento\Framework\View\Design\Theme\ListInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $themeList; + private $themeProvider; /** * @var \Magento\Framework\Css\PreProcessor\Instruction\Import @@ -55,14 +62,16 @@ protected function setUp() $this->asset = $this->getMock('\Magento\Framework\View\Asset\File', [], [], '', false); $this->asset->expects($this->any())->method('getContentType')->will($this->returnValue('css')); $this->assetRepo = $this->getMock('\Magento\Framework\View\Asset\Repository', [], [], '', false); - $this->themeList = $this->getMockForAbstractClass('\Magento\Framework\View\Design\Theme\ListInterface'); - $this->object = new \Magento\Framework\Css\PreProcessor\Instruction\MagentoImport( - $this->design, - $this->fileSource, - $this->errorHandler, - $this->assetRepo, - $this->themeList - ); + $this->themeProvider = $this->getMock(ThemeProviderInterface::class); + $this->object = (new ObjectManager($this))->getObject(MagentoImport::class, [ + 'design' => $this->design, + 'fileSource' => $this->fileSource, + 'errorHandler' => $this->errorHandler, + 'assetRepo' => $this->assetRepo, + ]); + $objectReflection = new \ReflectionProperty(MagentoImport::class, 'themeProvider'); + $objectReflection->setAccessible(true); + $objectReflection->setValue($this->object, $this->themeProvider); } /** @@ -88,7 +97,7 @@ public function testProcess($originalContent, $foundPath, $resolvedPath, $foundF ->will($this->returnValue($relatedAsset)); $relatedAsset->expects($this->once())->method('getContext')->will($this->returnValue($context)); $theme = $this->getMockForAbstractClass('\Magento\Framework\View\Design\ThemeInterface'); - $this->themeList->expects($this->once())->method('getThemeByFullPath')->will($this->returnValue($theme)); + $this->themeProvider->expects($this->once())->method('getThemeByFullPath')->will($this->returnValue($theme)); $files = []; foreach ($foundFiles as $file) { $fileObject = $this->getMock('Magento\Framework\View\File', [], [], '', false); diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index c6a54690734a0..40884c83bbaca 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -22,6 +22,7 @@ use Magento\Framework\Phrase; use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\StringUtils; +use Magento\Framework\DB\Query\Generator as QueryGenerator; /** * @SuppressWarnings(PHPMD.ExcessivePublicCount) @@ -183,6 +184,11 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface */ protected $logger; + /** + * @var QueryGenerator + */ + protected $queryGenerator; + /** * @param \Magento\Framework\Stdlib\StringUtils|String $string * @param DateTime $dateTime @@ -3329,57 +3335,32 @@ public function insertFromSelect(Select $select, $table, array $fields = [], $mo * @param int $stepCount * @return \Magento\Framework\DB\Select[] * @throws LocalizedException + * @deprecated */ public function selectsByRange($rangeField, \Magento\Framework\DB\Select $select, $stepCount = 100) { - $fromSelect = $select->getPart(\Magento\Framework\DB\Select::FROM); - if (empty($fromSelect)) { - throw new LocalizedException( - new \Magento\Framework\Phrase('Select object must have correct "FROM" part') - ); - } - - $tableName = []; - $correlationName = ''; - foreach ($fromSelect as $correlationName => $formPart) { - if ($formPart['joinType'] == \Magento\Framework\DB\Select::FROM) { - $tableName = $formPart['tableName']; - break; - } - } - - $selectRange = $this->select() - ->from( - $tableName, - [ - new \Zend_Db_Expr('MIN(' . $this->quoteIdentifier($rangeField) . ') AS min'), - new \Zend_Db_Expr('MAX(' . $this->quoteIdentifier($rangeField) . ') AS max'), - ] - ); - - $rangeResult = $this->fetchRow($selectRange); - $min = $rangeResult['min']; - $max = $rangeResult['max']; - + $iterator = $this->getQueryGenerator()->generate($rangeField, $select, $stepCount); $queries = []; - while ($min <= $max) { - $partialSelect = clone $select; - $partialSelect->where( - $this->quoteIdentifier($correlationName) . '.' - . $this->quoteIdentifier($rangeField) . ' >= ?', - $min - ) - ->where( - $this->quoteIdentifier($correlationName) . '.' - . $this->quoteIdentifier($rangeField) . ' < ?', - $min + $stepCount - ); - $queries[] = $partialSelect; - $min += $stepCount; + foreach ($iterator as $query) { + $queries[] = $query; } return $queries; } + /** + * Get query generator + * + * @return QueryGenerator + * @deprecated + */ + private function getQueryGenerator() + { + if ($this->queryGenerator === null) { + $this->queryGenerator = \Magento\Framework\App\ObjectManager::getInstance()->create(QueryGenerator::class); + } + return $this->queryGenerator; + } + /** * Get update table query using select object for join and update * diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIterator.php b/lib/internal/Magento/Framework/DB/Query/BatchIterator.php new file mode 100644 index 0000000000000..cf5e88c1b9944 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Query/BatchIterator.php @@ -0,0 +1,192 @@ +batchSize = $batchSize; + $this->select = $select; + $this->correlationName = $correlationName; + $this->rangeField = $rangeField; + $this->rangeFieldAlias = $rangeFieldAlias; + $this->connection = $select->getConnection(); + } + + /** + * @return Select + */ + public function current() + { + if (null == $this->currentSelect) { + $this->currentSelect = $this->initSelectObject(); + $itemsCount = $this->calculateBatchSize($this->currentSelect); + $this->isValid = $itemsCount > 0; + } + return $this->currentSelect; + } + + /** + * @return Select + */ + public function next() + { + if (null == $this->currentSelect) { + $this->current(); + } + $select = $this->initSelectObject(); + $itemsCountInSelect = $this->calculateBatchSize($select); + $this->isValid = $itemsCountInSelect > 0; + if ($this->isValid) { + $this->iteration++; + $this->currentSelect = $select; + } else { + $this->currentSelect = null; + } + return $this->currentSelect; + } + + /** + * @return int + */ + public function key() + { + return $this->iteration; + } + + /** + * @return bool + */ + public function valid() + { + return $this->isValid; + } + + /** + * @return void + */ + public function rewind() + { + $this->minValue = 0; + $this->currentSelect = null; + $this->iteration = 0; + $this->isValid = true; + } + + /** + * Calculate batch size for select. + * + * @param Select $select + * @return int + */ + private function calculateBatchSize(Select $select) + { + $wrapperSelect = $this->connection->select(); + $wrapperSelect->from( + $select, + [ + new \Zend_Db_Expr('MAX(' . $this->rangeFieldAlias . ') as max'), + new \Zend_Db_Expr('COUNT(*) as cnt') + ] + ); + $row = $this->connection->fetchRow($wrapperSelect); + $this->minValue = $row['max']; + return intval($row['cnt']); + } + + /** + * Initialize select object. + * + * @return \Magento\Framework\DB\Select + */ + private function initSelectObject() + { + $object = clone $this->select; + $object->where( + $this->connection->quoteIdentifier($this->correlationName) + . '.' . $this->connection->quoteIdentifier($this->rangeField) + . ' > ?', + $this->minValue + ); + $object->limit($this->batchSize); + /** + * Reset sort order section from origin select object + */ + $object->order($this->correlationName . '.' . $this->rangeField . ' ' . \Magento\Framework\DB\Select::SQL_ASC); + return $object; + } +} diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php new file mode 100644 index 0000000000000..a51a650197def --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php @@ -0,0 +1,51 @@ +objectManager = $objectManager; + $this->instanceName = $instanceName; + } + + /** + * Create class instance with specified parameters + * + * @param array $data + * @return \Magento\Framework\DB\Query\BatchIterator + */ + public function create(array $data = []) + { + return $this->objectManager->create($this->instanceName, $data); + } +} diff --git a/lib/internal/Magento/Framework/DB/Query/Generator.php b/lib/internal/Magento/Framework/DB/Query/Generator.php new file mode 100644 index 0000000000000..43f8388d3f449 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Query/Generator.php @@ -0,0 +1,79 @@ +iteratorFactory = $iteratorFactory; + } + + /** + * Generate select query list with predefined items count in each select item. + * + * @param string $rangeField + * @param \Magento\Framework\DB\Select $select + * @param int $batchSize + * @return BatchIterator + * @throws LocalizedException + */ + public function generate($rangeField, \Magento\Framework\DB\Select $select, $batchSize = 100) + { + $fromSelect = $select->getPart(\Magento\Framework\DB\Select::FROM); + if (empty($fromSelect)) { + throw new LocalizedException( + new \Magento\Framework\Phrase('Select object must have correct "FROM" part') + ); + } + + $fieldCorrelationName = ''; + foreach ($fromSelect as $correlationName => $fromPart) { + if ($fromPart['joinType'] == \Magento\Framework\DB\Select::FROM) { + $fieldCorrelationName = $correlationName; + break; + } + } + + $columns = $select->getPart(\Magento\Framework\DB\Select::COLUMNS); + /** + * Calculate $rangeField alias + */ + $rangeFieldAlias = $rangeField; + foreach ($columns as $column) { + list($table, $columnName, $alias) = $column; + if ($table == $fieldCorrelationName && $columnName == $rangeField) { + $rangeFieldAlias = $alias ?: $rangeField; + break; + } + } + + return $this->iteratorFactory->create( + [ + 'select' => $select, + 'batchSize' => $batchSize, + 'correlationName' => $fieldCorrelationName, + 'rangeField' => $rangeField, + 'rangeFieldAlias' => $rangeFieldAlias + ] + ); + } +} diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php index 748cfb4bef600..8359cd84a44c3 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php @@ -13,6 +13,7 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; +use Magento\Framework\DB\Query\Generator; class MysqlTest extends \PHPUnit_Framework_TestCase { @@ -73,7 +74,8 @@ protected function setUp() '_commit', '_rollBack', 'query', - 'fetchRow' + 'fetchRow', + 'getQueryGenerator' ], [ 'string' => $string, @@ -441,73 +443,31 @@ public function testInsertOnDuplicateWithQuotedColumnName() public function testSelectsByRange() { - $rangeField = 'test_id'; + $queryGenerator = $this->getMock(Generator::class, [], [], '', false, false); + + $resourceProperty = new \ReflectionProperty( + get_class($this->_adapter), + 'queryGenerator' + ); + $resourceProperty->setAccessible(true); + $resourceProperty->setValue($this->_adapter, $queryGenerator); + $tableName = 'test'; + $field = 'test_id'; + $select = $this->_adapter->select()->from($tableName); //min = 1; max 180; count: 120 - $this->_adapter->expects($this->once()) - ->method('fetchRow') - ->with( - $this->_adapter->select() - ->from( - $tableName, - [ - new \Zend_Db_Expr('MIN(' . $this->_adapter->quoteIdentifier($rangeField) . ') AS min'), - new \Zend_Db_Expr('MAX(' . $this->_adapter->quoteIdentifier($rangeField) . ') AS max'), - ] - ) - ) - ->will($this->returnValue(['min' => 1, 'max' => 200])); - $this->_adapter->expects($this->any()) - ->method('quote') - ->will( - $this->returnCallback( - function ($values) { - if (!is_array($values)) { - $values = [$values]; - } - foreach ($values as &$value) { - $value = "'" . $value . "'"; - } - return implode(',', $values); - } - ) - ); + $data = [ + $this->_adapter->select()->from($tableName)->where($field . '> ?', 0)->limit(50), //will return 0 ...61 + $this->_adapter->select()->from($tableName)->where($field . '> ?', 61)->limit(50), //will return 62 ...159 + $this->_adapter->select()->from($tableName)->where($field . '> ?', 159)->limit(50), //will return 160 ...180 + ]; - $expectedSelect = $this->_adapter->select() - ->from($tableName); - - $result = $this->_adapter->selectsByRange($rangeField, $expectedSelect, 50); - $this->assertCount(200/50, $result); - $prepareField = $this->_adapter->quoteIdentifier($tableName) - . '.' . $this->_adapter->quoteIdentifier($rangeField); - $this->assertEquals( - $this->_adapter->select() - ->from($tableName) - ->where($prepareField . ' >= ?', 1) - ->where($prepareField . ' < ?', 51), - $result[0] - ); - $this->assertEquals( - $this->_adapter->select() - ->from($tableName) - ->where($prepareField . ' >= ?', 51) - ->where($prepareField . ' < ?', 101), - $result[1] - ); - $this->assertEquals( - $this->_adapter->select() - ->from($tableName) - ->where($prepareField . ' >= ?', 101) - ->where($prepareField . ' < ?', 151), - $result[2] - ); - $this->assertEquals( - $this->_adapter->select() - ->from($tableName) - ->where($prepareField . ' >= ?', 151) - ->where($prepareField . ' < ?', 201), - $result[3] - ); + $queryGenerator->expects($this->once()) + ->method('generate') + ->with($field, $select, 50) + ->willReturn($data); + + $this->assertEquals($data, $this->_adapter->selectsByRange($field, $select, 50)); } /** diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php index 483434b1d8fc9..6dde49a65f0bc 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/Write.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/Write.php @@ -143,8 +143,6 @@ public function copyFile($path, $destination, WriteInterface $targetDirectory = */ public function createSymlink($path, $destination, WriteInterface $targetDirectory = null) { - $this->assertIsFile($path); - $targetDirectory = $targetDirectory ?: $this; $parentDirectory = $this->driver->getParentDirectory($destination); if (!$targetDirectory->isExist($parentDirectory)) { diff --git a/lib/internal/Magento/Framework/Filesystem/Directory/WriteInterface.php b/lib/internal/Magento/Framework/Filesystem/Directory/WriteInterface.php index 472c54a08d202..3fa0513dd21a0 100644 --- a/lib/internal/Magento/Framework/Filesystem/Directory/WriteInterface.php +++ b/lib/internal/Magento/Framework/Filesystem/Directory/WriteInterface.php @@ -48,7 +48,7 @@ public function renameFile($path, $newPath, WriteInterface $targetDirectory = nu public function copyFile($path, $destination, WriteInterface $targetDirectory = null); /** - * Creates symlink on a file and places it to destination + * Creates symlink on a file or directory and places it to destination * * @param string $path * @param string $destination diff --git a/lib/internal/Magento/Framework/Filesystem/Filter/ExcludeFilter.php b/lib/internal/Magento/Framework/Filesystem/Filter/ExcludeFilter.php new file mode 100644 index 0000000000000..a00bf449718ab --- /dev/null +++ b/lib/internal/Magento/Framework/Filesystem/Filter/ExcludeFilter.php @@ -0,0 +1,56 @@ +_filters = $filters; + } + + /** + * Check whether the current element of the iterator is acceptable + * + * @return bool + */ + public function accept() + { + $current = str_replace('\\', '/', $this->current()->__toString()); + $currentFilename = str_replace('\\', '/', $this->current()->getFilename()); + + if ($currentFilename == '.' || $currentFilename == '..') { + return false; + } + + foreach ($this->_filters as $filter) { + $filter = str_replace('\\', '/', $filter); + if (false !== strpos($current, $filter)) { + return false; + } + } + + return true; + } +} diff --git a/lib/internal/Magento/Framework/Filesystem/Test/Unit/File/ExcludeFilterTest.php b/lib/internal/Magento/Framework/Filesystem/Test/Unit/File/ExcludeFilterTest.php new file mode 100644 index 0000000000000..bca9b5b667685 --- /dev/null +++ b/lib/internal/Magento/Framework/Filesystem/Test/Unit/File/ExcludeFilterTest.php @@ -0,0 +1,57 @@ +iterator = $this->getFilesIterator(); + } + + public function testExclusion() + { + $iterator = new ExcludeFilter( + $this->iterator, + [ + BP . '/var/session/' + ] + ); + + foreach ($iterator as $i) { + $result[] = $i; + } + + $this->assertTrue(!in_array(BP . '/var/session/', $result), 'Filtered path should not be in array'); + } + + private function getFilesIterator () + { + $files = [ + BP . '/var/', + BP . '/var/session/', + BP . '/var/cache/' + ]; + + foreach ($files as $file) { + $item = $this->getMockBuilder('SplFileInfoClass')->setMethods(['__toString', 'getFilename'])->getMock(); + $item->expects($this->any())->method('__toString')->willReturn($file); + $item->expects($this->any())->method('getFilename')->willReturn('notDots'); + yield $item; + } + } +} diff --git a/lib/internal/Magento/Framework/Module/Dir/Reader.php b/lib/internal/Magento/Framework/Module/Dir/Reader.php index 7a8b1f11ef184..353f7e51c5906 100644 --- a/lib/internal/Magento/Framework/Module/Dir/Reader.php +++ b/lib/internal/Magento/Framework/Module/Dir/Reader.php @@ -46,6 +46,13 @@ class Reader */ protected $readFactory; + /** + * Found configuration files grouped by configuration types (filename). + * + * @var array + */ + private $fileIterators = []; + /** * @param Dir $moduleDirs * @param ModuleListInterface $moduleList @@ -65,24 +72,42 @@ public function __construct( } /** - * Go through all modules and find configuration files of active modules + * Go through all modules and find configuration files of active modules. * * @param string $filename * @return FileIterator */ public function getConfigurationFiles($filename) { - return $this->fileIteratorFactory->create($this->getFiles($filename, Dir::MODULE_ETC_DIR)); + return $this->getFilesIterator($filename, Dir::MODULE_ETC_DIR); } /** - * Go through all modules and find composer.json files of active modules + * Go through all modules and find composer.json files of active modules. * * @return FileIterator */ public function getComposerJsonFiles() { - return $this->fileIteratorFactory->create($this->getFiles('composer.json')); + return $this->getFilesIterator('composer.json'); + } + + /** + * Retrieve iterator for files with $filename from components located in component $subDir. + * + * @param string $filename + * @param string $subDir + * + * @return FileIterator + */ + private function getFilesIterator($filename, $subDir = '') + { + if (!isset($this->fileIterators[$subDir][$filename])) { + $this->fileIterators[$subDir][$filename] = $this->fileIteratorFactory->create( + $this->getFiles($filename, $subDir) + ); + } + return $this->fileIterators[$subDir][$filename]; } /** @@ -96,9 +121,9 @@ private function getFiles($filename, $subDir = '') { $result = []; foreach ($this->modulesList->getNames() as $moduleName) { - $moduleEtcDir = $this->getModuleDir($subDir, $moduleName); - $file = $moduleEtcDir . '/' . $filename; - $directoryRead = $this->readFactory->create($moduleEtcDir); + $moduleSubDir = $this->getModuleDir($subDir, $moduleName); + $file = $moduleSubDir . '/' . $filename; + $directoryRead = $this->readFactory->create($moduleSubDir); $path = $directoryRead->getRelativePath($file); if ($directoryRead->isExist($path)) { $result[] = $file; @@ -159,5 +184,6 @@ public function getModuleDir($type, $moduleName) public function setModuleDir($moduleName, $type, $path) { $this->customModuleDirs[$moduleName][$type] = $path; + $this->fileIterators = []; } } diff --git a/lib/internal/Magento/Framework/Test/Unit/App/ResourceConnectionTest.php b/lib/internal/Magento/Framework/Test/Unit/App/ResourceConnectionTest.php new file mode 100644 index 0000000000000..064faa7936964 --- /dev/null +++ b/lib/internal/Magento/Framework/Test/Unit/App/ResourceConnectionTest.php @@ -0,0 +1,102 @@ +deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->connectionFactoryMock = $this->getMockBuilder(ConnectionFactoryInterface::class) + ->getMock(); + + $this->configMock = $this->getMockBuilder(ConfigInterface::class)->getMock(); + + $this->objectManager = (new ObjectManager($this)); + $this->unit = $this->objectManager->getObject( + ResourceConnection::class, + [ + 'deploymentConfig' => $this->deploymentConfigMock, + 'connectionFactory' => $this->connectionFactoryMock, + 'resourceConfig' => $this->configMock, + ] + ); + } + + public function testGetConnectionByName() + { + $this->deploymentConfigMock->expects(self::once())->method('get') + ->with(ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTIONS . '/default') + ->willReturn(['config']); + $this->connectionFactoryMock->expects(self::once())->method('create') + ->with(['config']) + ->willReturn('connection'); + + self::assertEquals('connection', $this->unit->getConnectionByName('default')); + } + + public function testGetExistingConnectionByName() + { + $unit = $this->objectManager->getObject( + ResourceConnection::class, + [ + 'deploymentConfig' => $this->deploymentConfigMock, + ] + ); + $connectionProperty = new \ReflectionProperty( + ResourceConnection::class, + 'connections' + ); + $connectionProperty->setAccessible(true); + $connectionProperty->setValue($unit, ['default_process_' . getmypid() => 'existing_connection']); + $this->deploymentConfigMock->expects(self::never())->method('get'); + + self::assertEquals('existing_connection', $unit->getConnectionByName('default')); + } + + public function testCloseConnection() + { + $this->configMock->expects(self::once())->method('getConnectionName')->with('default'); + + $this->unit->closeConnection('default'); + + } +} diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchIteratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchIteratorTest.php new file mode 100644 index 0000000000000..e522daf97a802 --- /dev/null +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchIteratorTest.php @@ -0,0 +1,200 @@ +batchSize = 10; + $this->correlationName = 'correlationName'; + $this->rangeField = 'rangeField'; + $this->rangeFieldAlias = 'rangeFieldAlias'; + + $this->selectMock = $this->getMock(Select::class, [], [], '', false, false); + $this->wrapperSelectMock = $this->getMock(Select::class, [], [], '', false, false); + $this->connectionMock = $this->getMock(AdapterInterface::class); + $this->connectionMock->expects($this->any())->method('select')->willReturn($this->wrapperSelectMock); + $this->selectMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); + $this->connectionMock->expects($this->any())->method('quoteIdentifier')->willReturnArgument(0); + + $this->model = new BatchIterator( + $this->selectMock, + $this->batchSize, + $this->correlationName, + $this->rangeField, + $this->rangeFieldAlias + ); + } + + /** + * Test steps: + * 1. $iterator->current(); + * 2. $iterator->current(); + * 3. $iterator->key(); + * @return void + */ + public function testCurrent() + { + $filed = $this->correlationName . '.' . $this->rangeField; + + $this->selectMock->expects($this->once())->method('where')->with($filed . ' > ?', 0); + $this->selectMock->expects($this->once())->method('limit')->with($this->batchSize); + $this->selectMock->expects($this->once())->method('order')->with($filed . ' ASC'); + $this->assertEquals($this->selectMock, $this->model->current()); + $this->assertEquals($this->selectMock, $this->model->current()); + $this->assertEquals(0, $this->model->key()); + } + + /** + * SQL: select * from users + * Batch size: 10 + * IDS: [1 - 25] + * Items count: 25 + * Expected batches: [1-10, 11-20, 20-25] + * + * Test steps: + * 1. $iterator->rewind(); + * 2. $iterator->valid(); + * 3. $iterator->current(); + * 4. $iterator->key(); + * + * 1. $iterator->next() + * 2. $iterator->valid(); + * 3. $iterator->current(); + * 4. $iterator->key(); + * + * 1. $iterator->next() + * 2. $iterator->valid(); + * 3. $iterator->current(); + * 4. $iterator->key(); + * + * + * 1. $iterator->next() + * 2. $iterator->valid(); + * @return void + */ + public function testIterations() + { + $startCallIndex = 3; + $stepCall = 4; + + $this->connectionMock->expects($this->at($startCallIndex)) + ->method('fetchRow') + ->willReturn(['max' => 10, 'cnt' => 10]); + + $this->connectionMock->expects($this->at($startCallIndex += $stepCall)) + ->method('fetchRow') + ->willReturn(['max' => 20, 'cnt' => 10]); + + $this->connectionMock->expects($this->at($startCallIndex += $stepCall)) + ->method('fetchRow') + ->willReturn(['max' => 25, 'cnt' => 5]); + + $this->connectionMock->expects($this->at($startCallIndex += $stepCall)) + ->method('fetchRow') + ->willReturn(['max' => null, 'cnt' => 0]); + + /** + * Test 3 iterations + * [1-10, 11-20, 20-25] + */ + $iteration = 0; + $result = []; + foreach ($this->model as $key => $select) { + $result[] = $select; + $this->assertEquals($iteration, $key); + $iteration++; + } + $this->assertCount(3, $result); + } + + /** + * Test steps: + * 1. $iterator->next(); + * 2. $iterator->key() + * 3. $iterator->next(); + * 4. $iterator->current() + * 5. $iterator->key() + * @return void + */ + public function testNext() + { + $filed = $this->correlationName . '.' . $this->rangeField; + $this->selectMock->expects($this->at(0))->method('where')->with($filed . ' > ?', 0); + $this->selectMock->expects($this->exactly(3))->method('limit')->with($this->batchSize); + $this->selectMock->expects($this->exactly(3))->method('order')->with($filed . ' ASC'); + $this->selectMock->expects($this->at(3))->method('where')->with($filed . ' > ?', 25); + + $this->wrapperSelectMock->expects($this->exactly(3))->method('from')->with( + $this->selectMock, + [ + new \Zend_Db_Expr('MAX(' . $this->rangeFieldAlias . ') as max'), + new \Zend_Db_Expr('COUNT(*) as cnt') + ] + ); + $this->connectionMock->expects($this->exactly(3)) + ->method('fetchRow') + ->with($this->wrapperSelectMock) + ->willReturn(['max' => 25, 'cnt' => 10]); + + $this->assertEquals($this->selectMock, $this->model->next()); + $this->assertEquals(1, $this->model->key()); + + $this->assertEquals($this->selectMock, $this->model->next()); + $this->assertEquals($this->selectMock, $this->model->current()); + $this->assertEquals(2, $this->model->key()); + } +} diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php new file mode 100644 index 0000000000000..4be22eb14ca85 --- /dev/null +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php @@ -0,0 +1,168 @@ +factoryMock = $this->getMock(BatchIteratorFactory::class, [], [], '', false, false); + $this->selectMock = $this->getMock(Select::class, [], [], '', false, false); + $this->iteratorMock = $this->getMock(BatchIterator::class, [], [], '', false, false); + $this->model = new Generator($this->factoryMock); + } + + /** + * Test success generate. + * @return void + */ + public function testGenerate() + { + $map = [ + [ + Select::FROM, + [ + 'cp' => ['joinType' => Select::FROM] + ] + ], + [ + Select::COLUMNS, + [ + ['cp', 'entity_id', 'product_id'] + ] + ] + ]; + $this->selectMock->expects($this->exactly(2))->method('getPart')->willReturnMap($map); + $this->factoryMock->expects($this->once())->method('create')->with( + [ + 'select' => $this->selectMock, + 'batchSize' => 100, + 'correlationName' => 'cp', + 'rangeField' => 'entity_id', + 'rangeFieldAlias' => 'product_id' + ] + )->willReturn($this->iteratorMock); + $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); + } + + /** + * Test batch generation with invalid select object. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Select object must have correct "FROM" part + * @return void + */ + public function testGenerateWithoutFromPart() + { + $map = [ + [Select::FROM, []], + [ + Select::COLUMNS, + [ + ['cp', 'entity_id', 'product_id'] + ] + ] + ]; + $this->selectMock->expects($this->any())->method('getPart')->willReturnMap($map); + $this->factoryMock->expects($this->never())->method('create'); + $this->model->generate('entity_id', $this->selectMock, 100); + } + + /** + * Test generate batches with rangeField without alias. + * @return void + */ + public function testGenerateWithRangeFieldWithoutAlias() + { + $map = [ + [ + Select::FROM, + [ + 'cp' => ['joinType' => Select::FROM] + ] + ], + [ + Select::COLUMNS, + [ + ['cp', 'entity_id', null] + ] + ] + ]; + $this->selectMock->expects($this->exactly(2))->method('getPart')->willReturnMap($map); + $this->factoryMock->expects($this->once())->method('create')->with( + [ + 'select' => $this->selectMock, + 'batchSize' => 100, + 'correlationName' => 'cp', + 'rangeField' => 'entity_id', + 'rangeFieldAlias' => 'entity_id' + ] + )->willReturn($this->iteratorMock); + $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); + } + + /** + * Test generate batches with wild-card. + * + * @return void + */ + public function testGenerateWithInvalidWithWildcard() + { + $map = [ + [ + Select::FROM, + [ + 'cp' => ['joinType' => Select::FROM] + ] + ], + [ + Select::COLUMNS, + [ + ['cp', '*', null] + ] + ] + ]; + $this->selectMock->expects($this->exactly(2))->method('getPart')->willReturnMap($map); + $this->factoryMock->expects($this->once())->method('create')->with( + [ + 'select' => $this->selectMock, + 'batchSize' => 100, + 'correlationName' => 'cp', + 'rangeField' => 'entity_id', + 'rangeFieldAlias' => 'entity_id' + ] + )->willReturn($this->iteratorMock); + $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); + } +} diff --git a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php index 7f06f5ee95917..aa1eaa3b1f7c5 100644 --- a/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/TranslateTest.php @@ -170,8 +170,9 @@ public function testLoadData($area, $forceReload, $cachedData) ]; $this->resource->expects($this->any())->method('getTranslationArray')->will($this->returnValue($dbData)); - $this->cache->expects($this->exactly(1)) - ->method('save'); + if (!$forceReload) { + $this->cache->expects($this->exactly(1))->method('save'); + } $this->translate->loadData($area, $forceReload); diff --git a/lib/internal/Magento/Framework/Translate.php b/lib/internal/Magento/Framework/Translate.php index 0b10a4151f459..5d3cf722761df 100644 --- a/lib/internal/Magento/Framework/Translate.php +++ b/lib/internal/Magento/Framework/Translate.php @@ -181,7 +181,9 @@ public function loadData($area = null, $forceReload = false) $this->_loadPackTranslation(); $this->_loadDbTranslation(); - $this->_saveCache(); + if (!$forceReload) { + $this->_saveCache(); + } return $this; } diff --git a/lib/internal/Magento/Framework/View/Asset/Bundle.php b/lib/internal/Magento/Framework/View/Asset/Bundle.php index 002382c6a0e70..2f0c101f88e6a 100644 --- a/lib/internal/Magento/Framework/View/Asset/Bundle.php +++ b/lib/internal/Magento/Framework/View/Asset/Bundle.php @@ -26,7 +26,7 @@ class Bundle */ protected $assetsContent = []; - /** @var Bundle\Config */ + /** @var Bundle\ConfigInterface */ protected $bundleConfig; /** @@ -261,6 +261,7 @@ protected function save($types) $assetsParts = reset($parts); $context = reset($assetsParts['assets'])->getContext(); $bundlePath = empty($bundlePath) ? $context->getPath() . Manager::BUNDLE_PATH : $bundlePath; + $dir->delete($context->getPath() . DIRECTORY_SEPARATOR . Manager::BUNDLE_JS_DIR); $this->fillContent($parts, $context); } diff --git a/lib/internal/Magento/Framework/View/Asset/Bundle/Config.php b/lib/internal/Magento/Framework/View/Asset/Bundle/Config.php index 21e974fca57e0..ebd0a210052ce 100644 --- a/lib/internal/Magento/Framework/View/Asset/Bundle/Config.php +++ b/lib/internal/Magento/Framework/View/Asset/Bundle/Config.php @@ -6,10 +6,12 @@ namespace Magento\Framework\View\Asset\Bundle; +use Magento\Framework\App\ObjectManager; use Magento\Framework\View; use Magento\Framework\View\Asset\Bundle; use Magento\Framework\View\Design\Theme\ListInterface; use Magento\Framework\View\Asset\File\FallbackContext; +use Magento\Framework\View\Design\Theme\ThemeProviderInterface; class Config implements Bundle\ConfigInterface { @@ -30,6 +32,16 @@ class Config implements Bundle\ConfigInterface */ protected $viewConfig; + /** + * @var ThemeProviderInterface + */ + private $themeProvider; + + /** + * @var \Magento\Framework\Config\View[] + */ + private $config = []; + /** * @param View\ConfigInterface $viewConfig * @param ListInterface $themeList @@ -57,12 +69,17 @@ public function isSplit(FallbackContext $assetContext) */ public function getConfig(FallbackContext $assetContext) { - return $this->viewConfig->getViewConfig([ - 'area' => $assetContext->getAreaCode(), - 'themeModel' => $this->themeList->getThemeByFullPath( - $assetContext->getAreaCode() . '/' . $assetContext->getThemePath() - ) - ]); + $themePath = $assetContext->getAreaCode() . '/' . $assetContext->getThemePath(); + if (!isset($this->config[$themePath])) { + $this->config[$themePath] = $this->viewConfig->getViewConfig([ + 'area' => $assetContext->getAreaCode(), + 'themeModel' => $this->getThemeProvider()->getThemeByFullPath( + $themePath + ) + ]); + } + + return $this->config[$themePath]; } /** @@ -83,7 +100,20 @@ public function getPartSize(FallbackContext $assetContext) case 'MB': return (int)$size * 1024; default: - return (int)$size / 1024; + return (int)($size / 1024); } } + + /** + * @return ThemeProviderInterface + * @deprecated + */ + private function getThemeProvider() + { + if (null === $this->themeProvider) { + $this->themeProvider = ObjectManager::getInstance()->get(ThemeProviderInterface::class); + } + + return $this->themeProvider; + } } diff --git a/lib/internal/Magento/Framework/View/Asset/Bundle/Manager.php b/lib/internal/Magento/Framework/View/Asset/Bundle/Manager.php index 2e09ea6ca92c7..0b072eddf08ce 100644 --- a/lib/internal/Magento/Framework/View/Asset/Bundle/Manager.php +++ b/lib/internal/Magento/Framework/View/Asset/Bundle/Manager.php @@ -18,6 +18,8 @@ */ class Manager { + const BUNDLE_JS_DIR = 'js/bundle'; + const BUNDLE_PATH = '/js/bundle/bundle'; const ASSET_TYPE_JS = 'js'; @@ -41,6 +43,7 @@ class Manager /** @var array */ public static $availableTypes = [self::ASSET_TYPE_JS, self::ASSET_TYPE_HTML]; + /** * @var Asset\Minification */ @@ -127,7 +130,7 @@ protected function isExcludedFile($filePath, $asset) /** @var $asset LocalInterface */ $filePathInfo = $this->splitPath($filePath); if ($filePathInfo && $this->compareModules($filePathInfo, $asset)) { - return $asset->getSourceFile() == $filePathInfo['excludedPath']; + return $asset->getFilePath() == $filePathInfo['excludedPath']; } return false; } @@ -176,7 +179,7 @@ protected function splitPath($path) */ public function addAsset(LocalInterface $asset) { - if (!($this->isValidAsset($asset))) { + if (!$this->isValidAsset($asset)) { return false; } @@ -237,14 +240,9 @@ protected function isValidType(LocalInterface $asset) return false; } - if ($type == self::ASSET_TYPE_HTML) { - return $asset->getModule() !== ''; - } - return true; } - /** * Flush bundle * diff --git a/lib/internal/Magento/Framework/View/Asset/LockerProcess.php b/lib/internal/Magento/Framework/View/Asset/LockerProcess.php index 01b186ac67772..e8938211fecbf 100644 --- a/lib/internal/Magento/Framework/View/Asset/LockerProcess.php +++ b/lib/internal/Magento/Framework/View/Asset/LockerProcess.php @@ -9,6 +9,8 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\App\State; +use Magento\Framework\App\ObjectManager; /** * Class LockerProcess @@ -40,6 +42,11 @@ class LockerProcess implements LockerProcessInterface */ private $tmpDirectory; + /** + * @var State + */ + private $state; + /** * Constructor * @@ -52,19 +59,21 @@ public function __construct(Filesystem $filesystem) /** * @inheritdoc - * @throws FileSystemException */ public function lockProcess($lockName) { + if ($this->getState()->getMode() == State::MODE_PRODUCTION) { + return; + } + $this->tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); $this->lockFilePath = $this->getFilePath($lockName); while ($this->isProcessLocked()) { - sleep(1); + usleep(1000); } $this->tmpDirectory->writeFile($this->lockFilePath, time()); - } /** @@ -73,6 +82,10 @@ public function lockProcess($lockName) */ public function unlockProcess() { + if ($this->getState()->getMode() == State::MODE_PRODUCTION) { + return ; + } + $this->tmpDirectory->delete($this->lockFilePath); } @@ -80,14 +93,18 @@ public function unlockProcess() * Check whether generation process has already locked * * @return bool - * @throws FileSystemException */ private function isProcessLocked() { if ($this->tmpDirectory->isExist($this->lockFilePath)) { - $lockTime = (int) $this->tmpDirectory->readFile($this->lockFilePath); - if ((time() - $lockTime) >= self::MAX_LOCK_TIME) { - $this->tmpDirectory->delete($this->lockFilePath); + try { + $lockTime = (int)$this->tmpDirectory->readFile($this->lockFilePath); + if ((time() - $lockTime) >= self::MAX_LOCK_TIME) { + $this->tmpDirectory->delete($this->lockFilePath); + + return false; + } + } catch (FileSystemException $e) { return false; } @@ -108,4 +125,16 @@ private function getFilePath($name) { return DirectoryList::TMP . DIRECTORY_SEPARATOR . $name . self::LOCK_EXTENSION; } + + /** + * @return State + * @deprecated + */ + private function getState() + { + if (null === $this->state) { + $this->state = ObjectManager::getInstance()->get(State::class); + } + return $this->state; + } } diff --git a/lib/internal/Magento/Framework/View/Asset/Minification.php b/lib/internal/Magento/Framework/View/Asset/Minification.php index 255c9690e3fa9..1e32e32b99676 100644 --- a/lib/internal/Magento/Framework/View/Asset/Minification.php +++ b/lib/internal/Magento/Framework/View/Asset/Minification.php @@ -21,18 +21,21 @@ class Minification * @var ScopeConfigInterface */ private $scopeConfig; + /** * @var State */ private $appState; + /** * @var string */ private $scope; + /** * @var array */ - private $excludes = []; + private $configCache = []; /** * @param ScopeConfigInterface $scopeConfig @@ -54,12 +57,16 @@ public function __construct(ScopeConfigInterface $scopeConfig, State $appState, */ public function isEnabled($contentType) { - return - $this->appState->getMode() != State::MODE_DEVELOPER && - (bool)$this->scopeConfig->isSetFlag( - sprintf(self::XML_PATH_MINIFICATION_ENABLED, $contentType), - $this->scope - ); + if (!isset($this->configCache[self::XML_PATH_MINIFICATION_ENABLED][$contentType])) { + $this->configCache[self::XML_PATH_MINIFICATION_ENABLED][$contentType] = + $this->appState->getMode() != State::MODE_DEVELOPER && + (bool)$this->scopeConfig->isSetFlag( + sprintf(self::XML_PATH_MINIFICATION_ENABLED, $contentType), + $this->scope + ); + } + + return $this->configCache[self::XML_PATH_MINIFICATION_ENABLED][$contentType]; } /** @@ -131,15 +138,15 @@ public function isExcluded($filename) */ public function getExcludes($contentType) { - if (!isset($this->excludes[$contentType])) { - $this->excludes[$contentType] = []; + if (!isset($this->configCache[self::XML_PATH_MINIFICATION_EXCLUDES][$contentType])) { + $this->configCache[self::XML_PATH_MINIFICATION_EXCLUDES][$contentType] = []; $key = sprintf(self::XML_PATH_MINIFICATION_EXCLUDES, $contentType); foreach (explode("\n", $this->scopeConfig->getValue($key, $this->scope)) as $exclude) { if (trim($exclude) != '') { - $this->excludes[$contentType][] = trim($exclude); + $this->configCache[self::XML_PATH_MINIFICATION_EXCLUDES][$contentType][] = trim($exclude); } }; } - return $this->excludes[$contentType]; + return $this->configCache[self::XML_PATH_MINIFICATION_EXCLUDES][$contentType]; } } diff --git a/lib/internal/Magento/Framework/View/Asset/Repository.php b/lib/internal/Magento/Framework/View/Asset/Repository.php index c4171b153e089..e75a728610c5a 100644 --- a/lib/internal/Magento/Framework/View/Asset/Repository.php +++ b/lib/internal/Magento/Framework/View/Asset/Repository.php @@ -8,7 +8,8 @@ use Magento\Framework\UrlInterface; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Filesystem; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Design\Theme\ThemeProviderInterface; /** * A repository service for view assets @@ -33,6 +34,7 @@ class Repository /** * @var \Magento\Framework\View\Design\Theme\ListInterface + * @deprecated */ private $themeList; @@ -70,11 +72,17 @@ class Repository * @var File\ContextFactory */ private $contextFactory; + /** * @var RemoteFactory */ private $remoteFactory; + /** + * @var ThemeProviderInterface + */ + private $themeProvider; + /** * @param \Magento\Framework\UrlInterface $baseUrl * @param \Magento\Framework\View\DesignInterface $design @@ -136,7 +144,7 @@ public function updateDesignParams(array &$params) } if ($theme) { - $params['themeModel'] = $this->themeList->getThemeByFullPath($area . '/' . $theme); + $params['themeModel'] = $this->getThemeProvider()->getThemeByFullPath($area . '/' . $theme); if (!$params['themeModel']) { throw new \UnexpectedValueException("Could not find theme '$theme' for area '$area'"); } @@ -156,6 +164,19 @@ public function updateDesignParams(array &$params) return $this; } + /** + * @return ThemeProviderInterface + * @deprecated + */ + private function getThemeProvider() + { + if (null === $this->themeProvider) { + $this->themeProvider = ObjectManager::getInstance()->get(ThemeProviderInterface::class); + } + + return $this->themeProvider; + } + /** * Get default design parameter * diff --git a/lib/internal/Magento/Framework/View/Asset/Source.php b/lib/internal/Magento/Framework/View/Asset/Source.php index fa27473c1343b..b70c423364a6d 100644 --- a/lib/internal/Magento/Framework/View/Asset/Source.php +++ b/lib/internal/Magento/Framework/View/Asset/Source.php @@ -9,6 +9,8 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\View\Asset\PreProcessor\ChainFactoryInterface; use Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple; +use Magento\Framework\View\Design\Theme\ThemeProviderInterface; +use Magento\Framework\App\ObjectManager; /** * A service for preprocessing content of assets @@ -44,9 +46,15 @@ class Source /** * @var \Magento\Framework\View\Design\Theme\ListInterface + * @deprecated */ private $themeList; + /** + * @var ThemeProviderInterface + */ + private $themeProvider; + /** * @var ChainFactoryInterface */ @@ -189,7 +197,9 @@ private function findFileThroughFallback( LocalInterface $asset, \Magento\Framework\View\Asset\File\FallbackContext $context ) { - $themeModel = $this->themeList->getThemeByFullPath($context->getAreaCode() . '/' . $context->getThemePath()); + $themeModel = $this->getThemeProvider()->getThemeByFullPath( + $context->getAreaCode() . '/' . $context->getThemePath() + ); $sourceFile = $this->fallback->getFile( $context->getAreaCode(), $themeModel, @@ -200,6 +210,19 @@ private function findFileThroughFallback( return $sourceFile; } + /** + * @return ThemeProviderInterface + * @deprecated + */ + private function getThemeProvider() + { + if (null === $this->themeProvider) { + $this->themeProvider = ObjectManager::getInstance()->get(ThemeProviderInterface::class); + } + + return $this->themeProvider; + } + /** * Find asset file by simply appending its path to the directory in context * diff --git a/lib/internal/Magento/Framework/View/Config.php b/lib/internal/Magento/Framework/View/Config.php index c8960cb37845e..b72fe87c4acc4 100644 --- a/lib/internal/Magento/Framework/View/Config.php +++ b/lib/internal/Magento/Framework/View/Config.php @@ -57,16 +57,25 @@ public function __construct( public function getViewConfig(array $params = []) { $this->assetRepo->updateDesignParams($params); - /** @var $currentTheme \Magento\Framework\View\Design\ThemeInterface */ - $currentTheme = $params['themeModel']; - $key = $currentTheme->getCode(); - if (isset($this->viewConfigs[$key])) { - return $this->viewConfigs[$key]; + $viewConfigParams = []; + + if (isset($params['themeModel'])) { + /** @var \Magento\Framework\View\Design\ThemeInterface $currentTheme */ + $currentTheme = $params['themeModel']; + $key = $currentTheme->getFullPath(); + if (isset($this->viewConfigs[$key])) { + return $this->viewConfigs[$key]; + } + $viewConfigParams['themeModel'] = $currentTheme; } + $viewConfigParams['area'] = (isset($params['area'])) ? $params['area'] : null; - $config = $this->viewConfigFactory->create(); + /** @var \Magento\Framework\Config\View $config */ + $config = $this->viewConfigFactory->create($viewConfigParams); - $this->viewConfigs[$key] = $config; + if (isset($key)) { + $this->viewConfigs[$key] = $config; + } return $config; } } diff --git a/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Minification.php b/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Minification.php index 03d51faea2c69..aef2270c0957f 100644 --- a/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Minification.php +++ b/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Minification.php @@ -38,6 +38,7 @@ public function __construct(ResolverInterface $fallback, AssetMinification $mini $this->fallback = $fallback; $this->minification = $minification; } + /** * Get path of file after using fallback rules * @@ -51,8 +52,9 @@ public function __construct(ResolverInterface $fallback, AssetMinification $mini */ public function resolve($type, $file, $area = null, ThemeInterface $theme = null, $locale = null, $module = null) { + $file = $this->minification->addMinifiedSign($file); $path = $this->fallback->resolve($type, $file, $area, $theme, $locale, $module); - if (!$path && $file != ($newFile = $this->minification->removeMinifiedSign($file))) { + if (!$path && ($newFile = $this->minification->removeMinifiedSign($file))) { $path = $this->fallback->resolve($type, $newFile, $area, $theme, $locale, $module); } return $path; diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/Bundle/ManagerTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/Bundle/ManagerTest.php index a9d06fb33309a..cb161a5c204df 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/Bundle/ManagerTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/Bundle/ManagerTest.php @@ -30,23 +30,23 @@ class ManagerTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Framework\View\Asset\Minification|\PHPUnit_Framework_MockObject_MockObject */ private $minificationMock; - public function setUp() + protected function setUp() { - $this->filesystem = $this->getMockBuilder('Magento\Framework\Filesystem') + $this->filesystem = $this->getMockBuilder(\Magento\Framework\Filesystem::class) ->disableOriginalConstructor() ->getMock(); - $this->bundle = $this->getMockBuilder('Magento\Framework\View\Asset\Bundle') + $this->bundle = $this->getMockBuilder(\Magento\Framework\View\Asset\Bundle::class) ->disableOriginalConstructor() ->getMock(); - $this->bundleConfig = $this->getMockBuilder('Magento\Framework\View\Asset\Bundle\ConfigInterface') + $this->bundleConfig = $this->getMockBuilder(\Magento\Framework\View\Asset\Bundle\ConfigInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->assetConfig = $this->getMockBuilder('Magento\Framework\View\Asset\ConfigInterface') + $this->assetConfig = $this->getMockBuilder(\Magento\Framework\View\Asset\ConfigInterface::class) ->disableOriginalConstructor() ->getMock(); $this->asset = $this->getMockForAbstractClass( - 'Magento\Framework\View\Asset\LocalInterface', + \Magento\Framework\View\Asset\LocalInterface::class, [], '', false, @@ -55,7 +55,7 @@ public function setUp() ['getContentType'] ); - $this->minificationMock = $this->getMockBuilder('Magento\Framework\View\Asset\Minification') + $this->minificationMock = $this->getMockBuilder(\Magento\Framework\View\Asset\Minification::class) ->disableOriginalConstructor() ->getMock(); @@ -77,27 +77,15 @@ public function testAddAssetWithInvalidType() $this->assertFalse($this->manager->addAsset($this->asset)); } - public function testAddAssetWithHtmlTypeAndWithoutModule() - { - $this->asset->expects($this->once()) - ->method('getContentType') - ->willReturn('html'); - $this->asset->expects($this->once()) - ->method('getModule') - ->willReturn(''); - - $this->assertFalse($this->manager->addAsset($this->asset)); - } - public function testAddAssetWithExcludedFile() { - $dirRead = $this->getMockBuilder('Magento\Framework\Filesystem\Directory\ReadInterface') + $dirRead = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\ReadInterface::class) ->disableOriginalConstructor() ->getMock(); - $context = $this->getMockBuilder('Magento\Framework\View\Asset\File\FallbackContext') + $context = $this->getMockBuilder(\Magento\Framework\View\Asset\File\FallbackContext::class) ->disableOriginalConstructor() ->getMock(); - $configView = $this->getMockBuilder('Magento\Framework\Config\View') + $configView = $this->getMockBuilder(\Magento\Framework\Config\View::class) ->setMockClassName('configView') ->disableOriginalConstructor() ->getMock(); @@ -114,6 +102,9 @@ public function testAddAssetWithExcludedFile() $this->asset->expects($this->atLeastOnce()) ->method('getSourceFile') ->willReturn('source/file.min.js'); + $this->asset->expects($this->atLeastOnce()) + ->method('getFilePath') + ->willReturn('source/file.min.js'); $this->filesystem->expects($this->once()) ->method('getDirectoryRead') ->with(\Magento\Framework\App\Filesystem\DirectoryList::APP) @@ -131,13 +122,13 @@ public function testAddAssetWithExcludedFile() public function testAddAssetWithExcludedDirectory() { - $dirRead = $this->getMockBuilder('Magento\Framework\Filesystem\Directory\ReadInterface') + $dirRead = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\ReadInterface::class) ->disableOriginalConstructor() ->getMock(); - $context = $this->getMockBuilder('Magento\Framework\View\Asset\File\FallbackContext') + $context = $this->getMockBuilder(\Magento\Framework\View\Asset\File\FallbackContext::class) ->disableOriginalConstructor() ->getMock(); - $configView = $this->getMockBuilder('Magento\Framework\Config\View') + $configView = $this->getMockBuilder(\Magento\Framework\Config\View::class) ->disableOriginalConstructor() ->getMock(); @@ -183,13 +174,13 @@ public function testAddAssetWithExcludedDirectory() public function testAddAsset() { - $dirRead = $this->getMockBuilder('Magento\Framework\Filesystem\Directory\ReadInterface') + $dirRead = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\ReadInterface::class) ->disableOriginalConstructor() ->getMock(); - $context = $this->getMockBuilder('Magento\Framework\View\Asset\File\FallbackContext') + $context = $this->getMockBuilder(\Magento\Framework\View\Asset\File\FallbackContext::class) ->disableOriginalConstructor() ->getMock(); - $configView = $this->getMockBuilder('Magento\Framework\Config\View') + $configView = $this->getMockBuilder(\Magento\Framework\Config\View::class) ->disableOriginalConstructor() ->getMock(); diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/BundleTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/BundleTest.php index 8b1cdbb74fde1..8bfc7ba6f8c91 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/BundleTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/BundleTest.php @@ -68,12 +68,12 @@ public function testMinSuffix() ->withConsecutive( ['onefile.js'], ['onefile.js'], - ['/js/bundle/bundle0.js'] + ['path-to-theme/js/bundle/bundle0.js'] ) ->willReturnOnConsecutiveCalls( 'onefile.min.js', 'onefile.min.js', - '/js/bundle/bundle0.min.js' + 'path-to-theme/js/bundle/bundle0.min.js' ); $contextMock = $this->getMockBuilder('Magento\Framework\View\Asset\File\FallbackContext') @@ -91,6 +91,10 @@ public function testMinSuffix() ->expects($this->any()) ->method('getLocale') ->willReturn('locale'); + $contextMock + ->expects($this->any()) + ->method('getPath') + ->willReturn('path-to-theme'); $assetMock = $this->getMockBuilder('Magento\Framework\View\Asset\LocalInterface') ->setMethods(['getContentType', 'getContext']) @@ -110,10 +114,14 @@ public function testMinSuffix() $writeMock = $this->getMockBuilder('Magento\Framework\Filesystem\Directory\WriteInterface') ->getMockForAbstractClass(); + $writeMock + ->expects($this->once()) + ->method('delete') + ->with('path-to-theme' . DIRECTORY_SEPARATOR . \Magento\Framework\View\Asset\Bundle\Manager::BUNDLE_JS_DIR); $writeMock ->expects($this->once()) ->method('writeFile') - ->with('/js/bundle/bundle0.min.js', $this->stringContains('onefile.min.js')); + ->with('path-to-theme/js/bundle/bundle0.min.js', $this->stringContains('onefile.min.js')); $this->filesystemMock ->expects($this->any()) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/LockerProcessTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/LockerProcessTest.php index 91666d0838cea..49314f9a23f8f 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/LockerProcessTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/LockerProcessTest.php @@ -9,6 +9,8 @@ use Magento\Framework\View\Asset\LockerProcess; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\App\State; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Class LockerProcessTest @@ -34,6 +36,11 @@ class LockerProcessTest extends \PHPUnit_Framework_TestCase */ private $filesystemMock; + /** + * @var State|\PHPUnit_Framework_MockObject_MockObject + */ + private $stateMock; + /** * Set up */ @@ -44,8 +51,20 @@ protected function setUp() $this->filesystemMock = $this->getMockBuilder('Magento\Framework\Filesystem') ->disableOriginalConstructor() ->getMock(); + $this->stateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->getMock(); - $this->lockerProcess = new LockerProcess($this->filesystemMock); + $this->lockerProcess = (new ObjectManager($this))->getObject( + LockerProcess::class, + [ + 'filesystem' => $this->filesystemMock, + ] + ); + $reflection = new \ReflectionClass(LockerProcess::class); + $reflectionProperty = $reflection->getProperty('state'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->lockerProcess, $this->stateMock); } /** @@ -57,6 +76,7 @@ protected function setUp() */ public function testLockProcess($method) { + $this->stateMock->expects(self::once())->method('getMode')->willReturn(State::MODE_DEVELOPER); $this->filesystemMock->expects(self::once()) ->method('getDirectoryWrite') ->with(DirectoryList::VAR_DIR) @@ -65,11 +85,20 @@ public function testLockProcess($method) $this->lockerProcess->lockProcess(self::LOCK_NAME); } + public function testNotLockProcessInProductionMode() + { + $this->stateMock->expects(self::once())->method('getMode')->willReturn(State::MODE_PRODUCTION); + $this->filesystemMock->expects($this->never())->method('getDirectoryWrite'); + + $this->lockerProcess->lockProcess(self::LOCK_NAME); + } + /** * Test for unlockProcess method */ public function testUnlockProcess() { + $this->stateMock->expects(self::exactly(2))->method('getMode')->willReturn(State::MODE_DEVELOPER); $this->filesystemMock->expects(self::once()) ->method('getDirectoryWrite') ->with(DirectoryList::VAR_DIR) @@ -79,6 +108,15 @@ public function testUnlockProcess() $this->lockerProcess->unlockProcess(); } + public function testNotUnlockProcessInProductionMode() + { + $this->stateMock->expects(self::exactly(2))->method('getMode')->willReturn(State::MODE_PRODUCTION); + $this->filesystemMock->expects(self::never())->method('getDirectoryWrite'); + + $this->lockerProcess->lockProcess(self::LOCK_NAME); + $this->lockerProcess->unlockProcess(); + } + /** * @return array */ diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php index cadb00c259fff..7aad0b9b6df15 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/RepositoryTest.php @@ -6,10 +6,13 @@ namespace Magento\Framework\View\Test\Unit\Asset; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Asset\Repository; +use Magento\Framework\View\Design\Theme\ThemeProviderInterface; /** * Unit test for Magento\Framework\View\Asset\Repository + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class RepositoryTest extends \PHPUnit_Framework_TestCase { @@ -31,7 +34,7 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase /** * @var \Magento\Framework\View\Design\Theme\ListInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $listMock; + private $themeProvider; /** * @var \Magento\Framework\View\Asset\Source|\PHPUnit_Framework_MockObject_MockObject @@ -74,7 +77,7 @@ protected function setUp() $this->designMock = $this->getMockBuilder('Magento\Framework\View\DesignInterface') ->disableOriginalConstructor() ->getMock(); - $this->listMock = $this->getMockBuilder('Magento\Framework\View\Design\Theme\ListInterface') + $this->themeProvider = $this->getMockBuilder(ThemeProviderInterface::class) ->disableOriginalConstructor() ->getMock(); $this->sourceMock = $this->getMockBuilder('Magento\Framework\View\Asset\Source') @@ -99,17 +102,21 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->repository = new Repository( - $this->urlMock, - $this->designMock, - $this->listMock, - $this->sourceMock, - $this->httpMock, - $this->fileFactoryMock, - $this->fallbackFactoryMock, - $this->contextFactoryMock, - $this->remoteFactoryMock - ); + $this->repository = (new ObjectManager($this))->getObject(Repository::class, [ + 'baseUrl' => $this->urlMock, + 'design' => $this->designMock, + 'assetSource' => $this->sourceMock, + 'request' => $this->httpMock, + 'fileFactory' => $this->fileFactoryMock, + 'fallbackContextFactory' => $this->fallbackFactoryMock, + 'contextFactory' => $this->contextFactoryMock, + 'remoteFactory' => $this->remoteFactoryMock + ]); + + $reflection = new \ReflectionClass(Repository::class); + $reflectionProperty = $reflection->getProperty('themeProvider'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->repository, $this->themeProvider); } /** @@ -120,7 +127,7 @@ protected function setUp() public function testUpdateDesignParamsWrongTheme() { $params = ['area' => 'area', 'theme' => 'nonexistent_theme']; - $this->listMock->expects($this->once()) + $this->themeProvider->expects($this->once()) ->method('getThemeByFullPath') ->with('area/nonexistent_theme') ->will($this->returnValue(null)); @@ -135,7 +142,7 @@ public function testUpdateDesignParamsWrongTheme() */ public function testUpdateDesignParams($params, $result) { - $this->listMock + $this->themeProvider ->expects($this->any()) ->method('getThemeByFullPath') ->willReturn('ThemeID'); @@ -165,7 +172,7 @@ public function updateDesignParamsDataProvider() */ public function testCreateAsset() { - $this->listMock + $this->themeProvider ->expects($this->any()) ->method('getThemeByFullPath') ->willReturnArgument(0); @@ -227,7 +234,7 @@ public function testGetStaticViewFileContext() 'locale' => 'locale' ] ); - $this->listMock + $this->themeProvider ->expects($this->any()) ->method('getThemeByFullPath') ->willReturnArgument(0); diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php index eecafdfe6dd46..3e7549e881616 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php @@ -10,9 +10,11 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem\DriverPool; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Asset\PreProcessor\ChainFactoryInterface; use Magento\Framework\View\Asset\PreProcessor\Chain; use Magento\Framework\View\Asset\Source; +use Magento\Framework\View\Design\Theme\ThemeProviderInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -90,17 +92,25 @@ protected function setUp() ->method('create') ->willReturn($this->chain); - $themeList = $this->getMockForAbstractClass('Magento\Framework\View\Design\Theme\ListInterface'); - $themeList->expects($this->any()) + $themeProvider = $this->getMock(ThemeProviderInterface::class); + $themeProvider->expects($this->any()) ->method('getThemeByFullPath') ->with('frontend/magento_theme') ->willReturn($this->theme); $this->initFilesystem(); - $this->object = new Source( - $this->filesystem, $this->preProcessorPool, $this->viewFileResolution, $themeList, $this->chainFactory - ); + $this->object = (new ObjectManager($this))->getObject(Source::class, [ + 'filesystem' => $this->filesystem, + 'preProcessorPool' => $this->preProcessorPool, + 'fallback' => $this->viewFileResolution, + 'chainFactory' => $this->chainFactory + ]); + + $reflection = new \ReflectionClass(Source::class); + $reflectionProperty = $reflection->getProperty('themeProvider'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->object, $themeProvider); } /** diff --git a/lib/internal/Magento/Framework/View/Test/Unit/ConfigTest.php b/lib/internal/Magento/Framework/View/Test/Unit/ConfigTest.php index 8247cd83bab4e..2a09783c710e4 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/ConfigTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/ConfigTest.php @@ -6,7 +6,6 @@ namespace Magento\Framework\View\Test\Unit; -use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; class ConfigTest extends \PHPUnit_Framework_TestCase @@ -27,11 +26,11 @@ class ConfigTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->repositoryMock = $this->getMock('Magento\Framework\View\Asset\Repository', [], [], '', false); - $this->viewConfigFactoryMock = $this->getMock('Magento\Framework\Config\ViewFactory', [], [], '', false); + $this->repositoryMock = $this->getMock(\Magento\Framework\View\Asset\Repository::class, [], [], '', false); + $this->viewConfigFactoryMock = $this->getMock(\Magento\Framework\Config\ViewFactory::class, [], [], '', false); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->config = $this->objectManagerHelper->getObject( - 'Magento\Framework\View\Config', + \Magento\Framework\View\Config::class, [ 'assetRepo' => $this->repositoryMock, 'viewConfigFactory' => $this->viewConfigFactoryMock @@ -41,27 +40,32 @@ protected function setUp() public function testGetViewConfig() { + $themeCode = 'area/theme'; + $themeMock = $this->getMock( - 'Magento\Theme\Model\Theme', - ['getCode'], + \Magento\Theme\Model\Theme::class, + ['getFullPath'], [], '', false ); $themeMock->expects($this->atLeastOnce()) - ->method('getCode') - ->will($this->returnValue(2)); - $params = ['themeModel' => $themeMock]; + ->method('getFullPath') + ->will($this->returnValue($themeCode)); + $params = [ + 'themeModel' => $themeMock, + 'area' => 'frontend' + ]; $this->repositoryMock->expects($this->atLeastOnce()) ->method('updateDesignParams') ->with($this->equalTo($params)) ->will($this->returnSelf()); - $configViewMock = $this->getMock('Magento\Framework\Config\View', [], [], '', false); + $configViewMock = $this->getMock(\Magento\Framework\Config\View::class, [], [], '', false); $this->viewConfigFactoryMock->expects($this->once()) ->method('create') ->willReturn($configViewMock); - $this->assertInstanceOf('Magento\Framework\Config\View', $this->config->getViewConfig($params)); + $this->assertInstanceOf(\Magento\Framework\Config\View::class, $this->config->getViewConfig($params)); // lazy load test - $this->assertInstanceOf('Magento\Framework\Config\View', $this->config->getViewConfig($params)); + $this->assertInstanceOf(\Magento\Framework\Config\View::class, $this->config->getViewConfig($params)); } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/MinificationTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/MinificationTest.php index 48646023685d2..3f461f1602247 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/MinificationTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/MinificationTest.php @@ -28,17 +28,18 @@ class MinificationTest extends \PHPUnit_Framework_TestCase * @var \Magento\Framework\View\Asset\Minification|\PHPUnit_Framework_MockObject_MockObject */ protected $assetMinificationMock; + /** * {@inheritDoc} */ protected function setUp() { $this->resolverMock = $this - ->getMockBuilder('Magento\Framework\View\Design\FileResolution\Fallback\ResolverInterface') + ->getMockBuilder(\Magento\Framework\View\Design\FileResolution\Fallback\ResolverInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->assetMinificationMock = $this->getMockBuilder('Magento\Framework\View\Asset\Minification') + $this->assetMinificationMock = $this->getMockBuilder(\Magento\Framework\View\Asset\Minification::class) ->disableOriginalConstructor() ->getMock(); @@ -69,21 +70,20 @@ public function testResolve( $this->assetMinificationMock ->expects($this->any()) ->method('isEnabled') - ->willReturnMap([['css', $isEnabled]]); + ->willReturn($isEnabled); $this->assetMinificationMock ->expects($this->any()) - ->method('removeMinifiedSign') + ->method('addMinifiedSign') ->with($requested) ->willReturn($alternative); $this->resolverMock ->expects($this->any()) ->method('resolve') - ->withConsecutive( - ['', $requested, null, null, null, null], - ['', $alternative, null, null, null, null] - ) - ->willReturnOnConsecutiveCalls($resolvedOriginal, $resolvedAlternative); + ->willReturnMap([ + ['', $requested, null, null, null, null, $resolvedOriginal], + ['', $alternative, null, null, null, null, $resolvedAlternative] + ]); $this->assertEquals($expected, $this->minification->resolve('', $requested)); } @@ -94,8 +94,10 @@ public function testResolve( public function resolveDataProvider() { return [ - [true, 'file.min.css', 'file.css', 'found.css', false, 'found.css'], - [false, 'file.min.css', 'file.min.css', false, false, 'found.css'] + [true, 'file.css', 'file.min.css', 'found.min.css', false, 'found.min.css'], + [false, 'file.min.css', 'file.min.css', false, false, 'found.css'], + [true, 'file.js', 'file.min.js', 'found.min.js', false, 'found.min.js'], + [false, 'file.min.js', 'file.min.js', false, false, 'found.js'], ]; } } diff --git a/setup/config/di.config.php b/setup/config/di.config.php index 1fb42803996ed..9c73aae475358 100644 --- a/setup/config/di.config.php +++ b/setup/config/di.config.php @@ -38,6 +38,7 @@ 'Magento\Setup\Controller\Marketplace', 'Magento\Setup\Controller\SystemConfig', 'Magento\Setup\Controller\InstallExtensionGrid', + 'Magento\Setup\Controller\MarketplaceCredentials', 'Magento\Setup\Controller\Session' ], 'instance' => [ diff --git a/setup/config/states.home.config.php b/setup/config/states.home.config.php index 78e947b2ee7b1..ddb6c7430248f 100644 --- a/setup/config/states.home.config.php +++ b/setup/config/states.home.config.php @@ -49,13 +49,13 @@ ], [ 'id' => 'root.upgrade', - 'url' => 'select-version', - 'templateUrl' => "$base/select-version", + 'url' => 'marketplace-credentials', + 'templateUrl' => "$base/marketplace-credentials", 'title' => 'System Upgrade', - 'controller' => 'selectVersionController', - 'header' => 'Step 1: Select Version', + 'controller' => 'MarketplaceCredentialsController', 'order' => 1, - 'nav' => true, + 'nav' => false, + 'noMenu' => true, 'type' => 'upgrade' ], ], diff --git a/setup/config/states.upgrade.config.php b/setup/config/states.upgrade.config.php index c210cb7c1facc..dd1f8938f4581 100644 --- a/setup/config/states.upgrade.config.php +++ b/setup/config/states.upgrade.config.php @@ -11,13 +11,24 @@ 'upgrade' => 'System Upgrade', ], 'navUpdater' => [ + [ + 'id' => 'root.select-version', + 'url' => 'select-version', + 'templateUrl' => "$base/select-version", + 'title' => 'System Upgrade', + 'controller' => 'selectVersionController', + 'header' => 'Step 1: Select Version', + 'order' => 2, + 'nav' => true, + 'type' => 'upgrade' + ], [ 'id' => 'root.readiness-check-upgrade', 'url' => 'readiness-check-updater', 'templateUrl' => "$base/readiness-check-updater", 'title' => "Readiness \n Check", 'header' => 'Step 2: Readiness Check', - 'order' => 2, + 'order' => 3, 'nav' => true, 'type' => 'upgrade' ], @@ -29,7 +40,7 @@ 'header' => 'Step 2: Readiness Check', 'controller' => 'readinessCheckController', 'nav' => false, - 'order' => 3, + 'order' => 4, 'type' => 'upgrade' ], [ @@ -39,7 +50,7 @@ 'title' => 'Create Backup', 'controller' => 'createBackupController', 'header' => 'Step 3: Create Backup', - 'order' => 4, + 'order' => 5, 'nav' => true, 'type' => 'upgrade' ], @@ -51,7 +62,7 @@ 'header' => 'Step 3: Create Backup', 'controller' => 'completeBackupController', 'nav' => false, - 'order' => 5, + 'order' => 6, 'type' => 'upgrade' ], [ @@ -61,7 +72,7 @@ 'title' => "System \n Upgrade", 'controller' => 'startUpdaterController', 'header' => 'Step 4: System Upgrade', - 'order' => 6, + 'order' => 7, 'nav' => true, 'type' => 'upgrade' ], @@ -70,7 +81,7 @@ 'url' => 'updater-success', 'templateUrl' => "$base/updater-success", 'controller' => 'updaterSuccessController', - 'order' => 7, + 'order' => 8, 'noMenu' => true ], [ diff --git a/setup/pub/magento/setup/app.js b/setup/pub/magento/setup/app.js index 71d9c50b4e8ef..7da6d4dae759b 100644 --- a/setup/pub/magento/setup/app.js +++ b/setup/pub/magento/setup/app.js @@ -28,7 +28,8 @@ var app = angular.module( 'updater-success', 'home', 'auth-dialog', - 'system-config' + 'system-config', + 'marketplace-credentials' ]); app.config(['$httpProvider', '$stateProvider', function ($httpProvider, $stateProvider) { diff --git a/setup/pub/magento/setup/marketplace-credentials.js b/setup/pub/magento/setup/marketplace-credentials.js new file mode 100644 index 0000000000000..1d1071063d139 --- /dev/null +++ b/setup/pub/magento/setup/marketplace-credentials.js @@ -0,0 +1,87 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +'use strict'; +angular.module('marketplace-credentials', ['ngStorage']) + .controller('MarketplaceCredentialsController', ['$scope', '$state', '$http', '$localStorage', '$rootScope', '$sce', + function ($scope, $state, $http, $localStorage, $rootScope, $sce) { + $scope.showCredsForm = false; + $scope.user = { + username : $localStorage.marketplaceUsername ? $localStorage.marketplaceUsername : '', + password : '', + submitted : false + }; + + $scope.upgradeProcessError = false; + + $http.get('index.php/select-version/installedSystemPackage', {'responseType' : 'json'}) + .success(function (data) { + if (data.responseType == 'error') { + $scope.upgradeProcessError = true; + $scope.upgradeProcessErrorMessage = $sce.trustAsHtml(data.error); + } else { + if (!$rootScope.authRequest || !$rootScope.isMarketplaceAuthorized) { + $scope.isHiddenSpinner = false; + $http.post('index.php/marketplace/check-auth', []) + .success(function (response) { + if (response.success) { + $localStorage.marketplaceUsername = $scope.user.username = response.data.username; + $localStorage.isMarketplaceAuthorized = true; + $scope.nextState(); + } else { + $localStorage.isMarketplaceAuthorized = false; + $scope.showCredsForm = true; + } + $rootScope.isMarketplaceAuthorized = $localStorage.isMarketplaceAuthorized; + $rootScope.authRequest = true; + }); + } else { + $rootScope.isMarketplaceAuthorized = $localStorage.isMarketplaceAuthorized; + $scope.nextState(); + } + } + }) + .error(function (data) { + $scope.upgradeProcessError = true; + }); + + $scope.errors = false; + + $scope.saveAuthJson = function () { + if ($scope.auth.$valid) { + $http.post('index.php/marketplace/save-auth-json', $scope.user) + .success(function (data) { + $scope.saveAuthJson.result = data; + if ($scope.saveAuthJson.result.success) { + $scope.logout = false; + $localStorage.isMarketplaceAuthorized = true; + $scope.errors = false; + $scope.nextState(); + } else { + $localStorage.isMarketplaceAuthorized = false; + $scope.errors = true; + } + $rootScope.isMarketplaceAuthorized = $localStorage.isMarketplaceAuthorized; + $localStorage.marketplaceUsername = $scope.user.username; + }) + .error(function (data) { + $scope.saveAuthJson.failed = data; + $localStorage.isMarketplaceAuthorized = false; + $scope.errors = true; + + }); + } else { + $scope.validate(); + } + }; + + $scope.validate = function() { + if ($scope.user.$valid) { + $scope.user.submitted = false; + } else { + $scope.user.submitted = true; + } + } + }]); diff --git a/setup/pub/magento/setup/select-version.js b/setup/pub/magento/setup/select-version.js index 319055c5f6361..8e9eba9c5c413 100644 --- a/setup/pub/magento/setup/select-version.js +++ b/setup/pub/magento/setup/select-version.js @@ -5,7 +5,7 @@ 'use strict'; angular.module('select-version', ['ngStorage']) - .controller('selectVersionController', ['$scope', '$http', '$localStorage', function ($scope, $http, $localStorage) { + .controller('selectVersionController', ['$scope', '$http', '$localStorage', '$sce', function ($scope, $http, $localStorage, $sce) { $scope.packages = [{ name: '', version: '' @@ -13,6 +13,7 @@ angular.module('select-version', ['ngStorage']) $scope.upgradeReadyForNext = false; $scope.upgradeProcessed = false; $scope.upgradeProcessError = false; + $scope.upgradeProcessErrorMessage = ''; $scope.componentsReadyForNext = true; $scope.componentsProcessed = false; $scope.componentsProcessError = false; @@ -27,33 +28,39 @@ angular.module('select-version', ['ngStorage']) $http.get('index.php/select-version/systemPackage', {'responseType' : 'json'}) .success(function (data) { if (data.responseType != 'error') { - $scope.selectedOption = []; - $scope.versions = []; - for (var i = 0; i < data.packages.length; i++) { - angular.forEach(data.packages[i].versions, function (value, key) { - $scope.versions.push({ - 'versionInfo': angular.toJson({ - 'package': data.packages[i].package, - 'version': value - }), 'version': value + if (data.packages.length == 1 && data.packages[0].versions.length <=1) { + $scope.upgradeProcessError = true; + $scope.upgradeProcessErrorMessage = "You're already using the latest version, there's nothing for us to do."; + } else { + $scope.selectedOption = []; + $scope.versions = []; + for (var i = 0; i < data.packages.length; i++) { + angular.forEach(data.packages[i].versions, function (value, key) { + $scope.versions.push({ + 'versionInfo': angular.toJson({ + 'package': data.packages[i].package, + 'version': value + }), 'version': value + }); }); + } + + $scope.versions = $scope.versions.sort(function (a, b) { + if (a.version.id < b.version.id) { + return 1; + } + if (a.version.id > b.version.id) { + return -1; + } + return 0; }); + $scope.selectedOption = $scope.versions[0].versionInfo; + $scope.upgradeReadyForNext = true; } - $scope.versions = $scope.versions.sort(function (a, b) { - if (a.version.id < b.version.id) { - return 1; - } - if (a.version.id > b.version.id) { - return -1; - } - return 0; - }); - $scope.selectedOption = $scope.versions[0].versionInfo; - $scope.upgradeReadyForNext = true; - } else { $scope.upgradeProcessError = true; + $scope.upgradeProcessErrorMessage = $sce.trustAsHtml(data.error); } $scope.upgradeProcessed = true; }) diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php index 484ea598d9f31..908ba13afb70b 100644 --- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php +++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php @@ -151,17 +151,9 @@ protected function execute(InputInterface $input, OutputInterface $output) 'library' => $libraryPaths, 'generated_helpers' => $generationPath ]; - $excludedModulePaths = []; - foreach ($modulePaths as $appCodePath) { - $excludedModulePaths[] = '#^' . $appCodePath . '/Test#'; - } - $excludedLibraryPaths = []; - foreach ($libraryPaths as $libraryPath) { - $excludedLibraryPaths[] = '#^' . $libraryPath . '/([\\w]+/)?Test#'; - } $this->excludedPathsList = [ - 'application' => $excludedModulePaths, - 'framework' => $excludedLibraryPaths + 'application' => $this->getExcludedModulePaths($modulePaths), + 'framework' => $this->getExcludedLibraryPaths($libraryPaths), ]; $this->configureObjectManager($output); @@ -214,6 +206,54 @@ function (OperationInterface $operation) use ($progressBar) { } } + /** + * Build list of module path regexps which should be excluded from compilation + * + * @param string[] $modulePaths + * @return string[] + */ + private function getExcludedModulePaths(array $modulePaths) + { + $modulesByBasePath = []; + foreach ($modulePaths as $modulePath) { + $moduleDir = basename($modulePath); + $vendorPath = dirname($modulePath); + $vendorDir = basename($vendorPath); + $basePath = dirname($vendorPath); + $modulesByBasePath[$basePath][$vendorDir][] = $moduleDir; + } + + $basePathsRegExps = []; + foreach ($modulesByBasePath as $basePath => $vendorPaths) { + $vendorPathsRegExps = []; + foreach ($vendorPaths as $vendorDir => $vendorModules) { + $vendorPathsRegExps[] = $vendorDir + . '/(?:' . join('|', $vendorModules) . ')'; + } + $basePathsRegExps[] = $basePath + . '/(?:' . join('|', $vendorPathsRegExps) . ')'; + } + + $excludedModulePaths = [ + '#^(?:' . join('|', $basePathsRegExps) . ')/Test#', + ]; + return $excludedModulePaths; + } + + /** + * Build list of library path regexps which should be excluded from compilation + * + * @param string[] $libraryPaths + * @return string[] + */ + private function getExcludedLibraryPaths(array $libraryPaths) + { + $excludedLibraryPaths = [ + '#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?Test#', + ]; + return $excludedLibraryPaths; + } + /** * Delete directories by their code from "var" directory * diff --git a/setup/src/Magento/Setup/Controller/ComponentGrid.php b/setup/src/Magento/Setup/Controller/ComponentGrid.php index a3db50df89fe8..9d97cf7ae3cfe 100644 --- a/setup/src/Magento/Setup/Controller/ComponentGrid.php +++ b/setup/src/Magento/Setup/Controller/ComponentGrid.php @@ -63,12 +63,14 @@ class ComponentGrid extends \Zend\Mvc\Controller\AbstractActionController * @param \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider * @param \Magento\Setup\Model\UpdatePackagesCache $updatePackagesCache * @param \Magento\Setup\Model\MarketplaceManager $marketplaceManager + * @param TimezoneProvider $timezoneProvider */ public function __construct( \Magento\Framework\Composer\ComposerInformation $composerInformation, \Magento\Setup\Model\ObjectManagerProvider $objectManagerProvider, \Magento\Setup\Model\UpdatePackagesCache $updatePackagesCache, - \Magento\Setup\Model\MarketplaceManager $marketplaceManager + \Magento\Setup\Model\MarketplaceManager $marketplaceManager, + \Magento\Setup\Model\DateTime\TimezoneProvider $timezoneProvider ) { $this->composerInformation = $composerInformation; $this->objectManager = $objectManagerProvider->get(); @@ -77,34 +79,7 @@ public function __construct( $this->packageInfo = $this->objectManager->get('Magento\Framework\Module\PackageInfoFactory')->create(); $this->marketplaceManager = $marketplaceManager; $this->updatePackagesCache = $updatePackagesCache; - } - - /** - * Get timezone - * - * @return \Magento\Framework\Stdlib\DateTime\TimezoneInterface|null - */ - private function getTimezone() - { - if ($this->timezone === null) { - $this->timezone = $this->objectManager->get('Magento\Setup\Model\DateTime\TimezoneProvider')->get(); - } - return $this->timezone; - } - - /** - * Set timezone - * - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone - * @return void - * @throws \Exception - */ - public function setTimezone(\Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone) - { - if ($this->timezone !== null) { - throw new \Exception('timezone is already set'); - } - $this->timezone = $timezone; + $this->timezone = $timezoneProvider->get(); } /** @@ -182,18 +157,18 @@ public function componentsAction() public function syncAction() { $error = ''; + $packagesForInstall = []; + $lastSyncData = []; try { $this->updatePackagesCache->syncPackagesForUpdate(); $lastSyncData = $this->updatePackagesCache->getPackagesForUpdate(); $this->marketplaceManager->syncPackagesForInstall(); $packagesForInstall = $this->marketplaceManager->getPackagesForInstall(); + $lastSyncData = $this->formatLastSyncData($packagesForInstall, $lastSyncData); } catch (\Exception $e) { $error = $e->getMessage(); } - - $lastSyncData = $this->formatLastSyncData($packagesForInstall, $lastSyncData); - return new \Zend\View\Model\JsonModel( [ 'success' => true, @@ -248,12 +223,12 @@ private function formatLastSyncData($packagesForInstall, $lastSyncData) private function formatSyncDate($syncDate) { return [ - 'date' => $this->getTimezone()->formatDateTime( + 'date' => $this->timezone->formatDateTime( new \DateTime('@'.$syncDate), \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE ), - 'time' => $this->getTimezone()->formatDateTime( + 'time' => $this->timezone->formatDateTime( new \DateTime('@'.$syncDate), \IntlDateFormatter::NONE, \IntlDateFormatter::MEDIUM diff --git a/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php b/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php new file mode 100644 index 0000000000000..312b853688b0a --- /dev/null +++ b/setup/src/Magento/Setup/Controller/MarketplaceCredentials.php @@ -0,0 +1,22 @@ +setTerminal(true); + return $view; + } +} diff --git a/setup/src/Magento/Setup/Controller/SelectVersion.php b/setup/src/Magento/Setup/Controller/SelectVersion.php index d78194a23ee7b..5b1d11058fcc4 100644 --- a/setup/src/Magento/Setup/Controller/SelectVersion.php +++ b/setup/src/Magento/Setup/Controller/SelectVersion.php @@ -61,4 +61,22 @@ public function systemPackageAction() return new JsonModel($data); } + + /** + * Gets installed system package + * + * @return JsonModel + */ + public function installedSystemPackageAction() + { + $data = []; + try { + $data['packages'] = $this->systemPackage->getInstalledSystemPackages([]); + $data['responseType'] = ResponseTypeInterface::RESPONSE_TYPE_SUCCESS; + } catch (\Exception $e) { + $data['error'] = $e->getMessage(); + $data['responseType'] = ResponseTypeInterface::RESPONSE_TYPE_ERROR; + } + return new JsonModel($data); + } } diff --git a/setup/src/Magento/Setup/Model/FilePermissions.php b/setup/src/Magento/Setup/Model/FilePermissions.php index 43dc400a81e71..6863968ef3cd7 100644 --- a/setup/src/Magento/Setup/Model/FilePermissions.php +++ b/setup/src/Magento/Setup/Model/FilePermissions.php @@ -8,7 +8,9 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Backup\Filesystem\Iterator\Filter; +use Magento\Framework\Filesystem\Filter\ExcludeFilter; use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Driver\File; class FilePermissions { @@ -22,6 +24,11 @@ class FilePermissions */ protected $directoryList; + /** + * @var File + */ + protected $driverFile; + /** * List of required writable directories for installation * @@ -50,16 +57,26 @@ class FilePermissions */ protected $applicationCurrentNonWritableDirectories = []; + /** + * List of non-writable paths in a specified directory + * + * @var array + */ + protected $nonWritablePathsInDirectories = []; + /** * @param Filesystem $filesystem * @param DirectoryList $directoryList + * @param File $driverFile */ public function __construct( Filesystem $filesystem, - DirectoryList $directoryList + DirectoryList $directoryList, + File $driverFile ) { $this->filesystem = $filesystem; $this->directoryList = $directoryList; + $this->driverFile = $driverFile; } /** @@ -110,8 +127,12 @@ public function getInstallationCurrentWritableDirectories() { if (!$this->installationCurrentWritableDirectories) { foreach ($this->installationWritableDirectories as $code => $path) { - if ($this->isWritable($code) && $this->checkRecursiveDirectories($path)) { - $this->installationCurrentWritableDirectories[] = $path; + if ($this->isWritable($code)) { + if ($this->checkRecursiveDirectories($path)) { + $this->installationCurrentWritableDirectories[] = $path; + } + } else { + $this->nonWritablePathsInDirectories[$path] = [$path]; } } } @@ -132,21 +153,31 @@ private function checkRecursiveDirectories($directory) ); $noWritableFilesFolders = [ $this->directoryList->getPath(DirectoryList::GENERATION) . '/', - $this->directoryList->getPath(DirectoryList::DI) .'/' + $this->directoryList->getPath(DirectoryList::DI) . '/', ]; $directoryIterator = new Filter($directoryIterator, $noWritableFilesFolders); + $directoryIterator = new ExcludeFilter( + $directoryIterator, + [ + $this->directoryList->getPath(DirectoryList::SESSION) . '/', + ] + ); + + $foundNonWritable = false; + try { foreach ($directoryIterator as $subDirectory) { - if (!$subDirectory->isWritable()) { - return false; + if (!$subDirectory->isWritable() && !$subDirectory->isLink()) { + $this->nonWritablePathsInDirectories[$directory][] = $subDirectory; + $foundNonWritable = true; } } } catch (\UnexpectedValueException $e) { return false; } - return true; + return !$foundNonWritable; } /** @@ -190,6 +221,28 @@ protected function isNonWritable($code) return $this->isReadableDirectory($directory) && !$directory->isWritable(); } + /** + * Checks if var/generation/* has read and execute permissions + * + * @return bool + */ + public function checkDirectoryPermissionForCLIUser() + { + $varGenerationDir = $this->directoryList->getPath(DirectoryList::GENERATION); + $dirs = $this->driverFile->readDirectory($varGenerationDir); + array_unshift($dirs, $varGenerationDir); + + foreach ($dirs as $dir) { + if (!is_dir($dir) + || !is_readable($dir) + || !is_executable($dir) + ) { + return false; + } + } + return true; + } + /** * Checks if directory exists and is readable * @@ -204,9 +257,31 @@ protected function isReadableDirectory($directory) return true; } + /** + * Checks writable paths for installation + * + * @return array + */ + public function getMissingWritablePathsForInstallation() + { + $required = $this->getInstallationWritableDirectories(); + $current = $this->getInstallationCurrentWritableDirectories(); + $missingPaths = []; + foreach (array_diff($required, $current) as $missingPath) { + if (isset($this->nonWritablePathsInDirectories[$missingPath])) { + $missingPaths = array_merge( + $missingPaths, + $this->nonWritablePathsInDirectories[$missingPath] + ); + } + } + return $missingPaths; + } + /** * Checks writable directories for installation * + * @deprecated Use getMissingWritablePathsForInstallation() to get all missing writable paths required for install * @return array */ public function getMissingWritableDirectoriesForInstallation() diff --git a/setup/src/Magento/Setup/Model/MarketplaceManager.php b/setup/src/Magento/Setup/Model/MarketplaceManager.php index b1d4e5c0d425a..75cf909c37531 100644 --- a/setup/src/Magento/Setup/Model/MarketplaceManager.php +++ b/setup/src/Magento/Setup/Model/MarketplaceManager.php @@ -166,14 +166,18 @@ public function syncPackagesForInstall() } $packageNames = array_column($this->getComposerInformation()->getInstalledMagentoPackages(), 'name'); $installPackages = []; - foreach ($packagesJsonData['packages'] as $package) { - $package = $this->unsetDevVersions($package); - if (!empty($package)) { + foreach ($packagesJsonData['packages'] as $packageName => $package) { + if (!empty($package) && is_array($package)) { + $package = $this->unsetDevVersions($package); ksort($package); - $package = array_pop($package); - if ($this->isNewUserPackage($package, $packageNames)) { - $package['vendor'] = explode('/', $package['name'])[0]; - $installPackages[$package['name']] = $package; + $packageValues = array_values($package); + if ($this->isNewUserPackage($packageValues[0], $packageNames)) { + $versions = array_reverse(array_keys($package)); + $installPackage = $packageValues[0]; + $installPackage['versions'] = $versions; + $installPackage['name'] = $packageName; + $installPackage['vendor'] = explode('/', $packageName)[0]; + $installPackages[$packageName] = $installPackage; } } } @@ -364,6 +368,7 @@ public function getPackagesForInstall() $package['metapackage'] = isset($metaPackageByPackage[$package['name']]) ? $metaPackageByPackage[$package['name']] : ''; $actualInstallackages[$package['name']] = $package; + $actualInstallackages[$package['name']]['version'] = $package['versions'][0]; } } $installPackagesInfo['packages'] = $actualInstallackages; diff --git a/setup/src/Magento/Setup/Model/SystemPackage.php b/setup/src/Magento/Setup/Model/SystemPackage.php index 3850ab9051ac9..6ac9b9d3a60bc 100755 --- a/setup/src/Magento/Setup/Model/SystemPackage.php +++ b/setup/src/Magento/Setup/Model/SystemPackage.php @@ -58,14 +58,11 @@ public function getPackageVersions() $result = []; $systemPackages = []; $systemPackages = $this->getInstalledSystemPackages($systemPackages); - if (empty($systemPackages)) { - throw new \RuntimeException('System packages not found'); - } foreach ($systemPackages as $systemPackage) { $versions = []; $systemPackageInfo = $this->infoCommand->run($systemPackage); if (!$systemPackageInfo) { - throw new \RuntimeException('System package not found'); + throw new \RuntimeException("We cannot retrieve information on $systemPackage."); } $versions = $this->getSystemPackageVersions($systemPackageInfo, $versions); @@ -150,6 +147,7 @@ public function getSystemPackageVersions($systemPackageInfo, $versions) /** * @param array $systemPackages * @return array + * @throws \RuntimeException */ public function getInstalledSystemPackages($systemPackages) { @@ -169,6 +167,14 @@ public function getInstalledSystemPackages($systemPackages) } } } + if (empty($systemPackages)) { + throw new \RuntimeException( + 'We\'re sorry, no components are available because you cloned the Magento 2 GitHub repository. ' . + 'You must manually update components as discussed in the ' . + '' . + 'Installation Guide.' + ); + } return $systemPackages; } diff --git a/setup/src/Magento/Setup/Model/UpdatePackagesCache.php b/setup/src/Magento/Setup/Model/UpdatePackagesCache.php index a8e390db4e74d..e13125b5241de 100644 --- a/setup/src/Magento/Setup/Model/UpdatePackagesCache.php +++ b/setup/src/Magento/Setup/Model/UpdatePackagesCache.php @@ -11,7 +11,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\Framework\Composer\MagentoComposerApplicationFactory; -use Magento\Framework\ObjectManagerInterface; +use Magento\Setup\Model\DateTime\DateTimeProvider; /** * Class UpdatePackagesCache manages information about available for update packages though the cache file. @@ -54,58 +54,24 @@ class UpdatePackagesCache */ private $pathToCacheFile = 'update_composer_packages.json'; - /** - * @var ObjectManagerInterface - */ - private $objectManager; - /** * Constructor * * @param MagentoComposerApplicationFactory $applicationFactory * @param \Magento\Framework\Filesystem $filesystem * @param ComposerInformation $composerInformation - * @param ObjectManagerProvider $objectManagerProvider - * @throws \Exception + * @param DateTime\DateTimeProvider $dateTimeProvider */ public function __construct( MagentoComposerApplicationFactory $applicationFactory, Filesystem $filesystem, ComposerInformation $composerInformation, - ObjectManagerProvider $objectManagerProvider + DateTimeProvider $dateTimeProvider ) { $this->application = $applicationFactory->create(); $this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); - $this->objectManager = $objectManagerProvider->get(); $this->composerInformation = $composerInformation; - } - - /** - * Get datetime - * - * @return \Magento\Framework\Stdlib\DateTime\DateTime - */ - private function getDateTime() - { - if ($this->dateTime === null) { - $this->dateTime = $this->objectManager->get('Magento\Setup\Model\DateTime\DateTimeProvider')->get(); - } - return $this->dateTime; - } - - /** - * Set datetime - * - * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime - * @return void - * @throws \Exception - */ - public function setDateTime(\Magento\Framework\Stdlib\DateTime\DateTime $dateTime) - { - if ($this->dateTime !== null) { - throw new \Exception('dateTime is already set'); - } - $this->dateTime = $dateTime; + $this->dateTime = $dateTimeProvider->get(); } /** @@ -208,7 +174,7 @@ private function getPackageAvailableVersions($package) private function savePackagesForUpdateToCache($availableVersions) { $syncInfo = []; - $syncInfo['lastSyncDate'] = $this->getDateTime()->gmtTimestamp(); + $syncInfo['lastSyncDate'] = $this->dateTime->gmtTimestamp(); $syncInfo['packages'] = $availableVersions; $data = json_encode($syncInfo, JSON_UNESCAPED_SLASHES); try { diff --git a/setup/src/Magento/Setup/Module/Setup.php b/setup/src/Magento/Setup/Module/Setup.php index 4451267a0e9aa..bac44f28e8d77 100644 --- a/setup/src/Magento/Setup/Module/Setup.php +++ b/setup/src/Magento/Setup/Module/Setup.php @@ -19,7 +19,7 @@ class Setup extends \Magento\Framework\Module\Setup implements SchemaSetupInterf */ public function getIdxName($tableName, $fields, $indexType = '') { - return $this->getConnection()->getIndexName($tableName, $fields, $indexType); + return $this->getConnection()->getIndexName($this->getTable($tableName), $fields, $indexType); } /** @@ -33,6 +33,11 @@ public function getIdxName($tableName, $fields, $indexType = '') */ public function getFkName($priTableName, $priColumnName, $refTableName, $refColumnName) { - return $this->getConnection()->getForeignKeyName($priTableName, $priColumnName, $refTableName, $refColumnName); + return $this->getConnection()->getForeignKeyName( + $this->getTable($priTableName), + $priColumnName, + $refTableName, + $refColumnName + ); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/ComponentGridTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/ComponentGridTest.php index e1501dd01ddf4..9d56a19426e05 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/ComponentGridTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/ComponentGridTest.php @@ -170,9 +170,9 @@ public function setUp() $this->composerInformationMock, $objectManagerProvider, $this->updatePackagesCacheMock, - $this->marketplaceManagerMock + $this->marketplaceManagerMock, + $timezoneProviderMock ); - $this->controller->setTimezone($timezoneProviderMock->get()); } public function testIndexAction() diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php index 48b73674faff1..3127dd297e488 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SelectVersionTest.php @@ -67,4 +67,33 @@ public function testSystemPackageActionActionWithError() $this->assertArrayHasKey('responseType', $variables); $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); } + + public function testInstalledSystemPackageAction() + { + $this->systemPackage->expects($this->once()) + ->method('getInstalledSystemPackages') + ->willReturn([ + 'package' => 'magento/product-community-edition', + 'versions' => [ + 'id' => 'magento/product-community-edition', + 'name' => 'Version 1.0.0' + ] + ]); + $jsonModel = $this->controller->installedSystemPackageAction(); + $this->assertInstanceOf('Zend\View\Model\JsonModel', $jsonModel); + $variables = $jsonModel->getVariables(); + $this->assertArrayHasKey('responseType', $variables); + $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_SUCCESS, $variables['responseType']); + } + + public function testInstalledSystemPackageActionWithError() + { + $this->systemPackage->expects($this->once()) + ->method('getInstalledSystemPackages') + ->will($this->throwException(new \Exception("Test error message"))); + $jsonModel = $this->controller->installedSystemPackageAction(); + $variables = $jsonModel->getVariables(); + $this->assertArrayHasKey('responseType', $variables); + $this->assertEquals(ResponseTypeInterface::RESPONSE_TYPE_ERROR, $variables['responseType']); + } } diff --git a/setup/src/Magento/Setup/Test/Unit/Model/FilePermissionsTest.php b/setup/src/Magento/Setup/Test/Unit/Model/FilePermissionsTest.php index fe86df12ec117..6e4913f2dd6ef 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/FilePermissionsTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/FilePermissionsTest.php @@ -6,6 +6,7 @@ namespace Magento\Setup\Test\Unit\Model; +use Magento\Framework\Filesystem\Driver\File; use \Magento\Setup\Model\FilePermissions; use Magento\Framework\App\Filesystem\DirectoryList; @@ -32,6 +33,11 @@ class FilePermissionsTest extends \PHPUnit_Framework_TestCase */ private $filePermissions; + /** + * @var File + */ + private $driverFileMock; + public function setUp() { $this->directoryWriteMock = $this->getMock('Magento\Framework\Filesystem\Directory\Write', [], [], '', false); @@ -43,7 +49,15 @@ public function setUp() ->will($this->returnValue($this->directoryWriteMock)); $this->directoryListMock = $this->getMock('Magento\Framework\App\Filesystem\DirectoryList', [], [], '', false); - $this->filePermissions = new FilePermissions($this->filesystemMock, $this->directoryListMock); + $this->driverFileMock = $this->getMockBuilder(File::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->filePermissions = new FilePermissions( + $this->filesystemMock, + $this->directoryListMock, + $this->driverFileMock + ); } public function testGetInstallationWritableDirectories() diff --git a/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php b/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php index e78da2dc6cbc1..2f20cb1e284d0 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/SystemPackageTest.php @@ -177,7 +177,7 @@ public function testGetPackageVersions() /** * @expectedException \RuntimeException - * @expectedExceptionMessage System packages not found + * @expectedExceptionMessage We're sorry, no components are available */ public function testGetPackageVersionGitCloned() { @@ -206,7 +206,7 @@ public function testGetPackageVersionGitCloned() /** * @expectedException \RuntimeException - * @expectedExceptionMessage System package not found + * @expectedExceptionMessage We cannot retrieve information on */ public function testGetPackageVersionsFailed() { diff --git a/setup/src/Magento/Setup/Test/Unit/Module/SetupTest.php b/setup/src/Magento/Setup/Test/Unit/Module/SetupTest.php index 43c6fc0297ff0..05149673d2551 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/SetupTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/SetupTest.php @@ -3,11 +3,14 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Setup\Test\Unit\Module; -use \Magento\Setup\Module\Setup; +use Magento\Framework\App\ResourceConnection; +use Magento\Setup\Module\Setup; +/** + * Class SetupTest + */ class SetupTest extends \PHPUnit_Framework_TestCase { const CONNECTION_NAME = 'connection'; @@ -22,15 +25,20 @@ class SetupTest extends \PHPUnit_Framework_TestCase */ private $setup; + /** + * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + */ + private $resourceModelMock; + protected function setUp() { - $resourceModel = $this->getMock('\Magento\Framework\App\ResourceConnection', [], [], '', false); + $this->resourceModelMock = $this->getMock('\Magento\Framework\App\ResourceConnection', [], [], '', false); $this->connection = $this->getMockForAbstractClass('\Magento\Framework\DB\Adapter\AdapterInterface'); - $resourceModel->expects($this->any()) + $this->resourceModelMock->expects($this->any()) ->method('getConnection') ->with(self::CONNECTION_NAME) ->will($this->returnValue($this->connection)); - $this->setup = new Setup($resourceModel, self::CONNECTION_NAME); + $this->setup = new Setup($this->resourceModelMock, self::CONNECTION_NAME); } public function testGetIdxName() @@ -40,6 +48,11 @@ public function testGetIdxName() $indexType = 'index_type'; $expectedIdxName = 'idxName'; + $this->resourceModelMock->expects($this->once()) + ->method('getTableName') + ->with($tableName) + ->will($this->returnValue($tableName)); + $this->connection->expects($this->once()) ->method('getIndexName') ->with($tableName, $fields, $indexType) @@ -55,6 +68,11 @@ public function testGetFkName() $columnName = 'columnName'; $refColumnName = 'refColumnName'; + $this->resourceModelMock->expects($this->once()) + ->method('getTableName') + ->with($tableName) + ->will($this->returnValue($tableName)); + $this->connection->expects($this->once()) ->method('getForeignKeyName') ->with($tableName, $columnName, $refTable, $refColumnName) diff --git a/setup/view/layout/layout.phtml b/setup/view/layout/layout.phtml index 274f5e3d366b8..2cc0e6ef3b6b4 100644 --- a/setup/view/layout/layout.phtml +++ b/setup/view/layout/layout.phtml @@ -45,6 +45,7 @@ ->appendFile($this->basePath() . '/pub/magento/setup/home.js') ->appendFile($this->basePath() . '/pub/magento/setup/auth-dialog.js') ->appendFile($this->basePath() . '/pub/magento/setup/system-config.js') + ->appendFile($this->basePath() . '/pub/magento/setup/marketplace-credentials.js') ->appendFile($this->basePath() . '/pub/magento/setup/install-extension-grid.js'); ?>

%s

', 'Password Strength:', - 'Enter a mix of 7 or more numbers and letters. For a stronger password, include at least one small letter, big letter, and symbol (Ex: BuyIt$54).' + 'Enter a mix of 7 or more numbers and letters. For a stronger password, include at least one small letter, big letter, and symbol.' ); ?> diff --git a/setup/view/magento/setup/install-extension-grid.phtml b/setup/view/magento/setup/install-extension-grid.phtml index a336509285d03..d26a05f72fa7c 100644 --- a/setup/view/magento/setup/install-extension-grid.phtml +++ b/setup/view/magento/setup/install-extension-grid.phtml @@ -65,18 +65,18 @@
+ > + >
    @@ -134,7 +134,11 @@ {{extension.metapackage}} - {{extension.version}} + + + {{extension.type}} diff --git a/setup/view/magento/setup/marketplace-credentials.phtml b/setup/view/magento/setup/marketplace-credentials.phtml new file mode 100644 index 0000000000000..767fab6375ba4 --- /dev/null +++ b/setup/view/magento/setup/marketplace-credentials.phtml @@ -0,0 +1,102 @@ + + diff --git a/setup/view/magento/setup/popupauth.phtml b/setup/view/magento/setup/popupauth.phtml index 1a4593f8647bd..24be0eb00a3f2 100644 --- a/setup/view/magento/setup/popupauth.phtml +++ b/setup/view/magento/setup/popupauth.phtml @@ -36,9 +36,26 @@ >
    - Sign in to sync your Magento Marketplace Purchases + To upgrade or install purchases, enter your access keys
    + + + + + + + + + + + + + +
    Need to find your keys?
    1. Go to your + Magento Marketplace account page. +
    2. On the "Access keys" page, copy your public and private keys.
    3. Enter keys below: