diff --git a/app/code/Magento/Authorizenet/Model/Authorizenet.php b/app/code/Magento/Authorizenet/Model/Authorizenet.php index 9ab3317a43bb97d950ab32196ce1f9596ba8f7e4..4408c68436707cbfae7332f32cf7d3c902b531f0 100644 --- a/app/code/Magento/Authorizenet/Model/Authorizenet.php +++ b/app/code/Magento/Authorizenet/Model/Authorizenet.php @@ -332,7 +332,7 @@ abstract class Authorizenet extends \Magento\Payment\Model\Method\Cc ->setXCity($billing->getCity()) ->setXState($billing->getRegion()) ->setXZip($billing->getPostcode()) - ->setXCountry($billing->getCountry()) + ->setXCountry($billing->getCountryId()) ->setXPhone($billing->getTelephone()) ->setXFax($billing->getFax()) ->setXCustId($order->getCustomerId()) @@ -352,7 +352,7 @@ abstract class Authorizenet extends \Magento\Payment\Model\Method\Cc ->setXShipToCity($shipping->getCity()) ->setXShipToState($shipping->getRegion()) ->setXShipToZip($shipping->getPostcode()) - ->setXShipToCountry($shipping->getCountry()); + ->setXShipToCountry($shipping->getCountryId()); } $request->setXPoNum($payment->getPoNumber()) diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php index 8be7ed5da15ac1dd627ce691ef1cd6eaf9455824..4d5da3e76dc1c3eb4ff204e9ec388f9cece87865 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Request.php @@ -123,7 +123,7 @@ class Request extends AuthorizenetRequest ->setXCity(strval($billing->getCity())) ->setXState(strval($billing->getRegion())) ->setXZip(strval($billing->getPostcode())) - ->setXCountry(strval($billing->getCountry())) + ->setXCountry(strval($billing->getCountryId())) ->setXPhone(strval($billing->getTelephone())) ->setXFax(strval($billing->getFax())) ->setXCustId(strval($billing->getCustomerId())) @@ -151,7 +151,7 @@ class Request extends AuthorizenetRequest )->setXShipToZip( strval($shipping->getPostcode()) )->setXShipToCountry( - strval($shipping->getCountry()) + strval($shipping->getCountryId()) ); } diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Text.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Text.php index 1427cec7604e88935f53d6c108d77bb58357bdfa..11aa6bf86257cad79ea7698e6c9fdf893ba7082b 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Text.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Text.php @@ -3,14 +3,13 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ +namespace Magento\Backend\Block\Widget\Grid\Column\Renderer; + +use Magento\Framework\DataObject; /** * Backend grid item renderer - * - * @author Magento Core Team <core@magentocommerce.com> */ -namespace Magento\Backend\Block\Widget\Grid\Column\Renderer; - class Text extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** @@ -21,30 +20,53 @@ class Text extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRe protected $_variablePattern = '/\\$([a-z0-9_]+)/i'; /** - * Renders grid column + * Get value for the cel * - * @param \Magento\Framework\DataObject $row - * @return mixed + * @param DataObject $row + * @return string */ - public function _getValue(\Magento\Framework\DataObject $row) + public function _getValue(DataObject $row) { - $format = $this->getColumn()->getFormat() ? $this->getColumn()->getFormat() : null; - $defaultValue = $this->getColumn()->getDefault(); - if ($format === null) { - // If no format and it column not filtered specified return data as is. - $data = parent::_getValue($row); - $string = $data === null ? $defaultValue : $data; - return $this->escapeHtml($string); - } elseif (preg_match_all($this->_variablePattern, $format, $matches)) { - // Parsing of format string - $formattedString = $format; - foreach ($matches[0] as $matchIndex => $match) { - $value = $row->getData($matches[1][$matchIndex]); - $formattedString = str_replace($match, $value, $formattedString); + if (null === $this->getColumn()->getFormat()) { + return $this->getSimpleValue($row); + } + return $this->getFormattedValue($row); + } + + /** + * Get simple value + * + * @param DataObject $row + * @return string + */ + private function getSimpleValue(DataObject $row) + { + $data = parent::_getValue($row); + $value = null === $data ? $this->getColumn()->getDefault() : $data; + if (true === $this->getColumn()->getTranslate()) { + $value = __($value); + } + return $this->escapeHtml($value); + } + + /** + * Replace placeholders in the string with values + * + * @param DataObject $row + * @return string + */ + private function getFormattedValue(DataObject $row) + { + $value = $this->getColumn()->getFormat() ?: null; + if (true === $this->getColumn()->getTranslate()) { + $value = __($value); + } + if (preg_match_all($this->_variablePattern, $value, $matches)) { + foreach ($matches[0] as $index => $match) { + $replacement = $row->getData($matches[1][$index]); + $value = str_replace($match, $replacement, $value); } - return $formattedString; - } else { - return $this->escapeHtml($format); } + return $this->escapeHtml($value); } } diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml index 3e61fec077c6e3fcfccfb62b5ecad98248e9b41d..decc26f331c8298d16b4a76d4a74d82c5808c651 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml @@ -48,6 +48,7 @@ <argument name="width" xsi:type="string">180</argument> <argument name="align" xsi:type="string">left</argument> <argument name="sortable" xsi:type="string">0</argument> + <argument name="translate" xsi:type="boolean">true</argument> </arguments> </block> <block class="Magento\Backend\Block\Widget\Grid\Column" as="description"> @@ -57,6 +58,7 @@ <argument name="type" xsi:type="string">text</argument> <argument name="align" xsi:type="string">left</argument> <argument name="sortable" xsi:type="string">0</argument> + <argument name="translate" xsi:type="boolean">true</argument> </arguments> </block> <block class="Magento\Backend\Block\Widget\Grid\Column" as="tags"> diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js b/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js index ea832acb537e0051e34f976c050c9b8bd7d289b7..14729714b4e608f8c422da8c220b9ea1ed115ffb 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/vault.js @@ -28,7 +28,7 @@ define([ self.$selector = $('#' + self.selector); self.$container = $('#' + self.container); self.$selector.on( - 'setVaultNotActive', + 'setVaultNotActive.' + self.getCode(), function () { self.$selector.off('submitOrder.' + self.getCode()); } diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php index 7789f79d907f85d2402a82899af1de3f63db29de..4059f06bccded2f5dbaec98de627a0dc90407eb9 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php @@ -491,7 +491,7 @@ class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\D null )->join( ['e' => $this->getTable('catalog_product_entity')], - "i.entity_id=e.$linkField", + "i.entity_id=e.entity_id", [] )->where( 'e.type_id=?', @@ -502,7 +502,7 @@ class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\D $select = $connection->select()->from( ['tp' => $this->getTable('catalog_product_entity_tier_price')], - [$linkField] + ['e.entity_id'] )->join( ['e' => $this->getTable('catalog_product_entity')], "tp.{$linkField} = e.{$linkField}", @@ -523,11 +523,11 @@ class Price extends \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\D )->columns( new \Zend_Db_Expr('MIN(tp.value)') )->group( - ["tp.{$linkField}", 'cg.customer_group_id', 'cw.website_id'] + ['e.entity_id', 'cg.customer_group_id', 'cw.website_id'] ); if (!empty($entityIds)) { - $select->where("tp.{$linkField} IN(?)", $entityIds); + $select->where('e.entity_id IN(?)', $entityIds); } $query = $select->insertFromSelect($this->_getTierPriceIndexTable()); diff --git a/app/code/Magento/CatalogWidget/Block/Product/Widget/Conditions.php b/app/code/Magento/CatalogWidget/Block/Product/Widget/Conditions.php index d57db33131b9d3cbe16f60119b9e97b82d72cdad..9a407b118461c19e0db02b92ad2b16a8eb8f5816 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/Widget/Conditions.php +++ b/app/code/Magento/CatalogWidget/Block/Product/Widget/Conditions.php @@ -85,7 +85,7 @@ class Conditions extends Template implements RendererInterface $widget = $this->registry->registry('current_widget_instance'); if ($widget) { $widgetParameters = $widget->getWidgetParameters(); - } elseif($widgetOptions = $this->getLayout()->getBlock('wysiwyg_widget.options')) { + } elseif ($widgetOptions = $this->getLayout()->getBlock('wysiwyg_widget.options')) { $widgetParameters = $widgetOptions->getWidgetValues(); } @@ -100,6 +100,7 @@ class Conditions extends Template implements RendererInterface public function render(AbstractElement $element) { $this->element = $element; + $this->rule->getConditions()->setJsFormObject($this->getHtmlId()); return $this->toHtml(); } diff --git a/app/code/Magento/CatalogWidget/Test/Unit/Block/Product/Widget/ConditionsTest.php b/app/code/Magento/CatalogWidget/Test/Unit/Block/Product/Widget/ConditionsTest.php index 8d87c0ebf0d21768bbe05f5903aded09011e1663..b825e92bab1c2ef2eea4d2c08cd8da1ac1e9e192 100644 --- a/app/code/Magento/CatalogWidget/Test/Unit/Block/Product/Widget/ConditionsTest.php +++ b/app/code/Magento/CatalogWidget/Test/Unit/Block/Product/Widget/ConditionsTest.php @@ -15,6 +15,7 @@ use Magento\Framework\View\Element\BlockInterface; /** * Test class for \Magento\CatalogWidget\Block\Product\Widget\Conditions + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ConditionsTest extends \PHPUnit_Framework_TestCase { @@ -175,4 +176,116 @@ class ConditionsTest extends \PHPUnit_Framework_TestCase ] ); } + + /** + * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testRender() + { + $data = ['area' => 'backend']; + $abstractElementMock = $this->getMock( + \Magento\Framework\Data\Form\Element\AbstractElement::class, + ['getContainer'], + [], + '', + false + ); + $eventManagerMock = $this->getMock( + \Magento\Framework\Event\ManagerInterface::class, + [], + [], + '', + false + ); + $scopeConfigMock = $this->getMock( + \Magento\Framework\App\Config\ScopeConfigInterface::class, + [], + [], + '', + false + ); + $fieldsetMock = $this->getMock( + \Magento\Framework\Data\Form\Element\Fieldset::class, + [], + [], + '', + false + ); + $combineMock = $this->getMock( + \Magento\Rule\Model\Condition\Combine::class, + [], + [], + '', + false + ); + $resolverMock = $this->getMock( + \Magento\Framework\View\Element\Template\File\Resolver::class, + [], + [], + '', + false + ); + $filesystemMock = $this->getMock( + \Magento\Framework\Filesystem::class, + ['getDirectoryRead'], + [], + '', + false + ); + $validatorMock = $this->getMock( + \Magento\Framework\View\Element\Template\File\Validator::class, + [], + [], + '', + false + ); + $templateEnginePoolMock = $this->getMock( + \Magento\Framework\View\TemplateEnginePool::class, + [], + [], + '', + false + ); + $templateEngineMock = $this->getMock( + \Magento\Framework\View\TemplateEngineInterface::class, + [], + [], + '', + false + ); + $directoryReadMock = $this->getMock( + \Magento\Framework\Filesystem\Directory\ReadInterface::class, + [], + [], + '', + false + ); + + $this->ruleMock->expects($this->once())->method('getConditions')->willReturn($combineMock); + $combineMock->expects($this->once())->method('setJsFormObject')->willReturnSelf(); + $abstractElementMock->expects($this->any())->method('getContainer')->willReturn($fieldsetMock); + $filesystemMock->expects($this->once())->method('getDirectoryRead')->willReturn($directoryReadMock); + $validatorMock->expects($this->once())->method('isValid')->willReturn(true); + $this->contextMock->expects($this->once())->method('getEnginePool')->willReturn($templateEnginePoolMock); + $templateEnginePoolMock->expects($this->once())->method('get')->willReturn($templateEngineMock); + $templateEngineMock->expects($this->once())->method('render')->willReturn('html'); + + $this->widgetConditions = $this->objectManagerHelper->getObject( + Conditions::class, + [ + 'context' => $this->contextMock, + 'registry' => $this->registryMock, + 'rule' => $this->ruleMock, + '_eventManager' => $eventManagerMock, + '_filesystem' => $filesystemMock, + '_scopeConfig' => $scopeConfigMock, + 'validator' => $validatorMock, + 'resolver' => $resolverMock, + 'data' => $data + ] + ); + + $this->assertEquals($this->widgetConditions->render($abstractElementMock), 'html'); + } } diff --git a/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml b/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml index 6def568e9cbbd83fec74955430834f7538418ab9..63ef028238393bbbbcebaeff4c50caa1e94b44db 100644 --- a/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml +++ b/app/code/Magento/Indexer/view/adminhtml/layout/indexer_indexer_list_grid.xml @@ -46,6 +46,7 @@ <argument name="index" xsi:type="string">title</argument> <argument name="sortable" xsi:type="string">0</argument> <argument name="column_css_class" xsi:type="string">indexer-title</argument> + <argument name="translate" xsi:type="boolean">true</argument> </arguments> </block> <block class="Magento\Backend\Block\Widget\Grid\Column" as="indexer_description"> @@ -54,6 +55,7 @@ <argument name="index" xsi:type="string">description</argument> <argument name="sortable" xsi:type="string">0</argument> <argument name="column_css_class" xsi:type="string">indexer-description</argument> + <argument name="translate" xsi:type="boolean">true</argument> </arguments> </block> <block class="Magento\Backend\Block\Widget\Grid\Column" as="indexer_mode"> diff --git a/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php b/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php index d2febaccb2fdf3644774f89626e6224bf6e89415..cf9eed3e84d26860792c4d3751e138ad84863eb8 100644 --- a/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php +++ b/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php @@ -5,8 +5,6 @@ */ namespace Magento\OfflineShipping\Model\Quote\Address; -use Magento\Quote\Model\Quote\Address; - class FreeShipping implements \Magento\Quote\Model\Quote\Address\FreeShippingInterface { /** @@ -48,7 +46,8 @@ class FreeShipping implements \Magento\Quote\Model\Quote\Address\FreeShippingInt $quote->getCustomerGroupId(), $quote->getCouponCode() ); - + $shippingAddress = $quote->getShippingAddress(); + $shippingAddress->setFreeShipping(0); /** @var \Magento\Quote\Api\Data\CartItemInterface $item */ foreach ($items as $item) { if ($item->getNoDiscount()) { @@ -66,10 +65,14 @@ class FreeShipping implements \Magento\Quote\Model\Quote\Address\FreeShippingInt $itemFreeShipping = (bool)$item->getFreeShipping(); $addressFreeShipping = $addressFreeShipping && $itemFreeShipping; + if ($addressFreeShipping && !$item->getAddress()->getFreeShipping()) { + $item->getAddress()->setFreeShipping(true); + } + /** Parent free shipping we apply to all children*/ $this->applyToChildren($item, $itemFreeShipping); } - return $addressFreeShipping; + return (bool)$shippingAddress->getFreeShipping(); } /** diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php new file mode 100644 index 0000000000000000000000000000000000000000..27f3c375c91c5a70de74c3ed620838ec358144db --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\OfflineShipping\Test\Unit\Model\Quote\Address; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class FreeShippingTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\OfflineShipping\Model\Quote\Address\FreeShipping + */ + private $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\StoreManagerInterface + */ + private $storeManagerMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\OfflineShipping\Model\SalesRule\Calculator + */ + private $calculatorMock; + + protected function setUp() + { + $this->storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->calculatorMock = $this->getMock( + \Magento\OfflineShipping\Model\SalesRule\Calculator::class, + [], + [], + '', + false + ); + + $this->model = new \Magento\OfflineShipping\Model\Quote\Address\FreeShipping( + $this->storeManagerMock, + $this->calculatorMock + ); + } + + public function testIsFreeShippingIfNoItems() + { + $quoteMock = $this->getMock(\Magento\Quote\Model\Quote::class, [], [], '', false); + $this->assertFalse($this->model->isFreeShipping($quoteMock, [])); + } + + public function testIsFreeShipping() + { + $storeId = 100; + $websiteId = 200; + $customerGroupId = 300; + $objectManagerMock = new ObjectManagerHelper($this); + $quoteMock = $this->getMock( + \Magento\Quote\Model\Quote::class, + ['getShippingAddress', 'getStoreId', 'getCustomerGroupId', 'getCouponCode'], + [], + '', + false + ); + $itemMock = $this->getMock( + \Magento\Quote\Model\Quote\Item::class, + [ + 'getNoDiscount', + 'getParentItemId', + 'getFreeShipping', + 'getAddress', + 'isChildrenCalculated', + 'getHasChildren', + 'getChildren' + ], + [], + '', + false + ); + + $quoteMock->expects($this->once())->method('getStoreId')->willReturn($storeId); + $storeMock = $this->getMock(\Magento\Store\Api\Data\StoreInterface::class); + $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId); + $this->storeManagerMock->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock); + + $quoteMock->expects($this->once())->method('getCustomerGroupId')->willReturn($customerGroupId); + $quoteMock->expects($this->once())->method('getCouponCode')->willReturn(null); + + $this->calculatorMock->expects($this->once()) + ->method('init') + ->with($websiteId, $customerGroupId, null) + ->willReturnSelf(); + + $itemMock->expects($this->once())->method('getNoDiscount')->willReturn(false); + $itemMock->expects($this->once())->method('getParentItemId')->willReturn(false); + $this->calculatorMock->expects($this->exactly(2))->method('processFreeShipping')->willReturnSelf(); + $itemMock->expects($this->once())->method('getFreeShipping')->willReturn(true); + + $addressMock = $objectManagerMock->getObject(\Magento\Quote\Model\Quote\Address::class); + $quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($addressMock); + $itemMock->expects($this->exactly(2))->method('getAddress')->willReturn($addressMock); + + $itemMock->expects($this->once())->method('getHasChildren')->willReturn(true); + $itemMock->expects($this->once())->method('isChildrenCalculated')->willReturn(true); + + $childMock = $this->getMock(\Magento\Quote\Model\Quote\Item::class, ['setFreeShipping'], [], '', false); + $childMock->expects($this->once())->method('setFreeShipping')->with(true)->willReturnSelf(); + $itemMock->expects($this->once())->method('getChildren')->willReturn([$childMock]); + + $this->assertTrue($this->model->isFreeShipping($quoteMock, [$itemMock])); + } +} diff --git a/app/code/Magento/Sales/Block/Status/Grid/Column/State.php b/app/code/Magento/Sales/Block/Status/Grid/Column/State.php index cd049799e7b497b5b657bd03122a2dcf4e2986ab..ae9ea20fd35fd110e796d87564409cadcd8fe57e 100644 --- a/app/code/Magento/Sales/Block/Status/Grid/Column/State.php +++ b/app/code/Magento/Sales/Block/Status/Grid/Column/State.php @@ -49,8 +49,9 @@ class State extends \Magento\Backend\Block\Widget\Grid\Column */ public function decorateState($value, $row, $column, $isExport) { + $status = $row->getStatus(); if ($value) { - $cell = $value . '[' . $this->_config->getStateLabel($value) . ']'; + $cell = $value . '[' . $this->_config->getStateLabelByStateAndStatus($value, $status) . ']'; } else { $cell = $value; } diff --git a/app/code/Magento/Sales/Model/Order/Config.php b/app/code/Magento/Sales/Model/Order/Config.php index 8c6f2e55f55acfa9ea8b0c26e25efac97f330f30..535e2975ee13da8458cba83666ba49abb942a89a 100644 --- a/app/code/Magento/Sales/Model/Order/Config.php +++ b/app/code/Magento/Sales/Model/Order/Config.php @@ -256,4 +256,22 @@ class Config } return $this->statuses[(bool) $visibility]; } + + /** + * Retrieve label by state and status + * + * @param string $state + * @param string $status + * @return \Magento\Framework\Phrase|string + */ + public function getStateLabelByStateAndStatus($state, $status) + { + foreach ($this->_getCollection() as $item) { + if ($item->getData('state') == $state && $item->getData('status') == $status) { + $label = $item->getData('label'); + return __($label); + } + } + return $state; + } } diff --git a/app/code/Magento/Sales/Setup/UpgradeSchema.php b/app/code/Magento/Sales/Setup/UpgradeSchema.php index d35825242fb291a41961eb882ad93f7d24c58ee9..288e8085a0dab51947905ed0254e69c8479e79ff 100644 --- a/app/code/Magento/Sales/Setup/UpgradeSchema.php +++ b/app/code/Magento/Sales/Setup/UpgradeSchema.php @@ -76,8 +76,9 @@ class UpgradeSchema implements UpgradeSchemaInterface 'sales_shipment_grid', ]; foreach ($tables as $table) { - $setup->getConnection()->modifyColumn( - $setup->getTable($table), + $salesConnection = $setup->getConnection(self::$connectionName); + $salesConnection->modifyColumn( + $installer->getTable($table, self::$connectionName), 'customer_group_id', ['type' => 'integer'] ); diff --git a/app/code/Magento/Sales/Test/Unit/Block/Status/Grid/Column/StateTest.php b/app/code/Magento/Sales/Test/Unit/Block/Status/Grid/Column/StateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..59e7accb583d051a62821276a3c6521c8abb54f2 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Block/Status/Grid/Column/StateTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Test\Unit\Block\Status\Grid\Column; + +class StateTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Sales\Block\Status\Grid\Column\State + */ + private $stateColumn; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $orderStatusCollectionFactoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + protected function setUp() + { + $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->orderStatusCollectionFactoryMock = $this->getMock( + \Magento\Sales\Model\ResourceModel\Order\Status\CollectionFactory::class, + ['create'], + [], + '', + false, + false + ); + $this->configMock = $helper->getObject( + \Magento\Sales\Model\Order\Config::class, + [ + 'orderStatusCollectionFactory' => $this->orderStatusCollectionFactoryMock + ] + ); + $this->stateColumn = $helper + ->getObject( + \Magento\Sales\Block\Status\Grid\Column\State::class, + [ + 'config' => $this->configMock, + ] + ); + } + + public function testDecorateState() + { + $rowMock = $this->getMock(\Magento\Sales\Model\Order\Status::class, [], [], '', false); + $rowMock->expects($this->any())->method('getStatus')->willReturn('fraud'); + $columnMock = $this->getMock(\Magento\Backend\Block\Widget\Grid\Column::class, [], [], '', false); + $statuses = [ + new \Magento\Framework\DataObject( + [ + 'status' => 'fraud', + 'state' => 'processing', + 'label' => 'Suspected Fraud', + ] + ), + new \Magento\Framework\DataObject( + [ + 'status' => 'processing', + 'state' => 'processing', + 'label' => 'Processing', + ] + ) + ]; + $collectionMock = $this->getMock( + \Magento\Sales\Model\ResourceModel\Order\Status\Collection::class, + ['create', 'joinStates'], + [], + '', + false, + false + ); + $this->orderStatusCollectionFactoryMock->expects($this->once()) + ->method('create') + ->will($this->returnValue($collectionMock)); + $collectionMock->expects($this->once()) + ->method('joinStates') + ->will($this->returnValue($statuses)); + + $result = $this->stateColumn->decorateState('processing', $rowMock, $columnMock, false); + $this->assertSame('processing[processing]', $result); + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php index 1f29235efaef29a790daccd7cf16447d9254adac..7ee4f745cde8f75de23147c956ca0bf039ab2e3a 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ConfigTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Sales\Test\Unit\Model\Order; -use \Magento\Sales\Model\Order\Config; - /** * Class ConfigTest */ @@ -95,4 +93,40 @@ class ConfigTest extends \PHPUnit_Framework_TestCase $result = $this->salesConfig->getInvisibleOnFrontStatuses(); $this->assertSame($expectedResult, $result); } + + public function testGetStateLabelByStateAndStatus() + { + $statuses = [ + new \Magento\Framework\DataObject( + [ + 'status' => 'fraud', + 'state' => 'processing', + 'label' => 'Suspected Fraud', + ] + ), + new \Magento\Framework\DataObject( + [ + 'status' => 'processing', + 'state' => 'processing', + 'label' => 'Processing', + ] + ) + ]; + $collectionMock = $this->getMock( + \Magento\Sales\Model\ResourceModel\Order\Status\Collection::class, + ['create', 'joinStates'], + [], + '', + false, + false + ); + $this->orderStatusCollectionFactoryMock->expects($this->once()) + ->method('create') + ->will($this->returnValue($collectionMock)); + $collectionMock->expects($this->once()) + ->method('joinStates') + ->will($this->returnValue($statuses)); + $result = $this->salesConfig->getStateLabelByStateAndStatus('processing', 'fraud'); + $this->assertSame('Suspected Fraud', $result->getText()); + } } diff --git a/app/code/Magento/Ui/Component/MassAction/Filter.php b/app/code/Magento/Ui/Component/MassAction/Filter.php index c24b77198030d7ebe1889ed3586b01becf6677ad..c23466ef0844e5237e2ae577cada08dd19086390 100644 --- a/app/code/Magento/Ui/Component/MassAction/Filter.php +++ b/app/code/Magento/Ui/Component/MassAction/Filter.php @@ -11,6 +11,7 @@ use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\App\RequestInterface; use Magento\Framework\View\Element\UiComponentInterface; use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; /** * Class Filter @@ -43,6 +44,11 @@ class Filter */ protected $filterBuilder; + /** + * @var DataProviderInterface + */ + private $dataProvider; + /** * @param UiComponentFactory $factory * @param RequestInterface $request @@ -74,23 +80,33 @@ class Filter } /** + * Adds filters to collection using DataProvider filter results + * * @param AbstractDb $collection * @return AbstractDb * @throws LocalizedException */ public function getCollection(AbstractDb $collection) { - $component = $this->getComponent(); - $this->prepareComponent($component); - $dataProvider = $component->getContext()->getDataProvider(); - $dataProvider->setLimit(0, false); - $ids = []; - foreach ($dataProvider->getSearchResult()->getItems() as $document) { - $ids[] = $document->getId(); - } + $selected = $this->request->getParam(static::SELECTED_PARAM); + $excluded = $this->request->getParam(static::EXCLUDED_PARAM); - $collection->addFieldToFilter($collection->getIdFieldName(), ['in' => $ids]); - return $this->applySelection($collection); + $isExcludedIdsValid = (is_array($excluded) && !empty($excluded)); + $isSelectedIdsValid = (is_array($selected) && !empty($selected)); + + if ('false' !== $excluded) { + if (!$isExcludedIdsValid && !$isSelectedIdsValid) { + throw new LocalizedException(__('Please select item(s).')); + } + } + $idsArray = $this->getFilterIds(); + if (!empty($idsArray)) { + $collection->addFieldToFilter( + $collection->getIdFieldName(), + ['in' => $idsArray] + ); + } + return $collection; } /** @@ -106,9 +122,7 @@ class Filter if ('false' === $excluded) { return; } - $component = $this->getComponent(); - $this->prepareComponent($component); - $dataProvider = $component->getContext()->getDataProvider(); + $dataProvider = $this->getDataProvider(); try { if (is_array($excluded) && !empty($excluded)) { $this->filterBuilder->setConditionType('nin') @@ -127,6 +141,8 @@ class Filter } /** + * Applies selection to collection from POST parameters + * * @param AbstractDb $collection * @return AbstractDb * @throws LocalizedException @@ -169,7 +185,7 @@ class Filter } /** - * Returns RefererUrl + * Returns Referrer Url * * @return string|null */ @@ -178,4 +194,33 @@ class Filter $data = $this->getComponent()->getContext()->getDataProvider()->getConfigData(); return (isset($data['referer_url'])) ? $data['referer_url'] : null; } + + /** + * Get data provider + * + * @return DataProviderInterface + */ + private function getDataProvider() + { + if (!$this->dataProvider) { + $component = $this->getComponent(); + $this->prepareComponent($component); + $this->dataProvider = $component->getContext()->getDataProvider(); + } + return $this->dataProvider; + } + + /** + * Get filter ids as array + * + * @return int[] + */ + private function getFilterIds() + { + $this->applySelectionOnTargetProvider(); + if ($this->getDataProvider()->getSearchResult()) { + return $this->getDataProvider()->getSearchResult()->getAllIds(); + } + return []; + } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/MassAction/FilterTest.php b/app/code/Magento/Ui/Test/Unit/Component/MassAction/FilterTest.php new file mode 100644 index 0000000000000000000000000000000000000000..604febecde041df3e62aeae3cf135bb5e57e5398 --- /dev/null +++ b/app/code/Magento/Ui/Test/Unit/Component/MassAction/FilterTest.php @@ -0,0 +1,289 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Ui\Test\Unit\Component\MassAction; + +use Magento\Ui\Component\MassAction\Filter; +use Magento\Framework\Api\Filter as ApiFilter; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Framework\Api\Search\SearchResultInterface; +use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; + +class FilterTest extends \PHPUnit_Framework_TestCase +{ + /** + *\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * \PHPUnit_Framework_MockObject_MockObject + */ + private $uiComponentFactoryMock; + + /** + * \PHPUnit_Framework_MockObject_MockObject + */ + private $filterBuilderMock; + + /** @var \Magento\Ui\Component\MassAction\Filter */ + private $filter; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManager; + + /** + *\PHPUnit_Framework_MockObject_MockObject + */ + private $dataProviderMock; + + /** + *\PHPUnit_Framework_MockObject_MockObject + */ + private $abstractDbMock; + + /** + * \PHPUnit_Framework_MockObject_MockObject + */ + private $searchResultMock; + + /** + * \PHPUnit_Framework_MockObject_MockObject + */ + private $uiComponentMock; + + /** + * Set up + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->uiComponentFactoryMock = $this->getMock(UiComponentFactory::class, [], [], '', false); + $this->filterBuilderMock = $this->getMock(FilterBuilder::class, [], [], '', false); + $this->requestMock = $this->getMock(RequestInterface::class); + $this->dataProviderMock = $this->getMock(DataProviderInterface::class); + $this->uiComponentMock = $this->getMock(UiComponentInterface::class); + $this->abstractDbMock = $this->getMock(AbstractDb::class, [], [], '', false); + $contextMock = $this->getMock(ContextInterface::class); + $this->searchResultMock = $this->getMock(SearchResultInterface::class); + $uiComponentMockTwo = $this->getMock(UiComponentInterface::class); + $this->filter = $this->objectManager->getObject( + Filter::class, + [ + 'factory' => $this->uiComponentFactoryMock, + 'request' => $this->requestMock, + 'filterBuilder' => $this->filterBuilderMock + ] + ); + $this->uiComponentFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->uiComponentMock); + $this->uiComponentMock->expects($this->any()) + ->method('getChildComponents') + ->willReturn([$uiComponentMockTwo]); + $uiComponentMockTwo->expects($this->any()) + ->method('getChildComponents') + ->willReturn([]); + $this->uiComponentMock->expects($this->any()) + ->method('getContext') + ->willReturn($contextMock); + $contextMock->expects($this->any()) + ->method('getDataProvider') + ->willReturn($this->dataProviderMock); + $this->dataProviderMock->expects($this->any()) + ->method('setLimit'); + $this->dataProviderMock->expects($this->any()) + ->method('searchResultMock') + ->willReturn($this->searchResultMock); + $this->searchResultMock->expects($this->any()) + ->method('getAllIds') + ->willReturn([]); + } + + /** + * Run test for applySelectionOnTargetProvider method + * + * @param int[]|bool $selectedIds + * @param int[]|bool $excludedIds + * @param int $filterExpected + * @param string $conditionExpected + * @dataProvider applySelectionOnTargetProviderDataProvider + */ + public function testApplySelectionOnTargetProvider($selectedIds, $excludedIds, $filterExpected, $conditionExpected) + { + $this->setUpApplySelection($selectedIds, $excludedIds, $filterExpected, $conditionExpected); + $this->filter->applySelectionOnTargetProvider(); + } + + /** + * Data provider for testApplySelectionOnTargetProvider + */ + public function applySelectionOnTargetProviderDataProvider() + { + return [ + [[1, 2, 3], 'false' , 0, 'in'], + [[1, 2, 3], [1, 2, 3] , 1, 'nin'], + ['false', [1, 2, 3] , 1, 'nin'], + ['false', 'false' , 0, ''] + ]; + } + + /** + * @throws \Exception + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testApplySelectionOnTargetProviderException() + { + $filterMock = $this->getMock(ApiFilter::class, [], [], '', false); + $this->filterBuilderMock->expects($this->any()) + ->method('setConditionType') + ->willReturn($this->filterBuilderMock); + $this->filterBuilderMock->expects($this->any()) + ->method('create') + ->willReturn($filterMock); + $this->filterBuilderMock->expects($this->any()) + ->method('setField') + ->willReturn($this->filterBuilderMock); + $this->requestMock->expects($this->at(0)) + ->method('getParam') + ->with(Filter::SELECTED_PARAM) + ->willReturn([1]); + $this->requestMock->expects($this->at(1)) + ->method('getParam') + ->with(Filter::EXCLUDED_PARAM) + ->willReturn([]); + $this->dataProviderMock->expects($this->any()) + ->method('addFilter') + ->with($filterMock) + ->willThrowException(new \Exception('exception')); + $this->filter->applySelectionOnTargetProvider(); + } + + /** + * Run test for getCollection method + * + * @param int[]|bool $selectedIds + * @param int[]|bool $excludedIds + * @param int $filterExpected + * @param string $conditionExpected + * @dataProvider applySelectionOnTargetProviderDataProvider + */ + public function testGetCollection($selectedIds, $excludedIds, $filterExpected, $conditionExpected) + { + $this->setUpApplySelection($selectedIds, $excludedIds, $filterExpected, $conditionExpected); + $this->requestMock->expects($this->at(4)) + ->method('getParam') + ->with('namespace') + ->willReturn(''); + $this->requestMock->expects($this->at(2)) + ->method('getParam') + ->with(Filter::SELECTED_PARAM) + ->willReturn($selectedIds); + $this->requestMock->expects($this->at(3)) + ->method('getParam') + ->with(Filter::EXCLUDED_PARAM) + ->willReturn($excludedIds); + $this->assertEquals($this->abstractDbMock, $this->filter->getCollection($this->abstractDbMock)); + } + + /** + * This tests the method prepareComponent() + */ + public function testPrepareComponent() + { + $this->filter->prepareComponent($this->uiComponentMock); + } + + /** + * This tests the method getComponent() + */ + public function testGetComponent() + { + $this->requestMock->expects($this->at(0)) + ->method('getParam') + ->with('namespace') + ->willReturn(''); + $this->assertEquals($this->uiComponentMock, $this->filter->getComponent()); + } + + /** + * This tests the method getComponentRefererUrl() + */ + public function testGetComponentRefererUrlIsNotNull() + { + $returnArray = [ + 'referer_url' => 'referer_url' + ]; + $this->dataProviderMock->expects($this->once()) + ->method('getConfigData') + ->willReturn($returnArray); + $this->assertEquals('referer_url', $this->filter->getComponentRefererUrl()); + } + + /** + * This tests the method getComponentRefererUrl() + */ + public function testGetComponentRefererUrlIsNull() + { + $this->assertNull($this->filter->getComponentRefererUrl()); + } + + /** + * Apply mocks for current parameters from datasource + * + * @param int[]|bool $selectedIds + * @param int[]|bool $excludedIds + * @param int $filterExpected + * @param string $conditionExpected + */ + private function setUpApplySelection($selectedIds, $excludedIds, $filterExpected, $conditionExpected) + { + $filterMock = $this->getMock(ApiFilter::class, [], [], '', false); + $this->requestMock->expects($this->at(0)) + ->method('getParam') + ->with(Filter::SELECTED_PARAM) + ->willReturn($selectedIds); + $this->requestMock->expects($this->at(1)) + ->method('getParam') + ->with(Filter::EXCLUDED_PARAM) + ->willReturn($excludedIds); + $this->dataProviderMock->expects($this->exactly($filterExpected)) + ->method('addFilter') + ->with($filterMock); + $this->filterBuilderMock->expects($this->exactly($filterExpected)) + ->method('setConditionType') + ->with($conditionExpected) + ->willReturnSelf(); + $this->filterBuilderMock->expects($this->any()) + ->method('setField') + ->willReturnSelf(); + $this->filterBuilderMock->expects($this->any()) + ->method('value') + ->willReturnSelf(); + $this->filterBuilderMock->expects($this->any()) + ->method('create') + ->willReturn($filterMock); + $this->filterBuilderMock->expects($this->any()) + ->method('setConditionType') + ->willReturnSelf(); + $this->filterBuilderMock->expects($this->any()) + ->method('setField') + ->willReturnSelf(); + $this->filterBuilderMock->expects($this->any()) + ->method('value') + ->willReturnSelf(); + $this->filterBuilderMock->expects($this->any()) + ->method('create') + ->willReturn($filterMock); + } +} diff --git a/app/code/Magento/Vault/Setup/UpgradeData.php b/app/code/Magento/Vault/Setup/UpgradeData.php index 757b5f4d3167cb00254e8f2ef20fad3addc1cb27..1c3f113ba9831de6123cbf0c0af361a0f520a1fb 100644 --- a/app/code/Magento/Vault/Setup/UpgradeData.php +++ b/app/code/Magento/Vault/Setup/UpgradeData.php @@ -20,9 +20,11 @@ use Magento\Vault\Model\CreditCardTokenFactory; class UpgradeData implements UpgradeDataInterface { /** - * @var AdapterInterface + * Predefined name for sales connection + * + * @var string */ - private $connection; + private static $salesConnectionName = 'sales'; /** * @inheritdoc @@ -30,12 +32,11 @@ class UpgradeData implements UpgradeDataInterface public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); - $connection = $this->getConnection(); // data update for Vault module < 2.0.1 if (version_compare($context->getVersion(), '2.0.1', '<')) { // update sets credit card as default token type - $connection->update($setup->getTable(InstallSchema::PAYMENT_TOKEN_TABLE), [ + $setup->getConnection()->update($setup->getTable(InstallSchema::PAYMENT_TOKEN_TABLE), [ PaymentTokenInterface::TYPE => CreditCardTokenFactory::TOKEN_TYPE_CREDIT_CARD ], PaymentTokenInterface::TYPE . ' = ""'); } @@ -43,12 +44,13 @@ class UpgradeData implements UpgradeDataInterface // data update for Vault module < 2.0.2 if (version_compare($context->getVersion(), '2.0.2', '<')) { // update converts additional info with token metadata to single dimensional array - $select = $connection->select() + $salesConnection = $setup->getConnection(self::$salesConnectionName); + $select = $salesConnection->select() ->from($setup->getTable('sales_order_payment'), 'entity_id') ->columns(['additional_information']) ->where('additional_information LIKE ?', '%token_metadata%'); - $items = $connection->fetchAll($select); + $items = $salesConnection->fetchAll($select); foreach ($items as $item) { $additionalInfo = unserialize($item['additional_information']); $additionalInfo[PaymentTokenInterface::CUSTOMER_ID] = @@ -57,7 +59,7 @@ class UpgradeData implements UpgradeDataInterface $additionalInfo['token_metadata'][PaymentTokenInterface::PUBLIC_HASH]; unset($additionalInfo['token_metadata']); - $connection->update( + $salesConnection->update( $setup->getTable('sales_order_payment'), ['additional_information' => serialize($additionalInfo)], ['entity_id = ?' => $item['entity_id']] @@ -67,23 +69,4 @@ class UpgradeData implements UpgradeDataInterface $setup->endSetup(); } - - /** - * Tries to get connection for scalable sales DB, otherwise returns default connection - * @return AdapterInterface - */ - private function getConnection() - { - if ($this->connection === null) { - /** @var ResourceConnection $conn */ - $conn = ObjectManager::getInstance()->get(ResourceConnection::class); - try { - $this->connection = $conn->getConnectionByName('sales'); - } catch (\DomainException $e) { - $this->connection = $conn->getConnection(); - } - } - - return $this->connection; - } } diff --git a/app/code/Magento/Vault/view/adminhtml/web/js/vault.js b/app/code/Magento/Vault/view/adminhtml/web/js/vault.js index 560e65c007f8ab22c2b5b1e76bac0c9062e2af78..66c17801ec02edb262434bc807988bed1bece940 100644 --- a/app/code/Magento/Vault/view/adminhtml/web/js/vault.js +++ b/app/code/Magento/Vault/view/adminhtml/web/js/vault.js @@ -33,8 +33,8 @@ define([ .observe(['active']); // re-init payment method events - self.$selector.off('changePaymentMethod.' + this.code) - .on('changePaymentMethod.' + this.code, this.changePaymentMethod.bind(this)); + self.$selector.off('changePaymentMethod.' + this.getCode()) + .on('changePaymentMethod.' + this.getCode(), this.changePaymentMethod.bind(this)); if (this.active()) { $('#' + this.fieldset + ' input:radio:first').trigger('click'); @@ -50,7 +50,7 @@ define([ * @returns {exports.changePaymentMethod} */ changePaymentMethod: function (event, method) { - this.active(method === this.code); + this.active(method === this.getCode()); return this; }, @@ -61,13 +61,21 @@ define([ */ onActiveChange: function (isActive) { if (!isActive) { - this.$selector.trigger('setVaultNotActive'); + this.$selector.trigger('setVaultNotActive.' + this.getCode()); return; } $('#' + this.fieldset + ' input:radio:first').trigger('click'); - window.order.addExcludedPaymentMethod(this.code); + window.order.addExcludedPaymentMethod(this.getCode()); + }, + + /** + * Get payment method code + * @returns {String} + */ + getCode: function () { + return this.code; } }); }); diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml index 66d912d77bba7836ab2f747757f32f2a82cf02ec..5f5e2f50594bf8d0473b65ee584e50f47bc852a4 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/ReorderUsingVaultTest.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Vault\Test\TestCase\ReorderUsingVaultTest" summary="Reorder from Admin with saved within Braintree credit card"> - <variation name="ReorderUsingVaultBraintreeTestVariation1" summary="Reorder from Admin with saved within Braintree credit card for Guest Customer" ticketId="MAGETWO-54870"> + <variation name="ReorderUsingVaultBraintreeTestVariation1" summary="Reorder from Admin with saved within Braintree credit card for Guest Customer" ticketId="MAGETWO-54869, MAGETWO-54870"> <data name="description" xsi:type="string">Reorder from Admin with saved within Braintree credit card for Guest Customer</data> <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> <data name="customer/dataset" xsi:type="string">default</data> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..a34bf44d38cae01f46f711fd6181c1ae8895ae68 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Sales\Test\TestCase\MoveRecentlyComparedProductsOnOrderPageTest"> + <variation name="MoveRecentlyComparedProductsOnOrderPageTestVariationWithBundleProduct1"> + <data name="products/0" xsi:type="string">bundleProduct::bundle_dynamic_product</data> + <data name="products/1" xsi:type="string">bundleProduct::bundle_dynamic_product</data> + <data name="productsIsConfigured" xsi:type="boolean">true</data> + <constraint name="Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/etc/di.xml index 402dcca44c9485a46d9c40b5103f1cac50b86122..61eac871df9722550fff8735930febc339e4ea4d 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/etc/di.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/etc/di.xml @@ -11,4 +11,17 @@ <argument name="severity" xsi:type="string">S2</argument> </arguments> </type> + <type name="Magento\Sales\Test\Block\Adminhtml\Order\Create\CustomerActivities\Sidebar\RecentlyComparedProducts"> + <arguments> + <argument name="config" xsi:type="array"> + <item name="renders" xsi:type="array"> + <item name="bundle" xsi:type="array"> + <item name="class" xsi:type="string">\Magento\Bundle\Test\Block\Adminhtml\Product\Composite\Configure</item> + <item name="locator" xsi:type="string">//ancestor::body//*[contains(@class, "modal-slide") and contains(@class, "_show")]</item> + <item name="strategy" xsi:type="string">xpath</item> + </item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Page/Adminhtml/OrderCreateIndex.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Page/Adminhtml/OrderCreateIndex.xml index a67119cbf19aa5ac5aa1541672a30242edd29a93..97809af4bff7875f1bb4c90fe0f3a9c05d037b24 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Page/Adminhtml/OrderCreateIndex.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Page/Adminhtml/OrderCreateIndex.xml @@ -6,9 +6,9 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="OrderCreateIndex" area="Adminhtml" mca="sales/order_create/index"> - <block name="configureProductBlock"> - <render name="configurable" class="Magento\ConfigurableProduct\Test\Block\Adminhtml\Product\Composite\Configure"/> - </block> - </page> + <page name="OrderCreateIndex" area="Adminhtml" mca="sales/order_create/index"> + <block name="configureProductBlock"> + <render name="configurable" class="Magento\ConfigurableProduct\Test\Block\Adminhtml\Product\Composite\Configure" /> + </block> + </page> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCreditMemoEntityTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCreditMemoEntityTest.xml index dd92edc82b3310ad9610c70138578a220e4358cd..8a8195db638d565564ae0e2bdf20ad46fbb05bbd 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCreditMemoEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCreditMemoEntityTest.xml @@ -14,6 +14,7 @@ <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">configurableProduct::configurable_with_qty_1</data> <data name="order/data/price/dataset" xsi:type="string">full_refund</data> + <data name="configData" xsi:type="string"/> <constraint name="Magento\Sales\Test\Constraint\AssertRefundSuccessCreateMessage" /> <constraint name="Magento\ConfigurableProduct\Test\Constraint\AssertProductQtyDecreasedAfterCreditmemo" /> </variation> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..aaa9b3e1f88f918aa63f852b28a5ac653f94cdef --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Sales\Test\TestCase\MoveRecentlyComparedProductsOnOrderPageTest"> + <variation name="MoveRecentlyComparedProductsOnOrderPageTestVariationWithConfigurableProduct1"> + <data name="products/0" xsi:type="string">configurableProduct::configurable_with_qty_1</data> + <data name="products/1" xsi:type="string">configurableProduct::configurable_with_qty_1</data> + <constraint name="Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/etc/di.xml index 002ccfc4ed80ca48fb1b851558efa909b2e1c503..8bdf098cea58341cf83b081fca7bc946281e4884 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/etc/di.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/etc/di.xml @@ -26,4 +26,17 @@ <argument name="severity" xsi:type="string">high</argument> </arguments> </type> + <type name="Magento\Sales\Test\Block\Adminhtml\Order\Create\CustomerActivities\Sidebar\RecentlyComparedProducts"> + <arguments> + <argument name="config" xsi:type="array"> + <item name="renders" xsi:type="array"> + <item name="configurable" xsi:type="array"> + <item name="class" xsi:type="string">Magento\ConfigurableProduct\Test\Block\Adminhtml\Product\Composite\Configure</item> + <item name="locator" xsi:type="string">//ancestor::body//*[contains(@class, "modal-slide") and contains(@class, "_show")]</item> + <item name="strategy" xsi:type="string">xpath</item> + </item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Adminhtml/Product/Composite/Configure.xml b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Adminhtml/Product/Composite/Configure.xml index 04a918c3bf8d9598b74b0f53ebd41b9f477e778f..676ae6a64f3d820b4d65d821053d6d0ab7971542 100644 --- a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Adminhtml/Product/Composite/Configure.xml +++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Adminhtml/Product/Composite/Configure.xml @@ -8,7 +8,7 @@ <mapping strict="0"> <fields> <qty> - <selector>//tr[contains(.,"%product_name%")]//input[contains(@class,"qty")]</selector> + <selector>.//tr[contains(.,"%product_name%")]//input[contains(@class,"qty")]</selector> <strategy>xpath</strategy> </qty> </fields> diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Constraint/AssertProductInItemsOrderedGrid.php b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Constraint/AssertProductInItemsOrderedGrid.php new file mode 100644 index 0000000000000000000000000000000000000000..918c86f93340730e19ac576c1cda37255d86f0b3 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Constraint/AssertProductInItemsOrderedGrid.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\GroupedProduct\Test\Constraint; + +/** + * Assert product was added to Items Ordered grid in customer account on Order creation page backend. + */ +class AssertProductInItemsOrderedGrid extends \Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid +{ + /** + * Prepare data. + * + * @param array $data + * @param \Magento\Sales\Test\Block\Adminhtml\Order\Create\Items $itemsBlock + * @return array + */ + protected function prepareData(array $data, \Magento\Sales\Test\Block\Adminhtml\Order\Create\Items $itemsBlock) + { + $fixtureData = []; + foreach ($data as $product) { + $fixtureData = array_merge($fixtureData, $this->getOptionsDetails($product)); + } + $pageData = $itemsBlock->getProductsDataByFields($this->fields); + $preparePageData = $this->arraySort($fixtureData, $pageData); + return ['fixtureData' => $fixtureData, 'pageData' => $preparePageData]; + } + + /** + * Get product options details. + * + * @param \Magento\Mtf\Fixture\FixtureInterface $product + * @return array + */ + private function getOptionsDetails(\Magento\Mtf\Fixture\FixtureInterface $product) + { + /** @var \Magento\GroupedProduct\Test\Fixture\GroupedProduct $product */ + $fixtureProducts = []; + $optionsPrices = $this->getProductPrice($product); + $optionsQtys = $product->getCheckoutData()['cartItem']['qty']; + $assignedProducts = $product->getAssociated()['assigned_products']; + + foreach ($assignedProducts as $key => $assignedProduct) { + $fixtureProducts[] = [ + 'name' => $assignedProduct['name'], + 'price' => number_format($optionsPrices['product_key_' . $key], 2), + 'checkout_data' => [ + 'qty' => $this->productsIsConfigured ? $optionsQtys['product_key_' . $key] : 1 + ] + ]; + } + return $fixtureProducts; + } +} diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..1de8aeda4e9a4415b476d73c8b454b17ad4f5824 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Sales\Test\TestCase\MoveRecentlyComparedProductsOnOrderPageTest"> + <variation name="MoveRecentlyComparedProductsOnOrderPageTestVariationWithGroupedProduct1"> + <data name="products/0" xsi:type="string">groupedProduct::three_simple_products</data> + <data name="products/1" xsi:type="string">groupedProduct::three_simple_products</data> + <data name="productsIsConfigured" xsi:type="boolean">true</data> + <constraint name="Magento\GroupedProduct\Test\Constraint\AssertProductInItemsOrderedGrid" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/etc/di.xml new file mode 100644 index 0000000000000000000000000000000000000000..5b12a012749fb6feb0e90b1910d964a19394ca8d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/etc/di.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Sales\Test\Block\Adminhtml\Order\Create\CustomerActivities\Sidebar\RecentlyComparedProducts"> + <arguments> + <argument name="config" xsi:type="array"> + <item name="renders" xsi:type="array"> + <item name="grouped" xsi:type="array"> + <item name="class" xsi:type="string">\Magento\GroupedProduct\Test\Block\Adminhtml\Product\Composite\Configure</item> + <item name="locator" xsi:type="string">//ancestor::body//*[contains(@class, "modal-slide") and contains(@class, "_show")]</item> + <item name="strategy" xsi:type="string">xpath</item> + </item> + </item> + </argument> + </arguments> + </type> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/ReorderUsingVaultTest.xml b/dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/ReorderUsingVaultTest.xml index b1da43e473bc5d78d4bfaf0fbcbb718dac6acd3e..ad937cf472de7145429b16983288df69bb851134 100644 --- a/dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/ReorderUsingVaultTest.xml +++ b/dev/tests/functional/tests/app/Magento/Paypal/Test/TestCase/ReorderUsingVaultTest.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Vault\Test\TestCase\ReorderUsingVaultTest" summary="Reorder from Admin with saved within PayPal Payflow Pro credit card"> - <variation name="ReorderUsingVaultPayflowProTestVariation1" summary="Reorder from Admin with saved within PayPal Payflow Pro credit card for Guest Customer" ticketId="MAGETWO-54872"> + <variation name="ReorderUsingVaultPayflowProTestVariation1" summary="Reorder from Admin with saved within PayPal Payflow Pro credit card for Guest Customer" ticketId="MAGETWO-34217, MAGETWO-54872"> <data name="description" xsi:type="string">Reorder from Admin with saved within PayPal Payflow Pro credit card for Guest Customer</data> <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> <data name="customer/dataset" xsi:type="string">default</data> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/CustomerActivities/Sidebar.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/CustomerActivities/Sidebar.php index 050f797d83825e99eaf08728c2dc69149f8faaae..3fc41e0db1785d06eb29bd58b7343b02a274ee00 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/CustomerActivities/Sidebar.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/CustomerActivities/Sidebar.php @@ -21,6 +21,13 @@ abstract class Sidebar extends Block */ protected $addToOrder = './/tr[td[.="%s"]]//input[contains(@name,"add")]'; + /** + * 'Add to order' configure. + * + * @var string + */ + protected $addToOrderConfigure = './/tr[td[contains(.,"%s")]]//a[contains(@class, "icon-configure")]'; + /** * 'Add to order' checkbox. * @@ -39,9 +46,18 @@ abstract class Sidebar extends Block foreach ($products as $product) { $name = $product->getName(); $this->_rootElement->find(sprintf($this->addToOrderProductName, $name), Locator::SELECTOR_XPATH)->click(); - $this->_rootElement->click(); - $this->_rootElement->find(sprintf($this->addToOrder, $name), Locator::SELECTOR_XPATH, 'checkbox') - ->setValue('Yes'); + + $dataConfig = $product->getDataConfig(); + $typeId = isset($dataConfig['type_id']) ? $dataConfig['type_id'] : null; + + if ($this->hasRender($typeId)) { + $this->_rootElement->find(sprintf($this->addToOrderConfigure, $name), Locator::SELECTOR_XPATH)->click(); + $this->callRender($typeId, 'configProduct', ['product' => $product]); + } else { + $this->_rootElement->click(); + $this->_rootElement->find(sprintf($this->addToOrder, $name), Locator::SELECTOR_XPATH, 'checkbox') + ->setValue('Yes'); + } } } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.php index 49487ff1b2e8ea9b9e23543a44a2878b797c4f2c..a501ec3cd228d8c60a5225027719e463dbf6c688 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.php @@ -142,9 +142,10 @@ class MoveRecentlyComparedProductsOnOrderPageTest extends Injectable * * @param Customer $customer * @param string $products + * @param bool $productsIsConfigured * @return array */ - public function test(Customer $customer, $products) + public function test(Customer $customer, $products, $productsIsConfigured = false) { // Preconditions // Create product @@ -168,6 +169,6 @@ class MoveRecentlyComparedProductsOnOrderPageTest extends Injectable $activitiesBlock->getRecentlyComparedProductsBlock()->addProductsToOrder($products); $activitiesBlock->updateChanges(); - return ['products' => $products, 'productsIsConfigured' => false]; + return ['products' => $products, 'productsIsConfigured' => $productsIsConfigured]; } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml index 601f550e4588bfd3c613ef8a50e8615174b298ae..76282ff3eec53867d257029f5184b941899ac2db 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.xml @@ -7,17 +7,10 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\MoveRecentlyComparedProductsOnOrderPageTest" summary="Add Products to Order from Recently Compared Products Section" ticketId="MAGETWO-28109"> - <variation name="MoveRecentlyComparedProductsOnOrderPageTestVariation1"> - <data name="tag" xsi:type="string">stable:no</data> + <variation name="MoveRecentlyComparedProductsOnOrderPageTestVariationWithSimpleProduct1"> <data name="products/0" xsi:type="string">catalogProductSimple::default</data> <data name="products/1" xsi:type="string">catalogProductSimple::default</data> <constraint name="Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid" /> </variation> - <variation name="MoveRecentlyComparedProductsOnOrderPageTestVariation2"> - <data name="tag" xsi:type="string">to_maintain:yes</data> - <data name="products/0" xsi:type="string">configurableProduct::configurable_with_qty_1</data> - <data name="products/1" xsi:type="string">configurableProduct::configurable_with_qty_1</data> - <constraint name="Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid" /> - </variation> </testCase> </config> diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ab35c27985da8f13fb2fd4362c719fe0d3267315 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/Directpost/RequestTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Authorizenet\Model\Directpost; + +use Magento\Authorizenet\Model\Directpost; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\ObjectManager; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class contains tests for Authorize.net Direct Post request handler + */ +class RequestTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Order + */ + private $order; + + /** + * @var Request + */ + private $request; + + /** + * @var ObjectManager + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $this->order = $this->getOrder(); + $this->request = $this->objectManager->get(Request::class); + } + + /** + * @covers \Magento\Authorizenet\Model\Directpost\Request::setDataFromOrder + * @magentoDataFixture Magento/Authorizenet/_files/order.php + */ + public function testSetDataFromOrder() + { + $customerEmail = 'john.doe@example.com'; + $merchantEmail = 'merchant@example.com'; + + /** @var Directpost|MockObject $payment */ + $payment = $this->getMockBuilder(Directpost::class) + ->disableOriginalConstructor() + ->setMethods(['getConfigData']) + ->getMock(); + + $payment->expects(static::exactly(2)) + ->method('getConfigData') + ->willReturnMap([ + ['email_customer', null, $customerEmail], + ['merchant_email', null, $merchantEmail] + ]); + + $result = $this->request->setDataFromOrder($this->order, $payment); + + static::assertEquals('US', $result->getXCountry()); + static::assertEquals('UK', $result->getXShipToCountry()); + static::assertEquals($customerEmail, $result->getXEmailCustomer()); + static::assertEquals($merchantEmail, $result->getXMerchantEmail()); + } + + /** + * Get stored order + * @return Order + */ + private function getOrder() + { + /** @var FilterBuilder $filterBuilder */ + $filterBuilder = $this->objectManager->get(FilterBuilder::class); + $filters = [ + $filterBuilder->setField(OrderInterface::INCREMENT_ID) + ->setValue('100000002') + ->create() + ]; + + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilters($filters) + ->create(); + + $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); + $orders = $orderRepository->getList($searchCriteria) + ->getItems(); + + /** @var OrderInterface $order */ + return array_pop($orders); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c20db43c127b4a9c058cb206cde14822f9aafd84 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Authorizenet/Model/DirectpostTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Authorizenet\Model; + +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order\Payment; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Zend_Http_Response; + +/** + * Class contains tests for Direct Post integration + */ +class DirectpostTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ZendClientFactory|MockObject + */ + private $httpClientFactory; + + /** + * @var Directpost + */ + private $directPost; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $this->httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->directPost = $this->objectManager->create(Directpost::class, [ + 'httpClientFactory' => $this->httpClientFactory + ]); + } + + /** + * @covers \Magento\Authorizenet\Model\Directpost::capture + * @magentoDataFixture Magento/Authorizenet/_files/order.php + */ + public function testCapture() + { + $amount = 120.15; + /** @var Payment $payment */ + $payment = $this->getPayment(); + $transactionId = '106235225'; + + /** @var ZendClient|MockObject $httpClient */ + $httpClient = $this->getMockBuilder(ZendClient::class) + ->disableOriginalConstructor() + ->setMethods(['setUri', 'setConfig', 'setParameterPost', 'setMethod', 'request']) + ->getMock(); + + $this->httpClientFactory->expects(static::once()) + ->method('create') + ->willReturn($httpClient); + + $response = $this->getMockBuilder(Zend_Http_Response::class) + ->disableOriginalConstructor() + ->setMethods(['getBody']) + ->getMock(); + $response->expects(static::once()) + ->method('getBody') + ->willReturn( + "1(~)1(~)1(~)This transaction has been approved.(~)AWZFTG(~)P(~){$transactionId}(~)100000002(~) + (~)120.15(~)CC(~)prior_auth_capture(~)(~)Anthony(~)Nealy(~)(~)Pearl St(~)Los Angeles(~)California + (~)10020(~)US(~)22-333-44(~)(~)customer@example.com(~)John(~)Doe(~) + (~)Bourne St(~)London(~)(~)DW23W(~)UK(~)0.00(~)(~){$amount}(~)(~) + (~)74B5D54ADFE98093A0FF6446(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)XXXX1111(~)Visa(~)(~)(~)(~)(~) + (~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)(~)" + ); + + $httpClient->expects(static::once()) + ->method('request') + ->willReturn($response); + + $this->directPost->capture($payment, $amount); + + static::assertEquals($transactionId, $payment->getTransactionId()); + static::assertFalse($payment->getIsTransactionClosed()); + static::assertEquals('US', $payment->getOrder()->getBillingAddress()->getCountryId()); + static::assertEquals('UK', $payment->getOrder()->getShippingAddress()->getCountryId()); + } + + /** + * Get order payment + * @return Payment + */ + private function getPayment() + { + /** @var FilterBuilder $filterBuilder */ + $filterBuilder = $this->objectManager->get(FilterBuilder::class); + $filters = [ + $filterBuilder->setField(OrderInterface::INCREMENT_ID) + ->setValue('100000002') + ->create() + ]; + + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilters($filters) + ->create(); + + $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); + $orders = $orderRepository->getList($searchCriteria) + ->getItems(); + + /** @var OrderInterface $order */ + $order = array_pop($orders); + return $order->getPayment(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php new file mode 100644 index 0000000000000000000000000000000000000000..564ab84a8f8527c61bf8e79f7a57ad53b971089f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Authorizenet/_files/order.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Payment; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +$amount = 120.15; + +/** @var Payment $payment */ +$payment = $objectManager->get(Payment::class); +$payment + ->setMethod('authorizenet_directpost') + ->setAnetTransType('AUTH_ONLY') + ->setBaseAmountAuthorized($amount) + ->setPoNumber('10101200'); + +/** @var Address\ $billingAddress */ +$billingAddress = $objectManager->create(Address::class, [ + 'data' => [ + 'firstname' => 'John', + 'lastname' => 'Doe', + 'email' => 'customer@example.com', + 'street' => 'Pearl St', + 'city' => 'Los Angeles', + 'region' => 'CA', + 'postcode' => '10020', + 'country_id' => 'US', + 'telephone' => '22-333-44', + 'address_type' => 'billing' + ] +]); + +$shippingAddress = $objectManager->create(Address::class, [ + 'data' => [ + 'firstname' => 'John', + 'lastname' => 'Doe', + 'email' => 'customer@example.com', + 'street' => 'Bourne St', + 'city' => 'London', + 'postcode' => 'DW23W', + 'country_id' => 'UK', + 'telephone' => '22-333-44', + 'address_type' => 'billing' + ] +]); + +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000002') + ->setQuoteId(2) + ->setIncrementId('100000002') + ->setBaseGrandTotal($amount) + ->setBaseCurrencyCode('USD') + ->setBaseTaxAmount($amount) + ->setBaseShippingAmount($amount) + ->setCustomerEmail('customer@example.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setPayment($payment); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/Column/Renderer/TextTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/Column/Renderer/TextTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9d2875716156e83b48502be84a4ef1a86ef46772 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/Column/Renderer/TextTest.php @@ -0,0 +1,142 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Backend\Block\Widget\Grid\Column\Renderer; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\Backend\Block\Widget\Grid\Column; +use Magento\Framework\DataObject; +use Magento\Framework\Phrase; +use Magento\Framework\Phrase\RendererInterface; + +class TextTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var RendererInterface + */ + private $origRenderer; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->origRenderer = Phrase::getRenderer(); + /** @var RendererInterface|PHPUnit_Framework_MockObject_MockObject $rendererMock */ + $rendererMock = $this->getMock(RendererInterface::class); + $rendererMock->expects($this->any()) + ->method('render') + ->willReturnCallback( + function ($input) { + return end($input) . ' translated'; + } + ); + Phrase::setRenderer($rendererMock); + } + + protected function tearDown() + { + Phrase::setRenderer($this->origRenderer); + } + + /** + * @param array $columnData + * @param array $rowData + * @param string $expected + * @dataProvider renderDataProvider + */ + public function testRender($columnData, $rowData, $expected) + { + /** @var Text $renderer */ + $renderer = $this->objectManager->create(Text::class); + /** @var Column $column */ + $column = $this->objectManager->create( + Column::class, + [ + 'data' => $columnData + ] + ); + /** @var DataObject $row */ + $row = $this->objectManager->create( + DataObject::class, + [ + 'data' => $rowData + ] + ); + $this->assertEquals( + $expected, + $renderer->setColumn($column)->render($row) + ); + } + + /** + * @return array + */ + public function renderDataProvider() + { + return [ + [ + [ + 'index' => 'title', + 'translate' => true + ], + [ + 'title' => 'String' + ], + 'String translated' + ], + [ + [ + 'index' => 'title' + ], + [ + 'title' => 'Doesn\'t need to be translated' + ], + 'Doesn't need to be translated' + ], + [ + [ + 'format' => '#$subscriber_id $customer_name ($subscriber_email)' + ], + [ + 'subscriber_id' => '10', + 'customer_name' => 'John Doe', + 'subscriber_email' => 'john@doe.com' + ], + '#10 John Doe (john@doe.com)' + ], + [ + [ + 'format' => '$customer_name, email: $subscriber_email', + 'translate' => true + ], + [ + 'customer_name' => 'John Doe', + 'subscriber_email' => 'john@doe.com' + ], + 'John Doe, email: john@doe.com translated' + ], + [ + [ + 'format' => 'String', + 'translate' => true + ], + [], + 'String translated' + ], + [ + [ + 'format' => 'Doesn\'t need to be translated' + ], + [], + 'Doesn't need to be translated' + ] + ]; + } +} diff --git a/dev/tests/static/framework/Magento/Sniffs/Translation/ConstantUsageSniff.php b/dev/tests/static/framework/Magento/Sniffs/Translation/ConstantUsageSniff.php index 260f731aecf1ce30d60cbd5dee1c2fb0f20074d2..4f9082aa4eb2124072b3c9fe39ddd50a901b7bd1 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Translation/ConstantUsageSniff.php +++ b/dev/tests/static/framework/Magento/Sniffs/Translation/ConstantUsageSniff.php @@ -5,12 +5,10 @@ */ namespace Magento\Sniffs\Translation; -use PHP_CodeSniffer_File; - /** * Make sure that constants are not used as the first argument of translation function. */ -class ConstantUsageSniff extends \Generic_Sniffs_Files_LineLengthSniff +class ConstantUsageSniff implements \PHP_CodeSniffer_Sniff { /** * Having previous line content allows to process multi-line declaration. @@ -20,24 +18,73 @@ class ConstantUsageSniff extends \Generic_Sniffs_Files_LineLengthSniff protected $previousLineContent = ''; /** - * {@inheritdoc} + * {@inheritDoc} + */ + public function register() + { + return [T_OPEN_TAG]; + + } + + /** + * Copied from \Generic_Sniffs_Files_LineLengthSniff, minor changes made + * + * {@inheritDoc} */ - protected function checkLineLength(\PHP_CodeSniffer_File $phpcsFile, $stackPtr, $lineContent) + public function process(\PHP_CodeSniffer_File $phpcsFile, $stackPtr) { - $previousLineRegexp = '~__\($|Phrase\($~'; - $currentLineRegexp = '~__\(.+\)|Phrase\(.+\)~'; + $tokens = $phpcsFile->getTokens(); + + // Make sure this is the first open tag + $previousOpenTag = $phpcsFile->findPrevious(T_OPEN_TAG, ($stackPtr - 1)); + if ($previousOpenTag !== false) { + return; + } + + $tokenCount = 0; + $currentLineContent = ''; + $currentLine = 1; + + for (; $tokenCount < $phpcsFile->numTokens; $tokenCount++) { + if ($tokens[$tokenCount]['line'] === $currentLine) { + $currentLineContent .= $tokens[$tokenCount]['content']; + } else { + $this->checkIfFirstArgumentConstant($phpcsFile, ($tokenCount - 1), $currentLineContent); + $currentLineContent = $tokens[$tokenCount]['content']; + $currentLine++; + } + } + + $this->checkIfFirstArgumentConstant($phpcsFile, ($tokenCount - 1), $currentLineContent); + } + + /** + * Checks if first argument of \Magento\Framework\Phrase or translation function is a constant + * + * @param \PHP_CodeSniffer_File $phpcsFile + * @param int $stackPtr + * @param string $lineContent + * @return void + */ + private function checkIfFirstArgumentConstant( + \PHP_CodeSniffer_File $phpcsFile, + $stackPtr, + $lineContent + ) { + $previousLineRegexp = '/(__|Phrase)\($/im'; + $currentLineRegexp = '/(__|Phrase)\(.+\)/'; $currentLineMatch = preg_match($currentLineRegexp, $lineContent) !== 0; $previousLineMatch = preg_match($previousLineRegexp, $this->previousLineContent) !== 0; $this->previousLineContent = $lineContent; $error = 'Constants are not allowed as the first argument of translation function, use string literal instead'; - $constantRegexp = '[^\'"]+::[A-Z_0-9]+.*'; + $constantRegexp = '[^\$\'"]+::[A-Z_0-9]+.*'; if ($currentLineMatch) { - $variableRegexp = "~__\({$constantRegexp}\)|Phrase\({$constantRegexp}\)~"; + $variableRegexp = "/(__|Phrase)\({$constantRegexp}\)/"; if (preg_match($variableRegexp, $lineContent) !== 0) { $phpcsFile->addError($error, $stackPtr, 'VariableTranslation'); } } else if ($previousLineMatch) { - $variableRegexp = "~^\s+{$constantRegexp}~"; + $variableRegexp = "/^{$constantRegexp}/"; if (preg_match($variableRegexp, $lineContent) !== 0) { $phpcsFile->addError($error, $stackPtr, 'VariableTranslation'); } diff --git a/dev/tests/static/framework/Magento/TestFramework/Utility/ChangedFiles.php b/dev/tests/static/framework/Magento/TestFramework/Utility/ChangedFiles.php index d71c03eb0f85d01634f6f3a28c185c37e84bc1b2..571b663067151db4eb05d1484f38e1e10cd9527e 100644 --- a/dev/tests/static/framework/Magento/TestFramework/Utility/ChangedFiles.php +++ b/dev/tests/static/framework/Magento/TestFramework/Utility/ChangedFiles.php @@ -3,10 +3,10 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\TestFramework\Utility; use Magento\Framework\App\Utility\Files; +use Magento\TestFramework\Utility\File\RegexIteratorFactory; /** * A helper to gather various changed files @@ -23,7 +23,7 @@ class ChangedFiles */ public static function getPhpFiles($changedFilesList) { - $fileHelper = \Magento\Framework\App\Utility\Files::init(); + $fileUtilities = new File(Files::init(), new RegexIteratorFactory()); if (isset($_ENV['INCREMENTAL_BUILD'])) { $phpFiles = []; foreach (glob($changedFilesList) as $listFile) { @@ -36,27 +36,11 @@ class ChangedFiles } ); if (!empty($phpFiles)) { - $phpFiles = \Magento\Framework\App\Utility\Files::composeDataSets($phpFiles); - $phpFiles = array_intersect_key($phpFiles, $fileHelper->getPhpFiles( - Files::INCLUDE_APP_CODE - | Files::INCLUDE_PUB_CODE - | Files::INCLUDE_LIBS - | Files::INCLUDE_TEMPLATES - | Files::INCLUDE_TESTS - | Files::AS_DATA_SET - | Files::INCLUDE_NON_CLASSES - )); + $phpFiles = Files::composeDataSets($phpFiles); + $phpFiles = array_intersect_key($phpFiles, $fileUtilities->getPhpFiles()); } } else { - $phpFiles = $fileHelper->getPhpFiles( - Files::INCLUDE_APP_CODE - | Files::INCLUDE_PUB_CODE - | Files::INCLUDE_LIBS - | Files::INCLUDE_TEMPLATES - | Files::INCLUDE_TESTS - | Files::AS_DATA_SET - | Files::INCLUDE_NON_CLASSES - ); + $phpFiles = $fileUtilities->getPhpFiles(); } return $phpFiles; diff --git a/dev/tests/static/framework/Magento/TestFramework/Utility/File.php b/dev/tests/static/framework/Magento/TestFramework/Utility/File.php new file mode 100644 index 0000000000000000000000000000000000000000..7a8e02e5143a0808867a4b9c1d1d01b2da7b34f9 --- /dev/null +++ b/dev/tests/static/framework/Magento/TestFramework/Utility/File.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\TestFramework\Utility; + +use Magento\Framework\App\Utility\Files; +use Magento\TestFramework\Utility\File\RegexIteratorFactory; + +/** + * Get list of PHP files including files in setup application + */ +class File +{ + /** + * @var RegexIteratorFactory + */ + private $regexIteratorFactory; + + /** + * @var Files + */ + private $fileUtilities; + + /** + * Constructor + * + * @param Files $fileUtilities + * @param RegexIteratorFactory $regexIteratorFactory + */ + public function __construct( + Files $fileUtilities, + RegexIteratorFactory $regexIteratorFactory + ) { + $this->fileUtilities = $fileUtilities; + $this->regexIteratorFactory = $regexIteratorFactory; + } + + /** + * Get list of PHP files + * + * @return array + * @throws \Exception + */ + public function getPhpFiles() + { + $files = array_merge( + $this->fileUtilities->getPhpFiles( + Files::INCLUDE_APP_CODE + | Files::INCLUDE_PUB_CODE + | Files::INCLUDE_LIBS + | Files::INCLUDE_TEMPLATES + | Files::INCLUDE_TESTS + | Files::INCLUDE_NON_CLASSES + ), + $this->getSetupPhpFiles() + ); + return Files::composeDataSets($files); + } + + /** + * Get list of PHP files in setup application + * + * @param int $flags + * @return array + */ + private function getSetupPhpFiles() + { + $files = []; + $regexIterator = $this->regexIteratorFactory->create( + BP . '/setup', + '/.*php^/' + ); + foreach ($regexIterator as $file) { + $files = array_merge($files, [$file]); + } + return $files; + } +} diff --git a/dev/tests/static/framework/Magento/TestFramework/Utility/File/RegexIteratorFactory.php b/dev/tests/static/framework/Magento/TestFramework/Utility/File/RegexIteratorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d2e5768414f06f13138ba83ebcf34ce7535783e6 --- /dev/null +++ b/dev/tests/static/framework/Magento/TestFramework/Utility/File/RegexIteratorFactory.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\TestFramework\Utility\File; + +/** + * Factory for \RegexIterator + */ +class RegexIteratorFactory +{ + /** + * Create instance of \RegexIterator + * + * @param string $directoryPath + * @param string $regexp + * @return \RegexIterator + */ + public function create($directoryPath, $regexp) + { + $directory = new \RecursiveDirectoryIterator($directoryPath); + $recursiveIterator = new \RecursiveIteratorIterator($directory); + return new \RegexIterator($recursiveIterator, $regexp, \RegexIterator::GET_MATCH); + } +} diff --git a/dev/tests/static/framework/Magento/TestFramework/Utility/FunctionDetector.php b/dev/tests/static/framework/Magento/TestFramework/Utility/FunctionDetector.php new file mode 100644 index 0000000000000000000000000000000000000000..17310404e372097169f7cc36dcd8bb125349565d --- /dev/null +++ b/dev/tests/static/framework/Magento/TestFramework/Utility/FunctionDetector.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\TestFramework\Utility; + +/** + * Check if one or more functions are used in the file + */ +class FunctionDetector +{ + /** + * Detect functions in given file + * + * return result in this format: + * [ + * line_number => [ + * function_name_1, + * function_name_2, + * function_name_3, + * ], + * line_number => [ + * function_name_1, + * function_name_2, + * function_name_3, + * ], + * ] + * + * @param string $filePath + * @param string[] $functions + * @return array + */ + public function detect($filePath, $functions) + { + $result = []; + $regexp = $this->composeRegexp($functions); + if ($regexp) { + $file = file($filePath); + array_unshift($file, ''); + $lines = preg_grep( + $regexp, + $file + ); + foreach ($lines as $lineNumber => $line) { + if (preg_match_all($regexp, $line, $matches)) { + $result[$lineNumber] = $matches[1]; + } + } + } + return $result; + } + + /** + * Compose regular expression + * + * @param array $functions + * @return string + */ + private function composeRegexp(array $functions) + { + if (empty($functions)) { + return ''; + } + return '/(?<!function |->|::)\b(' . join('|', $functions) . ')\s*\(/i'; + } +} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/ConstantUsageSniffTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/ConstantUsageSniffTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a64d47054ede9bc1f695514aa8f3ef95fc1e8eeb --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/ConstantUsageSniffTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sniffs\Translation; + +class ConstantUsageSniffTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHP_CodeSniffer_File|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileMock; + + /** + * @var ConstantUsageSniff + */ + private $constantUsageSniff; + + protected function setUp() + { + $this->fileMock = $this->getMock(\PHP_CodeSniffer_File::class, [], [], '', false); + $this->constantUsageSniff = new ConstantUsageSniff(); + } + + /** + * @param string $file + * @param int $numIncorrectUsages + * @dataProvider processDataProvider + */ + public function testProcessIncorrectArguments($file, $numIncorrectUsages) + { + $stackPtr = 10; + $fileContent = file_get_contents(__DIR__ . '/_files/' . $file); + $tokens = $this->tokenizeString($fileContent); + $this->fileMock->expects($this->once()) + ->method('findPrevious') + ->with( + T_OPEN_TAG, + $stackPtr - 1 + ) + ->willReturn(false); + $this->fileMock->expects($this->once()) + ->method('getTokens') + ->willReturn($tokens); + $this->fileMock->numTokens = count($tokens); + $this->fileMock->expects($this->exactly($numIncorrectUsages)) + ->method('addError') + ->with( + 'Constants are not allowed as the first argument of translation function, use string literal instead', + $this->anything(), + 'VariableTranslation' + ); + $this->constantUsageSniff->process($this->fileMock, $stackPtr); + } + + /** + * Get tokens for a string + * + * @param string $fileContent + * @return array + */ + private function tokenizeString($fileContent) + { + $lineNumber = 1; + $tokens = token_get_all($fileContent); + $snifferTokens = []; + for ($i = 0; $i < count($tokens); $i++) { + $content = is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i]; + $snifferTokens[$i]['line'] = $lineNumber; + $snifferTokens[$i]['content'] = $content; + $trimmedContent = trim($content, ' '); + if ($trimmedContent == PHP_EOL || $trimmedContent == PHP_EOL . PHP_EOL) { + $lineNumber++; + } + } + return $snifferTokens; + } + + /** + * @return array + */ + public function processDataProvider() + { + return [ + [ + 'incorrect_arguments.txt', + 9 + ], + [ + 'correct_arguments.txt', + 0 + ] + ]; + } +} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/_files/correct_arguments.txt b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/_files/correct_arguments.txt new file mode 100644 index 0000000000000000000000000000000000000000..536029811fd87972da11ca531cd6787fa59921a4 --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/_files/correct_arguments.txt @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +__($variable) + +__($variable[Class::CONSTANT]) + +__($variable[\Namespace\Class::CONSTANT]) + +__($variable['value']) + +__( + $variable +) + +Phrase($variable) + +Phrase($variable[Class::CONSTANT]) + +Phrase($variable[\Namespace\Class::CONSTANT]) + +\Magento\Framework\Phrase($variable['value']) + +\Magento\Framework\Phrase( + $variable[Class::CONSTANT] +) diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/_files/incorrect_arguments.txt b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/_files/incorrect_arguments.txt new file mode 100644 index 0000000000000000000000000000000000000000..6112ce567ce14cc200e4bb648e828fc51d0dbc77 --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Translation/_files/incorrect_arguments.txt @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +__(Class::CONSTANT) + +__(self::CONSTANT) + +__(\Namespace\Class::CONSTANT) + +__( + Class::CONSTANT +) + +Phrase(Class::CONSTANT) + +Phrase(self::CONSTANT) + +Phrase(\Namespace\Class::CONSTANT) + +\Magento\Framework\Phrase(Class::CONSTANT) + +\Magento\Framework\Phrase( + Class::CONSTANT +) diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FileTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FileTest.php new file mode 100644 index 0000000000000000000000000000000000000000..be8b246a9330495fd5b859a4feb2552772492e21 --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FileTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\TestFramework\Utility; + +use Magento\Framework\App\Utility\Files; +use Magento\TestFramework\Utility\File\RegexIteratorFactory; +use Magento\TestFramework\Utility\File; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class FileTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Files|PHPUnit_Framework_MockObject_MockObject + */ + private $fileUtilitiesMock; + + /** + * @var RegexIteratorFactory|PHPUnit_Framework_MockObject_MockObject + */ + private $regexIteratorFactoryMock; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var File + */ + private $file; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->fileUtilitiesMock = $this->getMock(Files::class, [], [], '', false); + $this->regexIteratorFactoryMock = $this->getMock(RegexIteratorFactory::class, [], [], '', false); + $this->file = $this->objectManager->getObject( + File::class, + [ + 'fileUtilities' => $this->fileUtilitiesMock, + 'regexIteratorFactory' => $this->regexIteratorFactoryMock + ] + ); + } + + public function testGetPhpFiles() + { + $appFiles = [ + 'file1', + 'file2' + ]; + $setupFiles = [ + 'file3' + ]; + $expected = [ + 'file1' => ['file1'], + 'file2' => ['file2'], + 'file3' => ['file3'] + ]; + $iteratorMock = $this->getMock(\IteratorAggregate::class, [], [], '', false); + $iteratorMock->expects($this->any()) + ->method('getIterator') + ->willReturn(new \ArrayIterator($setupFiles)); + $this->regexIteratorFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($iteratorMock); + $this->fileUtilitiesMock->expects($this->once()) + ->method('getPhpFiles') + ->with( + Files::INCLUDE_APP_CODE + | Files::INCLUDE_PUB_CODE + | Files::INCLUDE_LIBS + | Files::INCLUDE_TEMPLATES + | Files::INCLUDE_TESTS + | Files::INCLUDE_NON_CLASSES + ) + ->willReturn($appFiles); + $this->assertEquals($expected, $this->file->getPhpFiles()); + } +} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FunctionDetectorTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FunctionDetectorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a356aaa3cc83b48e4bf310b31d2556ec4c63bc12 --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FunctionDetectorTest.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\TestFramework\Utility; + +class FunctionDetectorTest extends \PHPUnit_Framework_TestCase +{ + public function testDetectFunctions() + { + $fixturePath = __DIR__ . '/_files/test.txt'; + $expectedResults = [ + 1 => ['strtoupper', 'strtolower'], + 3 => ['foo'], + 4 => ['foo'], + ]; + $functionDetector = new FunctionDetector(); + $lines = $functionDetector->detect($fixturePath, ['foo', 'strtoupper', 'test', 'strtolower']); + $this->assertEquals($expectedResults, $lines); + } +} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/test.txt b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/test.txt new file mode 100644 index 0000000000000000000000000000000000000000..c2feb8284b2373bc94f9c16f7b5383fbc60f711a --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/test.txt @@ -0,0 +1,9 @@ +strtoupper(strtolower($foo . $bar)) +function foo($merchantMd5, $merchantApiLogin) +$this->generateHash(foo($bar), $foo) +foo( + 'bar' +) +Foo::bar($foo, $this->getData('bar')) +$this->foo('bar') +Foo::foo() diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ApiAnnotationTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ApiAnnotationTest.php deleted file mode 100644 index a1e7c25e3d6008cff744a090c0c01a810efad8a4..0000000000000000000000000000000000000000 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ApiAnnotationTest.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Scan source code for unmarked API interfaces - * - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Test\Integrity; - -use Magento\Framework\App\Utility\Files; -use Magento\Framework\Component\ComponentRegistrar; - -class ApiAnnotationTest extends \PHPUnit_Framework_TestCase -{ - /** - * API annotation pattern - */ - private $apiAnnotation = '~/\*{2}(.*@api.*)\*/\s+(?=interface)~s'; - - public function testApiAnnotations() - { - $modulePaths = array_map(function ($path) { - return $path . DIRECTORY_SEPARATOR . 'Api'; - }, (new ComponentRegistrar())->getPaths(ComponentRegistrar::MODULE)); - - foreach (Files::init()->getFiles($modulePaths, '*.php', true) as $file) { - $fileContent = file_get_contents($file); - if (!preg_match($this->apiAnnotation, $fileContent)) { - $result[] = $file; - } - } - if (!empty($result)) { - $this->fail(sprintf( - 'Found %s file(s) without @api annotations under Api namespace: %s', - count($result), - PHP_EOL . implode(PHP_EOL, $result) - )); - } - } -} diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php index 0c4c6eba49efa33c997b2dc62c70bd49434540e8..68c2d166448ab6fb4c39354b92ddb9f803d500f1 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php @@ -3,14 +3,13 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - -/** - * Tests to find usage of restricted code - */ namespace Magento\Test\Legacy; use Magento\Framework\Component\ComponentRegistrar; +/** + * Tests to find usage of restricted code + */ class RestrictedCodeTest extends \PHPUnit_Framework_TestCase { /**@#+ @@ -18,17 +17,29 @@ class RestrictedCodeTest extends \PHPUnit_Framework_TestCase * * @var array */ - protected static $_classes = []; + private static $_classes = []; /**#@-*/ /** * List of fixtures that contain restricted classes and should not be tested + * * @var array */ - protected static $_fixtureFiles = []; + private static $_fixtureFiles = []; + + /** + * @var ComponentRegistrar + */ + private $componentRegistrar; + + protected function setUp() + { + $this->componentRegistrar = new ComponentRegistrar(); + } /** * Read fixtures into memory as arrays + * * @return void */ public static function setUpBeforeClass() @@ -69,6 +80,7 @@ class RestrictedCodeTest extends \PHPUnit_Framework_TestCase /** * Test that restricted entities are not used in PHP files + * * @return void */ public function testPhpFiles() @@ -97,15 +109,13 @@ class RestrictedCodeTest extends \PHPUnit_Framework_TestCase protected function _testRestrictedClasses($file) { $content = file_get_contents($file); - $componentRegistrar = new ComponentRegistrar(); foreach (self::$_classes as $restrictedClass => $classRules) { foreach ($classRules['exclude'] as $skippedPathInfo) { - $skippedPath = $componentRegistrar->getPath($skippedPathInfo['type'], $skippedPathInfo['name']) - . '/' . $skippedPathInfo['path']; - if (strpos($file, $skippedPath) === 0) { + if (strpos($file, $this->getExcludedFilePath($skippedPathInfo)) === 0) { continue 2; } } + $this->assertFalse( \Magento\TestFramework\Utility\CodeCheck::isClassUsed($restrictedClass, $content), sprintf( @@ -117,4 +127,18 @@ class RestrictedCodeTest extends \PHPUnit_Framework_TestCase ); } } + + /** + * Get full path for excluded file + * + * @param array $pathInfo + * @return string + */ + private function getExcludedFilePath($pathInfo) + { + if ($pathInfo['type'] != 'setup') { + return $this->componentRegistrar->getPath($pathInfo['type'], $pathInfo['name']) . '/' . $pathInfo['path']; + } + return BP . '/setup/' . $pathInfo['path']; + } } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/UnsecureFunctionsUsageTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/UnsecureFunctionsUsageTest.php index e5263713d71d7af374ea1f32c2d765d3639e56ce..44eb7fa78d8ac7099bfbfb6c14eae050c5e578e1 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/UnsecureFunctionsUsageTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/UnsecureFunctionsUsageTest.php @@ -6,12 +6,28 @@ namespace Magento\Test\Legacy; use Magento\Framework\App\Utility\Files; +use Magento\Framework\Component\ComponentRegistrar; +use Magento\TestFramework\Utility\FunctionDetector; /** * Tests to detect unsecure functions usage */ class UnsecureFunctionsUsageTest extends \PHPUnit_Framework_TestCase { + /** + * Php unsecure functions + * + * @var array + */ + private static $phpUnsecureFunctions = []; + + /** + * JS unsecure functions + * + * @var array + */ + private static $jsUnsecureFunctions = []; + /** * File extensions pattern to search for * @@ -20,25 +36,54 @@ class UnsecureFunctionsUsageTest extends \PHPUnit_Framework_TestCase private $fileExtensions = '/\.(php|phtml|js)$/'; /** - * Php unsecure functions to detect + * Read fixtures into memory as arrays * - * @var array + * @return void */ - private $phpUnsecureFunctions = [ - 'unserialize', - 'serialize', - 'eval', - 'md5', - 'srand', - 'mt_srand' - ]; + public static function setUpBeforeClass() + { + self::loadData(self::$phpUnsecureFunctions, 'unsecure_php_functions*.php'); + self::loadData(self::$jsUnsecureFunctions, 'unsecure_js_functions*.php'); + } /** - * JS unsecure functions to detect + * Loads and merges data from fixtures * - * @var array + * @param array $data + * @param string $filePattern + * @return void */ - private $jsUnsecureFunctions = []; + private static function loadData(array &$data, $filePattern) + { + foreach (glob(__DIR__ . '/_files/security/' . $filePattern) as $file) { + $data = array_merge_recursive($data, self::readList($file)); + } + $componentRegistrar = new ComponentRegistrar(); + foreach ($data as $key => $value) { + $excludes = $value['exclude']; + $excludePaths = []; + foreach ($excludes as $exclude) { + if ('setup' == $exclude['type']) { + $excludePaths[] = BP . '/setup/' . $exclude['path']; + } else { + $excludePaths[] = $componentRegistrar->getPath($exclude['type'], $exclude['name']) + . '/' . $exclude['path']; + } + } + $data[$key]['exclude'] = $excludePaths; + } + } + + /** + * Isolate including a file into a method to reduce scope + * + * @param string $file + * @return array + */ + private static function readList($file) + { + return include $file; + } /** * Detect unsecure functions usage for changed files in whitelist with the exception of blacklist @@ -48,46 +93,69 @@ class UnsecureFunctionsUsageTest extends \PHPUnit_Framework_TestCase public function testUnsecureFunctionsUsage() { $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this); + $functionDetector = new FunctionDetector(); $invoker( - function ($fileName) { - $result = ''; - $errorMessage = 'The following functions are non secure and should be avoided: ' - . implode(', ', $this->phpUnsecureFunctions) - . ' for PHP'; - if (!empty($this->jsUnsecureFunctions)) { - $errorMessage .= ', and ' - . implode(', ', $this->jsUnsecureFunctions) - . ' for JavaScript'; - } - $errorMessage .= ".\n"; - $regexp = $this->getRegexpByFileExtension(pathinfo($fileName, PATHINFO_EXTENSION)); - if ($regexp) { - $matches = preg_grep( - $regexp, - file($fileName) - ); - if (!empty($matches)) { - foreach (array_keys($matches) as $line) { - $result .= $fileName . ':' . ($line + 1) . "\n"; - } - } - $this->assertEmpty($result, $errorMessage . $result); + function ($fileFullPath) use ($functionDetector) { + $functions = $this->getFunctions($fileFullPath); + $lines = $functionDetector->detect($fileFullPath, array_keys($functions)); + + $message = ''; + if (!empty($lines)) { + $message = $this->composeMessage($fileFullPath, $lines, $functions); } + $this->assertEmpty( + $lines, + $message + ); }, - $this->unsecureFunctionsUsageDataProvider() + $this->getFilesToVerify() ); } /** - * Data provider for test + * Compose message + * + * @param string $fileFullPath + * @param array $lines + * @param array $functionRules + * @return string + */ + private function composeMessage($fileFullPath, $lines, $functionRules) + { + $result = ''; + foreach ($lines as $lineNumber => $detectedFunctions) { + $detectedFunctionRules = array_intersect_key($functionRules, array_flip($detectedFunctions)); + $replacementString = ''; + foreach ($detectedFunctionRules as $function => $functionRule) { + $replacement = $functionRule['replacement']; + if (is_array($replacement)) { + $replacement = array_unique($replacement); + $replacement = count($replacement) > 1 ? + "[\n\t\t\t" . implode("\n\t\t\t", $replacement) . "\n\t\t]" : + $replacement[0]; + } + $replacement = empty($replacement) ? 'No suggested replacement at this time' : $replacement; + $replacementString .= "\t\t'$function' => '$replacement'\n"; + } + $result .= sprintf( + "Functions '%s' are not secure in %s. \n\tSuggested replacement:\n%s", + implode(', ', $detectedFunctions), + $fileFullPath . ':' . $lineNumber, + $replacementString + ); + } + return $result; + } + + /** + * Get files to be verified * * @return array */ - public function unsecureFunctionsUsageDataProvider() + private function getFilesToVerify() { $fileExtensions = $this->fileExtensions; $directoriesToScan = Files::init()->readLists(__DIR__ . '/_files/security/whitelist.txt'); - $blackListFiles = include __DIR__ . '/_files/security/blacklist.php'; $filesToVerify = []; foreach (glob(__DIR__ . '/../_files/changed_files*') as $listFile) { @@ -104,7 +172,7 @@ class UnsecureFunctionsUsageTest extends \PHPUnit_Framework_TestCase ); $filesToVerify = array_filter( $filesToVerify, - function ($path) use ($directoriesToScan, $fileExtensions, $blackListFiles) { + function ($path) use ($directoriesToScan, $fileExtensions) { if (!file_exists($path[0])) { return false; } @@ -113,11 +181,9 @@ class UnsecureFunctionsUsageTest extends \PHPUnit_Framework_TestCase $directory = realpath($directory); if (strpos($path, $directory) === 0) { if (preg_match($fileExtensions, $path)) { - foreach ($blackListFiles as $blackListFile) { - $blackListFile = preg_quote($blackListFile, '#'); - if (preg_match('#' . $blackListFile . '#', $path)) { - return false; - } + // skip unit tests + if (preg_match('#' . preg_quote('Test/Unit', '#') . '#', $path)) { + return false; } return true; } @@ -130,35 +196,27 @@ class UnsecureFunctionsUsageTest extends \PHPUnit_Framework_TestCase } /** - * Get regular expression by file extension + * Get functions for the given file * - * @param string $fileExtension - * @return string|bool + * @param string $fileFullPath + * @return array */ - private function getRegexpByFileExtension($fileExtension) + private function getFunctions($fileFullPath) { - $regexp = false; + $fileExtension = pathinfo($fileFullPath, PATHINFO_EXTENSION); + $functions = []; if ($fileExtension == 'php') { - $regexp = $this->prepareRegexp($this->phpUnsecureFunctions); + $functions = self::$phpUnsecureFunctions; } elseif ($fileExtension == 'js') { - $regexp = $this->prepareRegexp($this->jsUnsecureFunctions); + $functions = self::$jsUnsecureFunctions; } elseif ($fileExtension == 'phtml') { - $regexp = $this->prepareRegexp($this->phpUnsecureFunctions + $this->jsUnsecureFunctions); + $functions = array_merge_recursive(self::$phpUnsecureFunctions, self::$jsUnsecureFunctions); } - return $regexp; - } - - /** - * Prepare regular expression for unsecure function names - * - * @param array $functions - * @return string - */ - private function prepareRegexp(array $functions) - { - if (empty($functions)) { - return ''; + foreach ($functions as $function => $functionRules) { + if (in_array($fileFullPath, $functionRules['exclude'])) { + unset($functions[$function]); + } } - return '/(?<!function |[^\s])\b(' . join('|', $functions) . ')\s*\(/i'; + return $functions; } } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php index a950b2d7e9ed243356da176f8eef1095ef4fb210..683449d4e5e34b919ef476d93c26fe01d122b0a8 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/restricted_classes.php @@ -1,27 +1,103 @@ <?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + /** * Classes that are restricted to use directly. * A <replacement> will be suggested to be used instead. * Use <whitelist> to specify files and directories that are allowed to use restricted classes. * * Format: array(<class_name>, <replacement>[, array(<whitelist>)]]) - * - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. */ return [ 'Zend_Db_Select' => [ 'replacement' => '\Magento\Framework\DB\Select', 'exclude' => [ - ['type' => 'library', 'name' => 'magento/framework', 'path' => 'DB/Select.php'], - ['type' => 'library', 'name' => 'magento/framework', 'path' => 'DB/Adapter/Pdo/Mysql.php'], - ['type' => 'library', 'name' => 'magento/framework', 'path' => 'Model/ResourceModel/Iterator.php'], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'DB/Select.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'DB/Adapter/Pdo/Mysql.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'Model/ResourceModel/Iterator.php' + ], ] ], 'Zend_Db_Adapter_Pdo_Mysql' => [ 'replacement' => '\Magento\Framework\DB\Adapter\Pdo\Mysql', 'exclude' => [ - ['type' => 'library', 'name' => 'magento/framework', 'path' => 'DB/Adapter/Pdo/Mysql.php'], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'DB/Adapter/Pdo/Mysql.php' + ], ] ], + 'Magento\Framework\Serialize\Serializer\Serialize' => [ + 'replacement' => 'Magento\Framework\Serialize\SerializerInterface', + 'exclude' => [ + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'DB/Adapter/Pdo/Mysql.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'App/ObjectManager/ConfigLoader/Compiled.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'App/Config/ScopePool.php'], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'App/ObjectManager/ConfigCache.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'App/ObjectManager/ConfigLoader.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'ObjectManager/Config/Compiled.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'Interception/Config/Config.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'Interception/PluginList/PluginList.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'App/Router/ActionList.php' + ], + [ + 'type' => 'library', + 'name' => 'magento/framework', + 'path' => 'Serialize/Test/Unit/Serializer/SerializeTest.php' + ], + [ + 'type' => 'setup', + 'path' => 'src/Magento/Setup/Module/Di/Compiler/Config/Writer/Filesystem.php' + ], + ] + ] ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php deleted file mode 100644 index 42b8e68e784111b96d43049ae27dc5ebc963ad40..0000000000000000000000000000000000000000 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -return [ - 'Test/Unit', - 'lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php', - 'lib/internal/Magento/Framework/Serialize/Serializer/Serialize.php', -]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/unsecure_php_functions.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/unsecure_php_functions.php new file mode 100644 index 0000000000000000000000000000000000000000..eb51d1bbba3bd98ec7ef2d5fd26754aea8fc221a --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/unsecure_php_functions.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * Functions that are not secure to use. + * A <replacement> will be suggested to be used instead. + * Use <exclude> to specify files and directories that are allowed to use function. + * + * Format: [ + * <class_name> => [ + * 'replacement' => <replacement>, + * 'exclude' => [ + * <exclude>, + * <exclude>, + * ] + * ] + */ +return [ + 'unserialize' => [ + 'replacement' => '\Magento\Framework\Serialize\SerializerInterface::unserialize', + 'exclude' => [ + ['type' => 'library', 'name' => 'magento/framework', 'path' => 'DB/Adapter/Pdo/Mysql.php'], + ['type' => 'library', 'name' => 'magento/framework', 'path' => 'Serialize/Serializer/Serialize.php'], + ] + ], + 'serialize' => [ + 'replacement' => '\Magento\Framework\Serialize\SerializerInterface::serialize', + 'exclude' => [ + ['type' => 'library', 'name' => 'magento/framework', 'path' => 'DB/Adapter/Pdo/Mysql.php'], + ['type' => 'library', 'name' => 'magento/framework', 'path' => 'Serialize/Serializer/Serialize.php'], + ] + ], + 'eval' => [ + 'replacement' => '', + 'exclude' => [] + ], + 'md5' => [ + 'replacement' => '', + 'exclude' => [] + ], + 'srand' => [ + 'replacement' => '', + 'exclude' => [] + ], + 'mt_srand' => [ + 'replacement' => '', + 'exclude' => [] + ], +]; diff --git a/lib/internal/Magento/Framework/Api/Search/SearchResult.php b/lib/internal/Magento/Framework/Api/Search/SearchResult.php index f637ef27d7b2cb4889e509aac154997ff1454aa9..f4ece5c84dde049468b76b357ecb09e2e86c2230 100644 --- a/lib/internal/Magento/Framework/Api/Search/SearchResult.php +++ b/lib/internal/Magento/Framework/Api/Search/SearchResult.php @@ -85,4 +85,18 @@ class SearchResult extends AbstractSimpleObject implements SearchResultInterface { return $this->setData(self::TOTAL_COUNT, $totalCount); } + + /** + * Retrieve ids of all items + * + * @return array + */ + public function getAllIds() + { + $ids = []; + foreach ($this->getItems() as $item) { + $ids[] = $item->getId(); + } + return $ids; + } } diff --git a/lib/internal/Magento/Framework/Api/Search/SearchResultInterface.php b/lib/internal/Magento/Framework/Api/Search/SearchResultInterface.php index ffa4c0fe43cf2422efbefc5a2c1605ec78281711..d44a8be3fb0ecff9a5031d07d66a50785c31c9d0 100644 --- a/lib/internal/Magento/Framework/Api/Search/SearchResultInterface.php +++ b/lib/internal/Magento/Framework/Api/Search/SearchResultInterface.php @@ -7,6 +7,11 @@ namespace Magento\Framework\Api\Search; use Magento\Framework\Api\SearchResultsInterface; +/** + * Interface SearchResultInterface + * + * @api + */ interface SearchResultInterface extends SearchResultsInterface { /**#@+ @@ -48,4 +53,11 @@ interface SearchResultInterface extends SearchResultsInterface * @return \Magento\Framework\Api\Search\SearchCriteriaInterface */ public function getSearchCriteria(); + + /** + * Retrieve all ids from list + * + * @return int[] + */ + public function getAllIds(); } diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Search/SearchResultTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Search/SearchResultTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5d289291996906e4b60b0940da8fafd893a720fc --- /dev/null +++ b/lib/internal/Magento/Framework/Api/Test/Unit/Search/SearchResultTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\Api\Test\Unit\Search; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Api\Search\SearchResult; +use Magento\Framework\Api\Search\DocumentInterface; + +class SearchResultTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var SearchResult + */ + private $search; + + /** + * @var DocumentInterface[] + */ + private $items; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Set up + */ + protected function setUp() + { + $document1 = $this->getMock(DocumentInterface::class); + $document2 = $this->getMock(DocumentInterface::class); + + $this->items = [ $document1, $document2]; + $document1->expects($this->any()) + ->method('getId') + ->willReturn(1); + $document2->expects($this->any()) + ->method('getId') + ->willReturn(2); + + $data = [ + 'items' => $this->items + ]; + $this->objectManager = new ObjectManager($this); + $this->search = $this->objectManager->getObject( + SearchResult::class, + [ + 'data' => $data + ] + ); + } + + /** + * Test getAllIds + */ + public function testGetAllIds() + { + $this->assertEquals([1, 2], $this->search->getAllIds()); + } + + /** + * Test getItems + */ + public function testGetItems() + { + $this->assertEquals($this->items, $this->search->getItems()); + } +} diff --git a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php index 181783c7b2115d8a216c681077e116e98344d15c..35d7b3ac5ee20ae669ccc66775498b3c0d9348b4 100644 --- a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php @@ -28,6 +28,7 @@ class Curl implements \Zend_Http_Client_Adapter_Interface ), 'verifypeer' => true, 'verifyhost' => 2, + 'sslversion' => 6 ]; /** @@ -53,6 +54,7 @@ class Curl implements \Zend_Http_Client_Adapter_Interface 'protocols' => CURLOPT_PROTOCOLS, 'verifypeer' => CURLOPT_SSL_VERIFYPEER, 'verifyhost' => CURLOPT_SSL_VERIFYHOST, + 'sslversion' => CURLOPT_SSLVERSION, ]; /** diff --git a/lib/internal/Magento/Framework/HTTP/Client/Curl.php b/lib/internal/Magento/Framework/HTTP/Client/Curl.php index 67525f62f6336cf7de844e866a153be223dc0b5c..5ebc92abf70d9063af63683f5f530dd01470865a 100644 --- a/lib/internal/Magento/Framework/HTTP/Client/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Client/Curl.php @@ -9,14 +9,17 @@ namespace Magento\Framework\HTTP\Client; * Class to work with HTTP protocol using curl library * * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Curl implements \Magento\Framework\HTTP\ClientInterface { + const SSL_VERSION = 6; + /** * Max supported protocol by curl CURL_SSLVERSION_TLSv1_2 * @var int */ - private static $sslVersion = 6; + private $sslVersion; /** * Hostname @@ -86,7 +89,7 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface /** * Curl - * @var object + * @var resource */ protected $_ch; @@ -117,10 +120,11 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface } /** - * Constructor + * @param int|null $sslVersion */ - public function __construct() + public function __construct($sslVersion = self::SSL_VERSION) { + $this->sslVersion = $sslVersion; } /** @@ -377,10 +381,9 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface $this->curlOption(CURLOPT_PORT, $this->_port); } - //$this->curlOption(CURLOPT_HEADER, 1); $this->curlOption(CURLOPT_RETURNTRANSFER, 1); $this->curlOption(CURLOPT_HEADERFUNCTION, [$this, 'parseHeaders']); - $this->curlOption(CURLOPT_SSLVERSION, self::$sslVersion); + $this->curlOption(CURLOPT_SSLVERSION, $this->sslVersion); if (count($this->_curlUserOptions)) { foreach ($this->_curlUserOptions as $k => $v) { @@ -415,6 +418,7 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface * @param resource $ch curl handle, not needed * @param string $data * @return int + * @throws \Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function parseHeaders($ch, $data) @@ -422,11 +426,10 @@ class Curl implements \Magento\Framework\HTTP\ClientInterface if ($this->_headerCount == 0) { $line = explode(" ", trim($data), 3); if (count($line) != 3) { - return $this->doError("Invalid response line returned from server: " . $data); + $this->doError("Invalid response line returned from server: " . $data); } $this->_responseStatus = intval($line[1]); } else { - //var_dump($data); $name = $value = ''; $out = explode(": ", trim($data), 2); if (count($out) == 2) { diff --git a/lib/internal/Magento/Framework/Indexer/Config/Converter.php b/lib/internal/Magento/Framework/Indexer/Config/Converter.php index b8b17b185a1f2630842d6fe6e01b6a9de2a52107..0112b4d9a4de301a312e3e66bd146d8f477cd349 100644 --- a/lib/internal/Magento/Framework/Indexer/Config/Converter.php +++ b/lib/internal/Magento/Framework/Indexer/Config/Converter.php @@ -72,10 +72,10 @@ class Converter implements ConverterInterface $data['fieldsets'] = isset($data['fieldsets']) ? $data['fieldsets'] : []; switch ($childNode->nodeName) { case 'title': - $data['title'] = $this->getTranslatedNodeValue($childNode); + $data['title'] = $childNode->nodeValue; break; case 'description': - $data['description'] = $this->getTranslatedNodeValue($childNode); + $data['description'] = $childNode->nodeValue; break; case 'saveHandler': $data['saveHandler'] = $this->getAttributeValue($childNode, 'class'); @@ -207,6 +207,7 @@ class Converter implements ConverterInterface * * @param \DOMNode $node * @return string + * @deprecated */ protected function getTranslatedNodeValue(\DOMNode $node) {