diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php
index f67c9c57ee034..187fd27fa0554 100644
--- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php
+++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php
@@ -210,7 +210,7 @@ public function getAssociatedProducts($product)
$collection = $this->getAssociatedProductCollection(
$product
)->addAttributeToSelect(
- ['name', 'price', 'special_price', 'special_from_date', 'special_to_date', 'tax_class_id']
+ ['name', 'price', 'special_price', 'special_from_date', 'special_to_date', 'tax_class_id', 'image']
)->addFilterByRequiredOptions()->setPositionOrder()->addStoreFilter(
$this->getStoreFilter($product)
)->addAttributeToFilter(
@@ -475,10 +475,12 @@ public function hasWeight()
* @param \Magento\Catalog\Model\Product $product
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ * phpcs:disable Magento2.CodeAnalysis.EmptyBlock
*/
public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
{
}
+ //phpcs:enable
/**
* @inheritdoc
@@ -488,6 +490,7 @@ public function beforeSave($product)
//clear cached associated links
$product->unsetData($this->_keyAssociatedProducts);
if ($product->hasData('product_options') && !empty($product->getData('product_options'))) {
+ //phpcs:ignore Magento2.Exceptions.DirectThrow
throw new \Exception('Custom options for grouped product type are not supported');
}
return parent::beforeSave($product);
diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GroupedTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GroupedTest.php
index 327b47d4a75d8..ad4b86351a66c 100644
--- a/app/code/Magento/GroupedProduct/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GroupedTest.php
+++ b/app/code/Magento/GroupedProduct/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GroupedTest.php
@@ -21,6 +21,9 @@
use Magento\GroupedProduct\Model\Product\Type\Grouped as GroupedProductType;
use Magento\GroupedProduct\Ui\DataProvider\Product\Form\Modifier\Grouped;
use Magento\Store\Api\Data\StoreInterface;
+use Magento\Catalog\Model\Product;
+use Magento\GroupedProduct\Model\Product\Link\CollectionProvider\Grouped as GroupedProducts;
+use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory;
/**
* Class GroupedTest
@@ -82,23 +85,38 @@ class GroupedTest extends AbstractModifierTest
*/
protected $storeMock;
+ /**
+ * @var GroupedProducts|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $groupedProductsMock;
+
+ /**
+ * @var ProductLinkInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $productLinkFactoryMock;
+
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
$this->objectManager = new ObjectManager($this);
$this->locatorMock = $this->getMockBuilder(LocatorInterface::class)
->getMockForAbstractClass();
- $this->productMock = $this->getMockBuilder(ProductInterface::class)
+ $this->productMock = $this->getMockBuilder(Product::class)
->setMethods(['getId', 'getTypeId'])
- ->getMockForAbstractClass();
+ ->disableOriginalConstructor()
+ ->getMock();
$this->productMock->expects($this->any())
->method('getId')
->willReturn(self::PRODUCT_ID);
$this->productMock->expects($this->any())
->method('getTypeId')
->willReturn(GroupedProductType::TYPE_CODE);
- $this->linkedProductMock = $this->getMockBuilder(ProductInterface::class)
+ $this->linkedProductMock = $this->getMockBuilder(Product::class)
->setMethods(['getId', 'getName', 'getPrice'])
- ->getMockForAbstractClass();
+ ->disableOriginalConstructor()
+ ->getMock();
$this->linkedProductMock->expects($this->any())
->method('getId')
->willReturn(self::LINKED_PRODUCT_ID);
@@ -135,7 +153,7 @@ protected function setUp()
$this->linkRepositoryMock->expects($this->any())
->method('getList')
->with($this->productMock)
- ->willReturn([$this->linkMock]);
+ ->willReturn([$this->linkedProductMock]);
$this->productRepositoryMock = $this->getMockBuilder(ProductRepositoryInterface::class)
->setMethods(['get'])
->getMockForAbstractClass();
@@ -155,7 +173,7 @@ protected function setUp()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
protected function createModel()
{
@@ -169,6 +187,16 @@ protected function createModel()
->setMethods(['init', 'getUrl'])
->disableOriginalConstructor()
->getMock();
+
+ $this->groupedProductsMock = $this->getMockBuilder(GroupedProducts::class)
+ ->setMethods(['getLinkedProducts'])
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->productLinkFactoryMock = $this->getMockBuilder(ProductLinkInterfaceFactory::class)
+ ->setMethods(['create'])
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
$this->imageHelperMock->expects($this->any())
->method('init')
->willReturn($this->imageHelperMock);
@@ -189,16 +217,23 @@ protected function createModel()
'localeCurrency' => $this->currencyMock,
'imageHelper' => $this->imageHelperMock,
'attributeSetRepository' => $this->attributeSetRepositoryMock,
+ 'groupedProducts' => $this->groupedProductsMock,
+ 'productLinkFactory' => $this->productLinkFactoryMock,
]);
}
+ /**
+ * Assert array has key
+ *
+ * @return void
+ */
public function testModifyMeta()
{
$this->assertArrayHasKey(Grouped::GROUP_GROUPED, $this->getModel()->modifyMeta([]));
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function testModifyData()
{
@@ -226,6 +261,42 @@ public function testModifyData()
],
],
];
- $this->assertSame($expectedData, $this->getModel()->modifyData([]));
+ $model = $this->getModel();
+ $linkedProductMock = $this->getMockBuilder(Product::class)
+ ->setMethods(['getId', 'getName', 'getPrice', 'getSku', 'getImage', 'getPosition', 'getQty'])
+ ->disableOriginalConstructor()
+ ->getMock();
+ $linkedProductMock->expects($this->once())
+ ->method('getId')
+ ->willReturn(self::LINKED_PRODUCT_ID);
+ $linkedProductMock->expects($this->once())
+ ->method('getName')
+ ->willReturn(self::LINKED_PRODUCT_NAME);
+ $linkedProductMock->expects($this->once())
+ ->method('getPrice')
+ ->willReturn(self::LINKED_PRODUCT_PRICE);
+ $linkedProductMock->expects($this->once())
+ ->method('getSku')
+ ->willReturn(self::LINKED_PRODUCT_SKU);
+ $linkedProductMock->expects($this->once())
+ ->method('getImage')
+ ->willReturn('');
+ $linkedProductMock->expects($this->exactly(2))
+ ->method('getPosition')
+ ->willReturn(self::LINKED_PRODUCT_POSITION);
+ $linkedProductMock->expects($this->once())
+ ->method('getQty')
+ ->willReturn(self::LINKED_PRODUCT_QTY);
+ $this->groupedProductsMock->expects($this->once())
+ ->method('getLinkedProducts')
+ ->willReturn([$linkedProductMock]);
+ $linkMock = $this->getMockBuilder(ProductLinkInterface::class)
+ ->getMockForAbstractClass();
+
+ $this->productLinkFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($linkMock);
+
+ $this->assertSame($expectedData, $model->modifyData([]));
}
}
diff --git a/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php b/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php
index fff84d9221c8a..2ea622c1c2b8f 100644
--- a/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php
+++ b/app/code/Magento/GroupedProduct/Ui/DataProvider/Product/Form/Modifier/Grouped.php
@@ -21,6 +21,9 @@
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Framework\Locale\CurrencyInterface;
+use Magento\GroupedProduct\Model\Product\Link\CollectionProvider\Grouped as GroupedProducts;
+use Magento\Framework\App\ObjectManager;
+use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory;
/**
* Data provider for Grouped products
@@ -99,6 +102,16 @@ class Grouped extends AbstractModifier
*/
private static $codeQty = 'qty';
+ /**
+ * @var GroupedProducts
+ */
+ private $groupedProducts;
+
+ /**
+ * @var ProductLinkInterfaceFactory
+ */
+ private $productLinkFactory;
+
/**
* @param LocatorInterface $locator
* @param UrlInterface $urlBuilder
@@ -109,6 +122,9 @@ class Grouped extends AbstractModifier
* @param AttributeSetRepositoryInterface $attributeSetRepository
* @param CurrencyInterface $localeCurrency
* @param array $uiComponentsConfig
+ * @param GroupedProducts $groupedProducts
+ * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory|null $productLinkFactory
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
LocatorInterface $locator,
@@ -119,7 +135,9 @@ public function __construct(
Status $status,
AttributeSetRepositoryInterface $attributeSetRepository,
CurrencyInterface $localeCurrency,
- array $uiComponentsConfig = []
+ array $uiComponentsConfig = [],
+ GroupedProducts $groupedProducts = null,
+ \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory = null
) {
$this->locator = $locator;
$this->urlBuilder = $urlBuilder;
@@ -130,6 +148,11 @@ public function __construct(
$this->status = $status;
$this->localeCurrency = $localeCurrency;
$this->uiComponentsConfig = array_replace_recursive($this->uiComponentsConfig, $uiComponentsConfig);
+ $this->groupedProducts = $groupedProducts ?: ObjectManager::getInstance()->get(
+ \Magento\GroupedProduct\Model\Product\Link\CollectionProvider\Grouped::class
+ );
+ $this->productLinkFactory = $productLinkFactory ?: ObjectManager::getInstance()
+ ->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class);
}
/**
@@ -143,18 +166,15 @@ public function modifyData(array $data)
if ($modelId) {
$storeId = $this->locator->getStore()->getId();
$data[$product->getId()]['links'][self::LINK_TYPE] = [];
- $linkedItems = $this->productLinkRepository->getList($product);
+ $linkedItems = $this->groupedProducts->getLinkedProducts($product);
usort($linkedItems, function ($a, $b) {
return $a->getPosition() <=> $b->getPosition();
});
+ $productLink = $this->productLinkFactory->create();
foreach ($linkedItems as $index => $linkItem) {
- if ($linkItem->getLinkType() !== self::LINK_TYPE) {
- continue;
- }
/** @var \Magento\Catalog\Api\Data\ProductInterface $linkedProduct */
- $linkedProduct = $this->productRepository->get($linkItem->getLinkedProductSku(), false, $storeId);
$linkItem->setPosition($index);
- $data[$modelId]['links'][self::LINK_TYPE][] = $this->fillData($linkedProduct, $linkItem);
+ $data[$modelId]['links'][self::LINK_TYPE][] = $this->fillData($linkItem, $productLink);
}
$data[$modelId][self::DATA_SOURCE_DEFAULT]['current_store_id'] = $storeId;
}
@@ -167,6 +187,7 @@ public function modifyData(array $data)
* @param ProductInterface $linkedProduct
* @param ProductLinkInterface $linkItem
* @return array
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function fillData(ProductInterface $linkedProduct, ProductLinkInterface $linkItem)
{
@@ -176,12 +197,15 @@ protected function fillData(ProductInterface $linkedProduct, ProductLinkInterfac
return [
'id' => $linkedProduct->getId(),
'name' => $linkedProduct->getName(),
- 'sku' => $linkItem->getLinkedProductSku(),
+ 'sku' => $linkedProduct->getSku(),
'price' => $currency->toCurrency(sprintf("%f", $linkedProduct->getPrice())),
- 'qty' => $linkItem->getExtensionAttributes()->getQty(),
- 'position' => $linkItem->getPosition(),
- 'positionCalculated' => $linkItem->getPosition(),
- 'thumbnail' => $this->imageHelper->init($linkedProduct, 'product_listing_thumbnail')->getUrl(),
+ 'qty' => $linkedProduct->getQty(),
+ 'position' => $linkedProduct->getPosition(),
+ 'positionCalculated' => $linkedProduct->getPosition(),
+ 'thumbnail' => $this->imageHelper
+ ->init($linkedProduct, 'product_listing_thumbnail')
+ ->setImageFile($linkedProduct->getImage())
+ ->getUrl(),
'type_id' => $linkedProduct->getTypeId(),
'status' => $this->status->getOptionText($linkedProduct->getStatus()),
'attribute_set' => $this->attributeSetRepository
diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php
index 2a4d6904b11b5..04f4111d3a0a8 100644
--- a/app/code/Magento/ImportExport/Model/Import.php
+++ b/app/code/Magento/ImportExport/Model/Import.php
@@ -6,13 +6,35 @@
namespace Magento\ImportExport\Model;
+use Magento\Eav\Model\Entity\Attribute;
+use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
+use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Exception\FileSystemException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Filesystem;
use Magento\Framework\HTTP\Adapter\FileTransferFactory;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Framework\Math\Random;
use Magento\Framework\Stdlib\DateTime\DateTime;
+use Magento\ImportExport\Helper\Data as DataHelper;
+use Magento\ImportExport\Model\Export\Adapter\CsvFactory;
+use Magento\ImportExport\Model\Import\AbstractEntity as ImportAbstractEntity;
+use Magento\ImportExport\Model\Import\AbstractSource;
+use Magento\ImportExport\Model\Import\Adapter;
+use Magento\ImportExport\Model\Import\ConfigInterface;
+use Magento\ImportExport\Model\Import\Entity\AbstractEntity;
+use Magento\ImportExport\Model\Import\Entity\Factory;
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError;
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
use Magento\Framework\Message\ManagerInterface;
+use Magento\ImportExport\Model\ResourceModel\Import\Data;
+use Magento\ImportExport\Model\Source\Import\AbstractBehavior;
+use Magento\ImportExport\Model\Source\Import\Behavior\Factory as BehaviorFactory;
+use Magento\MediaStorage\Model\File\Uploader;
+use Magento\MediaStorage\Model\File\UploaderFactory;
+use Psr\Log\LoggerInterface;
/**
* Import model
@@ -20,32 +42,20 @@
* @api
*
* @method string getBehavior() getBehavior()
- * @method \Magento\ImportExport\Model\Import setEntity() setEntity(string $value)
+ * @method self setEntity() setEntity(string $value)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @SuppressWarnings(PHPMD.TooManyFields)
* @since 100.0.2
*/
-class Import extends \Magento\ImportExport\Model\AbstractModel
+class Import extends AbstractModel
{
- /**#@+
- * Import behaviors
- */
const BEHAVIOR_APPEND = 'append';
-
const BEHAVIOR_ADD_UPDATE = 'add_update';
-
const BEHAVIOR_REPLACE = 'replace';
-
const BEHAVIOR_DELETE = 'delete';
-
const BEHAVIOR_CUSTOM = 'custom';
- /**#@-*/
-
- /**#@+
- * Form field names (and IDs)
- */
-
/**
* Import source file.
*/
@@ -91,8 +101,6 @@ class Import extends \Magento\ImportExport\Model\AbstractModel
*/
const FIELDS_ENCLOSURE = 'fields_enclosure';
- /**#@-*/
-
/**
* default delimiter for several values in one cell as default for FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR
*/
@@ -102,27 +110,20 @@ class Import extends \Magento\ImportExport\Model\AbstractModel
* default empty attribute value constant
*/
const DEFAULT_EMPTY_ATTRIBUTE_VALUE_CONSTANT = '__EMPTY__VALUE__';
-
- /**#@+
- * Import constants
- */
const DEFAULT_SIZE = 50;
-
const MAX_IMPORT_CHUNKS = 4;
-
const IMPORT_HISTORY_DIR = 'import_history/';
-
const IMPORT_DIR = 'import/';
- /**#@-*/
-
- /**#@-*/
+ /**
+ * @var AbstractEntity|ImportAbstractEntity
+ */
protected $_entityAdapter;
/**
* Import export data
*
- * @var \Magento\ImportExport\Helper\Data
+ * @var DataHelper
*/
protected $_importExportData = null;
@@ -133,46 +134,47 @@ class Import extends \Magento\ImportExport\Model\AbstractModel
/**
* @var \Magento\ImportExport\Model\Import\ConfigInterface
+ * @var ConfigInterface
*/
protected $_importConfig;
/**
- * @var \Magento\ImportExport\Model\Import\Entity\Factory
+ * @var Factory
*/
protected $_entityFactory;
/**
- * @var \Magento\ImportExport\Model\ResourceModel\Import\Data
+ * @var Data
*/
protected $_importData;
/**
- * @var \Magento\ImportExport\Model\Export\Adapter\CsvFactory
+ * @var CsvFactory
*/
protected $_csvFactory;
/**
- * @var \Magento\Framework\HTTP\Adapter\FileTransferFactory
+ * @var FileTransferFactory
*/
protected $_httpFactory;
/**
- * @var \Magento\MediaStorage\Model\File\UploaderFactory
+ * @var UploaderFactory
*/
protected $_uploaderFactory;
/**
- * @var \Magento\Framework\Indexer\IndexerRegistry
+ * @var IndexerRegistry
*/
protected $indexerRegistry;
/**
- * @var \Magento\ImportExport\Model\Source\Import\Behavior\Factory
+ * @var BehaviorFactory
*/
protected $_behaviorFactory;
/**
- * @var \Magento\Framework\Filesystem
+ * @var Filesystem
*/
protected $_filesystem;
@@ -192,41 +194,48 @@ class Import extends \Magento\ImportExport\Model\AbstractModel
private $messageManager;
/**
- * @param \Psr\Log\LoggerInterface $logger
- * @param \Magento\Framework\Filesystem $filesystem
- * @param \Magento\ImportExport\Helper\Data $importExportData
- * @param \Magento\Framework\App\Config\ScopeConfigInterface $coreConfig
+ * @var Random
+ */
+ private $random;
+
+ /**
+ * @param LoggerInterface $logger
+ * @param Filesystem $filesystem
+ * @param DataHelper $importExportData
+ * @param ScopeConfigInterface $coreConfig
* @param Import\ConfigInterface $importConfig
* @param Import\Entity\Factory $entityFactory
- * @param \Magento\ImportExport\Model\ResourceModel\Import\Data $importData
+ * @param Data $importData
* @param Export\Adapter\CsvFactory $csvFactory
* @param FileTransferFactory $httpFactory
- * @param \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory
+ * @param UploaderFactory $uploaderFactory
* @param Source\Import\Behavior\Factory $behaviorFactory
- * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
+ * @param IndexerRegistry $indexerRegistry
* @param History $importHistoryModel
* @param DateTime $localeDate
* @param array $data
* @param ManagerInterface|null $messageManager
+ * @param Random|null $random
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
- \Psr\Log\LoggerInterface $logger,
- \Magento\Framework\Filesystem $filesystem,
- \Magento\ImportExport\Helper\Data $importExportData,
- \Magento\Framework\App\Config\ScopeConfigInterface $coreConfig,
- \Magento\ImportExport\Model\Import\ConfigInterface $importConfig,
- \Magento\ImportExport\Model\Import\Entity\Factory $entityFactory,
- \Magento\ImportExport\Model\ResourceModel\Import\Data $importData,
- \Magento\ImportExport\Model\Export\Adapter\CsvFactory $csvFactory,
- \Magento\Framework\HTTP\Adapter\FileTransferFactory $httpFactory,
- \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
- \Magento\ImportExport\Model\Source\Import\Behavior\Factory $behaviorFactory,
- \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry,
- \Magento\ImportExport\Model\History $importHistoryModel,
+ LoggerInterface $logger,
+ Filesystem $filesystem,
+ DataHelper $importExportData,
+ ScopeConfigInterface $coreConfig,
+ ConfigInterface $importConfig,
+ Factory $entityFactory,
+ Data $importData,
+ CsvFactory $csvFactory,
+ FileTransferFactory $httpFactory,
+ UploaderFactory $uploaderFactory,
+ BehaviorFactory $behaviorFactory,
+ IndexerRegistry $indexerRegistry,
+ History $importHistoryModel,
DateTime $localeDate,
array $data = [],
- ManagerInterface $messageManager = null
+ ManagerInterface $messageManager = null,
+ Random $random = null
) {
$this->_importExportData = $importExportData;
$this->_coreConfig = $coreConfig;
@@ -241,15 +250,18 @@ public function __construct(
$this->_filesystem = $filesystem;
$this->importHistoryModel = $importHistoryModel;
$this->localeDate = $localeDate;
- $this->messageManager = $messageManager ?: ObjectManager::getInstance()->get(ManagerInterface::class);
+ $this->messageManager = $messageManager ?: ObjectManager::getInstance()
+ ->get(ManagerInterface::class);
+ $this->random = $random ?: ObjectManager::getInstance()
+ ->get(Random::class);
parent::__construct($logger, $filesystem, $data);
}
/**
* Create instance of entity adapter and return it
*
- * @throws \Magento\Framework\Exception\LocalizedException
- * @return \Magento\ImportExport\Model\Import\Entity\AbstractEntity|\Magento\ImportExport\Model\Import\AbstractEntity
+ * @throws LocalizedException
+ * @return AbstractEntity|ImportAbstractEntity
*/
protected function _getEntityAdapter()
{
@@ -260,30 +272,30 @@ protected function _getEntityAdapter()
$this->_entityAdapter = $this->_entityFactory->create($entities[$this->getEntity()]['model']);
} catch (\Exception $e) {
$this->_logger->critical($e);
- throw new \Magento\Framework\Exception\LocalizedException(
+ throw new LocalizedException(
__('Please enter a correct entity model.')
);
}
- if (!$this->_entityAdapter instanceof \Magento\ImportExport\Model\Import\Entity\AbstractEntity &&
- !$this->_entityAdapter instanceof \Magento\ImportExport\Model\Import\AbstractEntity
+ if (!$this->_entityAdapter instanceof AbstractEntity &&
+ !$this->_entityAdapter instanceof ImportAbstractEntity
) {
- throw new \Magento\Framework\Exception\LocalizedException(
+ throw new LocalizedException(
__(
'The entity adapter object must be an instance of %1 or %2.',
- \Magento\ImportExport\Model\Import\Entity\AbstractEntity::class,
- \Magento\ImportExport\Model\Import\AbstractEntity::class
+ AbstractEntity::class,
+ ImportAbstractEntity::class
)
);
}
// check for entity codes integrity
if ($this->getEntity() != $this->_entityAdapter->getEntityTypeCode()) {
- throw new \Magento\Framework\Exception\LocalizedException(
+ throw new LocalizedException(
__('The input entity code is not equal to entity adapter code.')
);
}
} else {
- throw new \Magento\Framework\Exception\LocalizedException(__('Please enter a correct entity.'));
+ throw new LocalizedException(__('Please enter a correct entity.'));
}
$this->_entityAdapter->setParameters($this->getData());
}
@@ -294,12 +306,12 @@ protected function _getEntityAdapter()
* Returns source adapter object.
*
* @param string $sourceFile Full path to source file
- * @return \Magento\ImportExport\Model\Import\AbstractSource
- * @throws \Magento\Framework\Exception\FileSystemException
+ * @return AbstractSource
+ * @throws FileSystemException
*/
protected function _getSourceAdapter($sourceFile)
{
- return \Magento\ImportExport\Model\Import\Adapter::findAdapterFor(
+ return Adapter::findAdapterFor(
$sourceFile,
$this->_filesystem->getDirectoryWrite(DirectoryList::ROOT),
$this->getData(self::FIELD_FIELD_SEPARATOR)
@@ -311,7 +323,7 @@ protected function _getSourceAdapter($sourceFile)
*
* @param ProcessingErrorAggregatorInterface $validationResult
* @return string[]
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getOperationResultMessages(ProcessingErrorAggregatorInterface $validationResult)
{
@@ -354,10 +366,11 @@ public function getOperationResultMessages(ProcessingErrorAggregatorInterface $v
/**
* Get attribute type for upcoming validation.
*
- * @param \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|\Magento\Eav\Model\Entity\Attribute $attribute
+ * @param AbstractAttribute|Attribute $attribute
* @return string
+ * phpcs:disable Magento2.Functions.StaticFunction
*/
- public static function getAttributeType(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute)
+ public static function getAttributeType(AbstractAttribute $attribute)
{
$frontendInput = $attribute->getFrontendInput();
if ($attribute->usesSource() && in_array($frontendInput, ['select', 'multiselect', 'boolean'])) {
@@ -372,7 +385,7 @@ public static function getAttributeType(\Magento\Eav\Model\Entity\Attribute\Abst
/**
* DB data source model getter.
*
- * @return \Magento\ImportExport\Model\ResourceModel\Import\Data
+ * @return Data
*/
public function getDataSourceModel()
{
@@ -393,14 +406,19 @@ public static function getDefaultBehavior()
/**
* Override standard entity getter.
*
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
* @return string
*/
public function getEntity()
{
- if (empty($this->_data['entity'])) {
- throw new \Magento\Framework\Exception\LocalizedException(__('Entity is unknown'));
+ $entities = $this->_importConfig->getEntities();
+
+ if (empty($this->_data['entity'])
+ || !empty($this->_data['entity']) && !isset($entities[$this->_data['entity']])
+ ) {
+ throw new LocalizedException(__('Entity is unknown'));
}
+
return $this->_data['entity'];
}
@@ -408,7 +426,7 @@ public function getEntity()
* Returns number of checked entities.
*
* @return int
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getProcessedEntitiesCount()
{
@@ -419,7 +437,7 @@ public function getProcessedEntitiesCount()
* Returns number of checked rows.
*
* @return int
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getProcessedRowsCount()
{
@@ -440,7 +458,7 @@ public function getWorkingDir()
* Import source file structure to DB.
*
* @return bool
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function importSource()
{
@@ -477,7 +495,7 @@ public function importSource()
* Process import.
*
* @return bool
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
protected function processImport()
{
@@ -488,7 +506,7 @@ protected function processImport()
* Import possibility getter.
*
* @return bool
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function isImportAllowed()
{
@@ -499,7 +517,7 @@ public function isImportAllowed()
* Get error aggregator instance.
*
* @return ProcessingErrorAggregatorInterface
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getErrorAggregator()
{
@@ -509,7 +527,7 @@ public function getErrorAggregator()
/**
* Move uploaded file.
*
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
* @return string Source file path
*/
public function uploadSource()
@@ -523,20 +541,22 @@ public function uploadSource()
} else {
$errorMessage = __('The file was not uploaded.');
}
- throw new \Magento\Framework\Exception\LocalizedException($errorMessage);
+ throw new LocalizedException($errorMessage);
}
$entity = $this->getEntity();
- /** @var $uploader \Magento\MediaStorage\Model\File\Uploader */
+ /** @var $uploader Uploader */
$uploader = $this->_uploaderFactory->create(['fileId' => self::FIELD_NAME_SOURCE_FILE]);
$uploader->skipDbProcessing(true);
- $result = $uploader->save($this->getWorkingDir());
+ $fileName = $this->random->getRandomString(32) . '.' . $uploader->getFileExtension();
+ $result = $uploader->save($this->getWorkingDir(), $fileName);
+ // phpcs:disable Magento2.Functions.DiscouragedFunction.Discouraged
$extension = pathinfo($result['file'], PATHINFO_EXTENSION);
$uploadedFile = $result['path'] . $result['file'];
if (!$extension) {
$this->_varDirectory->delete($uploadedFile);
- throw new \Magento\Framework\Exception\LocalizedException(__('The file you uploaded has no extension.'));
+ throw new LocalizedException(__('The file you uploaded has no extension.'));
}
$sourceFile = $this->getWorkingDir() . $entity;
@@ -553,8 +573,8 @@ public function uploadSource()
$this->_varDirectory->getRelativePath($uploadedFile),
$sourceFileRelative
);
- } catch (\Magento\Framework\Exception\FileSystemException $e) {
- throw new \Magento\Framework\Exception\LocalizedException(__('The source file moving process failed.'));
+ } catch (FileSystemException $e) {
+ throw new LocalizedException(__('The source file moving process failed.'));
}
}
$this->_removeBom($sourceFile);
@@ -566,8 +586,7 @@ public function uploadSource()
* Move uploaded file and provide source instance.
*
* @return Import\AbstractSource
- * @throws \Magento\Framework\Exception\FileSystemException
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function uploadFileAndGetSource()
{
@@ -576,7 +595,7 @@ public function uploadFileAndGetSource()
$source = $this->_getSourceAdapter($sourceFile);
} catch (\Exception $e) {
$this->_varDirectory->delete($this->_varDirectory->getRelativePath($sourceFile));
- throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()));
+ throw new LocalizedException(__($e->getMessage()));
}
return $source;
@@ -587,7 +606,7 @@ public function uploadFileAndGetSource()
*
* @param string $sourceFile
* @return $this
- * @throws \Magento\Framework\Exception\FileSystemException
+ * @throws FileSystemException
*/
protected function _removeBom($sourceFile)
{
@@ -605,11 +624,11 @@ protected function _removeBom($sourceFile)
* Before validate data the method requires to initialize error aggregator (ProcessingErrorAggregatorInterface)
* with 'validation strategy' and 'allowed error count' values to allow using this parameters in validation process.
*
- * @param \Magento\ImportExport\Model\Import\AbstractSource $source
+ * @param AbstractSource $source
* @return bool
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
- public function validateSource(\Magento\ImportExport\Model\Import\AbstractSource $source)
+ public function validateSource(AbstractSource $source)
{
$this->addLogComment(__('Begin data validation'));
@@ -624,7 +643,7 @@ public function validateSource(\Magento\ImportExport\Model\Import\AbstractSource
$adapter->validateData();
} catch (\Exception $e) {
$errorAggregator->addError(
- \Magento\ImportExport\Model\Import\Entity\AbstractEntity::ERROR_CODE_SYSTEM_EXCEPTION,
+ AbstractEntity::ERROR_CODE_SYSTEM_EXCEPTION,
ProcessingError::ERROR_LEVEL_CRITICAL,
null,
null,
@@ -646,7 +665,7 @@ public function validateSource(\Magento\ImportExport\Model\Import\AbstractSource
* Invalidate indexes by process codes.
*
* @return $this
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function invalidateIndex()
{
@@ -662,6 +681,7 @@ public function invalidateIndex()
if (!$indexer->isScheduled()) {
$indexer->invalidate();
}
+ // phpcs:disable Magento2.CodeAnalysis.EmptyBlock.DetectedCatch
} catch (\InvalidArgumentException $e) {
}
}
@@ -680,7 +700,7 @@ public function invalidateIndex()
* )
*
* @return array
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getEntityBehaviors()
{
@@ -689,7 +709,7 @@ public function getEntityBehaviors()
foreach ($entities as $entityCode => $entityData) {
$behaviorClassName = isset($entityData['behaviorModel']) ? $entityData['behaviorModel'] : null;
if ($behaviorClassName && class_exists($behaviorClassName)) {
- /** @var $behavior \Magento\ImportExport\Model\Source\Import\AbstractBehavior */
+ /** @var $behavior AbstractBehavior */
$behavior = $this->_behaviorFactory->create($behaviorClassName);
$behaviourData[$entityCode] = [
'token' => $behaviorClassName,
@@ -697,7 +717,7 @@ public function getEntityBehaviors()
'notes' => $behavior->getNotes($entityCode),
];
} else {
- throw new \Magento\Framework\Exception\LocalizedException(
+ throw new LocalizedException(
__('The behavior token for %1 is invalid.', $entityCode)
);
}
@@ -713,7 +733,7 @@ public function getEntityBehaviors()
* )
*
* @return array
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getUniqueEntityBehaviors()
{
@@ -733,7 +753,7 @@ public function getUniqueEntityBehaviors()
*
* @param string|null $entity
* @return bool
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function isReportEntityType($entity = null)
{
@@ -747,12 +767,12 @@ public function isReportEntityType($entity = null)
try {
$result = $this->_getEntityAdapter()->isNeedToLogInHistory();
} catch (\Exception $e) {
- throw new \Magento\Framework\Exception\LocalizedException(
+ throw new LocalizedException(
__('Please enter a correct entity model')
);
}
} else {
- throw new \Magento\Framework\Exception\LocalizedException(__('Please enter a correct entity model'));
+ throw new LocalizedException(__('Please enter a correct entity model'));
}
} else {
$result = $this->_getEntityAdapter()->isNeedToLogInHistory();
@@ -768,7 +788,7 @@ public function isReportEntityType($entity = null)
* @param string $extension
* @param array $result
* @return $this
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
protected function createHistoryReport($sourceFileRelative, $entity, $extension = null, $result = null)
{
@@ -781,6 +801,7 @@ protected function createHistoryReport($sourceFileRelative, $entity, $extension
} elseif ($extension !== null) {
$fileName = $entity . $extension;
} else {
+ // phpcs:disable Magento2.Functions.DiscouragedFunction.Discouraged
$fileName = basename($sourceFileRelative);
}
$copyName = $this->localeDate->gmtTimestamp() . '_' . $fileName;
@@ -792,8 +813,8 @@ protected function createHistoryReport($sourceFileRelative, $entity, $extension
$content = $this->_varDirectory->getDriver()->fileGetContents($sourceFileRelative);
$this->_varDirectory->writeFile($copyFile, $content);
}
- } catch (\Magento\Framework\Exception\FileSystemException $e) {
- throw new \Magento\Framework\Exception\LocalizedException(__('Source file coping failed'));
+ } catch (FileSystemException $e) {
+ throw new LocalizedException(__('Source file coping failed'));
}
$this->importHistoryModel->addReport($copyName);
}
@@ -804,7 +825,7 @@ protected function createHistoryReport($sourceFileRelative, $entity, $extension
* Get count of created items
*
* @return int
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getCreatedItemsCount()
{
@@ -815,7 +836,7 @@ public function getCreatedItemsCount()
* Get count of updated items
*
* @return int
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getUpdatedItemsCount()
{
@@ -826,7 +847,7 @@ public function getUpdatedItemsCount()
* Get count of deleted items
*
* @return int
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws LocalizedException
*/
public function getDeletedItemsCount()
{
diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/ImportTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/ImportTest.php
index 823ec29d41760..6fdf9b04450c0 100644
--- a/app/code/Magento/ImportExport/Test/Unit/Model/ImportTest.php
+++ b/app/code/Magento/ImportExport/Test/Unit/Model/ImportTest.php
@@ -499,6 +499,10 @@ public function testInvalidateIndex()
$this->_importConfig->expects($this->atLeastOnce())
->method('getRelatedIndexers')
->willReturn($indexers);
+ $this->_importConfig->method('getEntities')
+ ->willReturn([
+ 'test' => []
+ ]);
$this->indexerRegistry->expects($this->any())
->method('get')
->willReturnMap([
@@ -532,6 +536,10 @@ public function testInvalidateIndexWithoutIndexers()
$this->_importConfig->expects($this->once())
->method('getRelatedIndexers')
->willReturn([]);
+ $this->_importConfig->method('getEntities')
+ ->willReturn([
+ 'test' => []
+ ]);
$logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
->disableOriginalConstructor()
@@ -558,12 +566,82 @@ public function testInvalidateIndexWithoutIndexers()
$this->assertSame($import, $import->invalidateIndex());
}
+ public function testGetKnownEntity()
+ {
+ $this->_importConfig->method('getEntities')
+ ->willReturn([
+ 'test' => []
+ ]);
+
+ $logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $import = new Import(
+ $logger,
+ $this->_filesystem,
+ $this->_importExportData,
+ $this->_coreConfig,
+ $this->_importConfig,
+ $this->_entityFactory,
+ $this->_importData,
+ $this->_csvFactory,
+ $this->_httpFactory,
+ $this->_uploaderFactory,
+ $this->_behaviorFactory,
+ $this->indexerRegistry,
+ $this->historyModel,
+ $this->dateTime
+ );
+
+ $import->setEntity('test');
+ $entity = $import->getEntity();
+ self::assertSame('test', $entity);
+ }
+
/**
- * @todo to implement it.
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage Entity is unknown
+ * @dataProvider unknownEntitiesProvider
*/
- public function testGetEntityBehaviors()
+ public function testGetUnknownEntity($entity)
{
- $this->markTestIncomplete('This test has not been implemented yet.');
+ $this->_importConfig->method('getEntities')
+ ->willReturn([
+ 'test' => []
+ ]);
+
+ $logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $import = new Import(
+ $logger,
+ $this->_filesystem,
+ $this->_importExportData,
+ $this->_coreConfig,
+ $this->_importConfig,
+ $this->_entityFactory,
+ $this->_importData,
+ $this->_csvFactory,
+ $this->_httpFactory,
+ $this->_uploaderFactory,
+ $this->_behaviorFactory,
+ $this->indexerRegistry,
+ $this->historyModel,
+ $this->dateTime
+ );
+
+ $import->setEntity($entity);
+ $import->getEntity();
+ }
+
+ public function unknownEntitiesProvider()
+ {
+ return [
+ [''],
+ ['foo'],
+ ];
}
/**
diff --git a/app/code/Magento/Integration/Model/AdminTokenService.php b/app/code/Magento/Integration/Model/AdminTokenService.php
index 5a030325e9fbd..7726ff979c6d7 100644
--- a/app/code/Magento/Integration/Model/AdminTokenService.php
+++ b/app/code/Magento/Integration/Model/AdminTokenService.php
@@ -72,7 +72,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function createAdminAccessToken($username, $password)
{
@@ -110,7 +110,7 @@ public function revokeAdminAccessToken($adminId)
{
$tokenCollection = $this->tokenModelCollectionFactory->create()->addFilterByAdminId($adminId);
if ($tokenCollection->getSize() == 0) {
- throw new LocalizedException(__('This user has no tokens.'));
+ return true;
}
try {
foreach ($tokenCollection as $token) {
diff --git a/app/code/Magento/Integration/Test/Unit/Model/AdminTokenServiceTest.php b/app/code/Magento/Integration/Test/Unit/Model/AdminTokenServiceTest.php
index e0b1113e80d8d..83efe4074e15f 100644
--- a/app/code/Magento/Integration/Test/Unit/Model/AdminTokenServiceTest.php
+++ b/app/code/Magento/Integration/Test/Unit/Model/AdminTokenServiceTest.php
@@ -8,6 +8,9 @@
use Magento\Integration\Model\Oauth\Token;
+/**
+ * Test for Magento\Integration\Model\AdminTokenService class.
+ */
class AdminTokenServiceTest extends \PHPUnit\Framework\TestCase
{
/** \Magento\Integration\Model\AdminTokenService */
@@ -99,10 +102,6 @@ public function testRevokeAdminAccessToken()
$this->assertTrue($this->_tokenService->revokeAdminAccessToken($adminId));
}
- /**
- * @expectedException \Magento\Framework\Exception\LocalizedException
- * @expectedExceptionMessage This user has no tokens.
- */
public function testRevokeAdminAccessTokenWithoutAdminId()
{
$this->_tokenModelCollectionMock->expects($this->once())
diff --git a/app/code/Magento/Msrp/Pricing/MsrpPriceCalculator.php b/app/code/Magento/Msrp/Pricing/MsrpPriceCalculator.php
index 3d1e5ef0b8e6c..af5a29eb288ea 100644
--- a/app/code/Magento/Msrp/Pricing/MsrpPriceCalculator.php
+++ b/app/code/Magento/Msrp/Pricing/MsrpPriceCalculator.php
@@ -23,7 +23,7 @@ class MsrpPriceCalculator implements MsrpPriceCalculatorInterface
/**
* @param array $msrpPriceCalculators
*/
- public function __construct(array $msrpPriceCalculators)
+ public function __construct(array $msrpPriceCalculators = [])
{
$this->msrpPriceCalculators = $this->getMsrpPriceCalculators($msrpPriceCalculators);
}
diff --git a/app/code/Magento/MysqlMq/Model/Driver/Queue.php b/app/code/Magento/MysqlMq/Model/Driver/Queue.php
index b8dab6fac7b24..cbc2e951782f2 100644
--- a/app/code/Magento/MysqlMq/Model/Driver/Queue.php
+++ b/app/code/Magento/MysqlMq/Model/Driver/Queue.php
@@ -73,7 +73,7 @@ public function __construct(
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function dequeue()
{
@@ -92,7 +92,7 @@ public function dequeue()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function acknowledge(EnvelopeInterface $envelope)
{
@@ -103,25 +103,26 @@ public function acknowledge(EnvelopeInterface $envelope)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function subscribe($callback)
{
while (true) {
while ($envelope = $this->dequeue()) {
try {
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
call_user_func($callback, $envelope);
- $this->acknowledge($envelope);
} catch (\Exception $e) {
$this->reject($envelope);
}
}
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
sleep($this->interval);
}
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function reject(EnvelopeInterface $envelope, $requeue = true, $rejectionMessage = null)
{
@@ -139,7 +140,7 @@ public function reject(EnvelopeInterface $envelope, $requeue = true, $rejectionM
}
/**
- * {@inheritDoc}
+ * @inheritDoc
*/
public function push(EnvelopeInterface $envelope)
{
diff --git a/app/code/Magento/NewRelicReporting/Model/Module/Collect.php b/app/code/Magento/NewRelicReporting/Model/Module/Collect.php
index 7e381762f5d27..fe5389e258aa5 100644
--- a/app/code/Magento/NewRelicReporting/Model/Module/Collect.php
+++ b/app/code/Magento/NewRelicReporting/Model/Module/Collect.php
@@ -11,6 +11,9 @@
use Magento\NewRelicReporting\Model\Config;
use Magento\NewRelicReporting\Model\Module;
+/**
+ * Class for collecting data for the report
+ */
class Collect
{
/**
@@ -92,7 +95,6 @@ protected function getAllModules()
* @param string $active
* @param string $setupVersion
* @param string $state
- *
* @return array
*/
protected function getNewModuleChanges($moduleName, $active, $setupVersion, $state)
@@ -277,9 +279,7 @@ public function getModuleData($refresh = true)
$changes = array_diff($module, $changeTest);
$changesCleanArray = $this->getCleanChangesArray($changes);
- if (count($changesCleanArray) > 0 ||
- ($this->moduleManager->isOutputEnabled($changeTest['name']) &&
- $module['setup_version'] != null)) {
+ if (!empty($changesCleanArray)) {
$data = [
'entity_id' => $changeTest['entity_id'],
'name' => $changeTest['name'],
diff --git a/app/code/Magento/NewRelicReporting/Test/Unit/Model/Module/CollectTest.php b/app/code/Magento/NewRelicReporting/Test/Unit/Model/Module/CollectTest.php
index 8d8e6255ab8d3..4286406d6e9ab 100644
--- a/app/code/Magento/NewRelicReporting/Test/Unit/Model/Module/CollectTest.php
+++ b/app/code/Magento/NewRelicReporting/Test/Unit/Model/Module/CollectTest.php
@@ -162,10 +162,6 @@ public function testGetModuleDataWithoutRefresh()
->method('getNames')
->willReturn($enabledModulesMockArray);
- $this->moduleManagerMock->expects($this->any())->method('isOutputEnabled')->will(
- $this->returnValue(false)
- );
-
$this->assertInternalType(
'array',
$this->model->getModuleData()
@@ -256,10 +252,6 @@ public function testGetModuleDataRefresh($data)
->method('getNames')
->willReturn($enabledModulesMockArray);
- $this->moduleManagerMock->expects($this->any())->method('isOutputEnabled')->will(
- $this->returnValue(true)
- );
-
$this->assertInternalType(
'array',
$this->model->getModuleData()
@@ -350,10 +342,6 @@ public function testGetModuleDataRefreshOrStatement($data)
->method('getNames')
->willReturn($enabledModulesMockArray);
- $this->moduleManagerMock->expects($this->any())->method('isOutputEnabled')->will(
- $this->returnValue(true)
- );
-
$this->assertInternalType(
'array',
$this->model->getModuleData()
diff --git a/app/code/Magento/Newsletter/view/frontend/templates/subscribe.phtml b/app/code/Magento/Newsletter/view/frontend/templates/subscribe.phtml
index c1a526ee95148..554437095f36c 100644
--- a/app/code/Magento/Newsletter/view/frontend/templates/subscribe.phtml
+++ b/app/code/Magento/Newsletter/view/frontend/templates/subscribe.phtml
@@ -19,16 +19,24 @@
data-mage-init='{"validation": {"errorClass": "mage-error"}}'
id="newsletter-validate-detail">
-
diff --git a/app/code/Magento/OfflinePayments/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/OfflinePayments/Test/Mftf/Data/ConfigData.xml
new file mode 100644
index 0000000000000..ec8dd46a00d8e
--- /dev/null
+++ b/app/code/Magento/OfflinePayments/Test/Mftf/Data/ConfigData.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+ payment/checkmo/active
+ No
+ 0
+
+
+
+ payment/checkmo/active
+ Yes
+ 1
+
+
+
+ payment/cashondelivery/active
+ No
+ 0
+
+
+ payment/cashondelivery/active
+ Yes
+ 1
+
+
diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl
index c73c4e39170e6..801e6cb475d8a 100644
--- a/app/code/Magento/PageCache/etc/varnish4.vcl
+++ b/app/code/Magento/PageCache/etc/varnish4.vcl
@@ -92,8 +92,8 @@ sub vcl_recv {
}
# Remove all marketing get parameters to minimize the cache objects
- if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") {
- set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
+ if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") {
+ set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
set req.url = regsub(req.url, "[?|&]+$", "");
}
diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl
index ea586858eff75..76c5ffee5f14f 100644
--- a/app/code/Magento/PageCache/etc/varnish5.vcl
+++ b/app/code/Magento/PageCache/etc/varnish5.vcl
@@ -93,8 +93,8 @@ sub vcl_recv {
}
# Remove all marketing get parameters to minimize the cache objects
- if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") {
- set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
+ if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") {
+ set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", "");
set req.url = regsub(req.url, "[?|&]+$", "");
}
diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php
index 06a8a5b680bf4..7143576b71a07 100644
--- a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php
+++ b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php
@@ -3,9 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Paypal\Model\Payflow\Service\Response;
use Magento\Framework\DataObject;
+use Magento\Framework\Intl\DateTimeFactory;
use Magento\Payment\Model\Method\Logger;
use Magento\Paypal\Model\Payflow\Service\Response\Handler\HandlerInterface;
use Magento\Framework\Session\Generic;
@@ -18,6 +21,7 @@
/**
* Class Transaction
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Transaction
{
@@ -51,6 +55,11 @@ class Transaction
*/
private $logger;
+ /**
+ * @var DateTimeFactory
+ */
+ private $dateTimeFactory;
+
/**
* @param Generic $sessionTransparent
* @param CartRepositoryInterface $quoteRepository
@@ -58,6 +67,7 @@ class Transaction
* @param PaymentMethodManagementInterface $paymentManagement
* @param HandlerInterface $errorHandler
* @param Logger $logger
+ * @param DateTimeFactory $dateTimeFactory
*/
public function __construct(
Generic $sessionTransparent,
@@ -65,7 +75,8 @@ public function __construct(
Transparent $transparent,
PaymentMethodManagementInterface $paymentManagement,
HandlerInterface $errorHandler,
- Logger $logger
+ Logger $logger,
+ DateTimeFactory $dateTimeFactory
) {
$this->sessionTransparent = $sessionTransparent;
$this->quoteRepository = $quoteRepository;
@@ -73,6 +84,7 @@ public function __construct(
$this->paymentManagement = $paymentManagement;
$this->errorHandler = $errorHandler;
$this->logger = $logger;
+ $this->dateTimeFactory = $dateTimeFactory;
}
/**
@@ -114,8 +126,45 @@ public function savePaymentInQuote($response)
$payment->setData(OrderPaymentInterface::CC_TYPE, $response->getData(OrderPaymentInterface::CC_TYPE));
$payment->setAdditionalInformation(Payflowpro::PNREF, $response->getData(Payflowpro::PNREF));
+ $expDate = $response->getData('expdate');
+ $expMonth = $this->getCcExpMonth($expDate);
+ $payment->setCcExpMonth($expMonth);
+ $expYear = $this->getCcExpYear($expDate);
+ $payment->setCcExpYear($expYear);
+
$this->errorHandler->handle($payment, $response);
$this->paymentManagement->set($quote->getId(), $payment);
}
+
+ /**
+ * Extracts expiration month from PayPal response expiration date.
+ *
+ * @param string $expDate format {MMYY}
+ * @return int
+ */
+ private function getCcExpMonth(string $expDate): int
+ {
+ return (int)substr($expDate, 0, 2);
+ }
+
+ /**
+ * Extracts expiration year from PayPal response expiration date.
+ *
+ * @param string $expDate format {MMYY}
+ * @return int
+ */
+ private function getCcExpYear(string $expDate): int
+ {
+ $last2YearDigits = (int)substr($expDate, 2, 2);
+ $currentDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC'));
+ $first2YearDigits = (int)substr($currentDate->format('Y'), 0, 2);
+
+ // case when credit card expires at next century
+ if ((int)$currentDate->format('y') > $last2YearDigits) {
+ $first2YearDigits++;
+ }
+
+ return 100 * $first2YearDigits + $last2YearDigits;
+ }
}
diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml
new file mode 100644
index 0000000000000..dc6f87bef0ba8
--- /dev/null
+++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Quote/Model/Quote/Item/Compare.php b/app/code/Magento/Quote/Model/Quote/Item/Compare.php
index ddaa636ef32b3..76ba324518dc1 100644
--- a/app/code/Magento/Quote/Model/Quote/Item/Compare.php
+++ b/app/code/Magento/Quote/Model/Quote/Item/Compare.php
@@ -50,7 +50,7 @@ protected function getOptionValues($value)
if (is_string($value) && $this->jsonValidator->isValid($value)) {
$value = $this->serializer->unserialize($value);
if (is_array($value)) {
- unset($value['qty'], $value['uenc']);
+ unset($value['qty'], $value['uenc'], $value['related_product'], $value['item']);
$value = array_filter($value, function ($optionValue) {
return !empty($optionValue);
});
diff --git a/app/code/Magento/Quote/Model/QuoteIdMask.php b/app/code/Magento/Quote/Model/QuoteIdMask.php
index 7950ab47c3665..47b02db7650df 100644
--- a/app/code/Magento/Quote/Model/QuoteIdMask.php
+++ b/app/code/Magento/Quote/Model/QuoteIdMask.php
@@ -53,11 +53,14 @@ protected function _construct()
* Initialize quote identifier before save
*
* @return $this
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
parent::beforeSave();
- $this->setMaskedId($this->randomDataGenerator->getUniqueHash());
+ if (empty($this->getMaskedId())) {
+ $this->setMaskedId($this->randomDataGenerator->getUniqueHash());
+ }
return $this;
}
}
diff --git a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php
index 76c9a49b290c5..77dfec9603a5c 100644
--- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php
+++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php
@@ -7,6 +7,11 @@
use Magento\Framework\Event\ObserverInterface;
+/**
+ * Handle customer VAT number on collect_totals_before event of quote address.
+ *
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
+ */
class CollectTotalsObserver implements ObserverInterface
{
/**
@@ -124,7 +129,7 @@ public function execute(\Magento\Framework\Event\Observer $observer)
);
}
- if ($groupId) {
+ if ($groupId !== null) {
$address->setPrevQuoteCustomerGroupId($quote->getCustomerGroupId());
$quote->setCustomerGroupId($groupId);
$this->customerSession->setCustomerGroupId($groupId);
diff --git a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
index f3357f8aacd31..4ea067c9be8f6 100644
--- a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
+++ b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
@@ -199,7 +199,7 @@ public function testDispatchWithCustomerCountryNotInEUAndNotLoggedCustomerInGrou
->method('getNotLoggedInGroup')
->will($this->returnValue($this->groupInterfaceMock));
$this->groupInterfaceMock->expects($this->once())
- ->method('getId')->will($this->returnValue(0));
+ ->method('getId')->will($this->returnValue(null));
$this->vatValidatorMock->expects($this->once())
->method('isEnabled')
->with($this->quoteAddressMock, $this->storeId)
@@ -220,9 +220,6 @@ public function testDispatchWithCustomerCountryNotInEUAndNotLoggedCustomerInGrou
$this->returnValue(false)
);
- $groupMock = $this->getMockBuilder(\Magento\Customer\Api\Data\GroupInterface::class)
- ->disableOriginalConstructor()
- ->getMock();
$this->customerMock->expects($this->once())->method('getId')->will($this->returnValue(null));
/** Assertions */
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php
new file mode 100644
index 0000000000000..481bad090dac1
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForCustomer.php
@@ -0,0 +1,70 @@
+cartManagement = $cartManagement;
+ $this->quoteIdMaskFactory = $quoteIdMaskFactory;
+ $this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel;
+ }
+
+ /**
+ * Create empty cart for customer
+ *
+ * @param int $customerId
+ * @param string|null $predefinedMaskedQuoteId
+ * @return string
+ */
+ public function execute(int $customerId, string $predefinedMaskedQuoteId = null): string
+ {
+ $quoteId = $this->cartManagement->createEmptyCartForCustomer($customerId);
+
+ $quoteIdMask = $this->quoteIdMaskFactory->create();
+ $quoteIdMask->setQuoteId($quoteId);
+
+ if (isset($predefinedMaskedQuoteId)) {
+ $quoteIdMask->setMaskedId($predefinedMaskedQuoteId);
+ }
+
+ $this->quoteIdMaskResourceModel->save($quoteIdMask);
+ return $quoteIdMask->getMaskedId();
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForGuest.php b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForGuest.php
new file mode 100644
index 0000000000000..a6396ed6352ab
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Cart/CreateEmptyCartForGuest.php
@@ -0,0 +1,68 @@
+guestCartManagement = $guestCartManagement;
+ $this->quoteIdMaskFactory = $quoteIdMaskFactory;
+ $this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel;
+ }
+
+ /**
+ * Create empty cart for guest
+ *
+ * @param string|null $predefinedMaskedQuoteId
+ * @return string
+ */
+ public function execute(string $predefinedMaskedQuoteId = null): string
+ {
+ $maskedQuoteId = $this->guestCartManagement->createEmptyCart();
+
+ if (isset($predefinedMaskedQuoteId)) {
+ $quoteIdMask = $this->quoteIdMaskFactory->create();
+ $this->quoteIdMaskResourceModel->load($quoteIdMask, $maskedQuoteId, 'masked_id');
+
+ $quoteIdMask->setMaskedId($predefinedMaskedQuoteId);
+ $this->quoteIdMaskResourceModel->save($quoteIdMask);
+ }
+ return $predefinedMaskedQuoteId ?? $maskedQuoteId;
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartEmail.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartEmail.php
new file mode 100644
index 0000000000000..8d0cb114d8315
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartEmail.php
@@ -0,0 +1,49 @@
+getCartForUser = $getCartForUser;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ if (!isset($value['model'])) {
+ throw new LocalizedException(__('"model" value should be specified'));
+ }
+ /** @var Quote $cart */
+ $cart = $value['model'];
+
+ return $cart->getCustomerEmail();
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPrices.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPrices.php
new file mode 100644
index 0000000000000..7a9bdd926764c
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPrices.php
@@ -0,0 +1,87 @@
+totalsCollector = $totalsCollector;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ if (!isset($value['model'])) {
+ throw new LocalizedException(__('"model" value should be specified'));
+ }
+
+ /** @var Quote $quote */
+ $quote = $value['model'];
+ $cartTotals = $this->totalsCollector->collectQuoteTotals($quote);
+ $currency = $quote->getQuoteCurrencyCode();
+
+ return [
+ 'grand_total' => ['value' => $cartTotals->getGrandTotal(), 'currency' => $currency],
+ 'subtotal_including_tax' => ['value' => $cartTotals->getSubtotalInclTax(), 'currency' => $currency],
+ 'subtotal_excluding_tax' => ['value' => $cartTotals->getSubtotal(), 'currency' => $currency],
+ 'subtotal_with_discount_excluding_tax' => [
+ 'value' => $cartTotals->getSubtotalWithDiscount(), 'currency' => $currency
+ ],
+ 'applied_taxes' => $this->getAppliedTaxes($cartTotals, $currency),
+ 'model' => $quote
+ ];
+ }
+
+ /**
+ * Returns taxes applied to the current quote
+ *
+ * @param Total $total
+ * @param string $currency
+ * @return array
+ */
+ private function getAppliedTaxes(Total $total, string $currency): array
+ {
+ $appliedTaxesData = [];
+ $appliedTaxes = $total->getAppliedTaxes();
+
+ if (count($appliedTaxes) === 0) {
+ return $appliedTaxesData;
+ }
+
+ foreach ($appliedTaxes as $appliedTax) {
+ $appliedTaxesData[] = [
+ 'label' => $appliedTax['id'],
+ 'amount' => ['value' => $appliedTax['amount'], 'currency' => $currency]
+ ];
+ }
+ return $appliedTaxesData;
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php
index 06123abe615e6..f020527d958e4 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CreateEmptyCart.php
@@ -7,13 +7,15 @@
namespace Magento\QuoteGraphQl\Model\Resolver;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\GraphQl\Config\Element\Field;
+use Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException;
+use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
-use Magento\Quote\Api\CartManagementInterface;
-use Magento\Quote\Api\GuestCartManagementInterface;
-use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface;
-use Magento\Quote\Model\QuoteIdMaskFactory;
+use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
+use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer;
+use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForGuest;
/**
* @inheritdoc
@@ -21,41 +23,33 @@
class CreateEmptyCart implements ResolverInterface
{
/**
- * @var CartManagementInterface
+ * @var CreateEmptyCartForCustomer
*/
- private $cartManagement;
+ private $createEmptyCartForCustomer;
/**
- * @var GuestCartManagementInterface
+ * @var CreateEmptyCartForGuest
*/
- private $guestCartManagement;
+ private $createEmptyCartForGuest;
/**
- * @var QuoteIdToMaskedQuoteIdInterface
+ * @var MaskedQuoteIdToQuoteIdInterface
*/
- private $quoteIdToMaskedId;
+ private $maskedQuoteIdToQuoteId;
/**
- * @var QuoteIdMaskFactory
- */
- private $quoteIdMaskFactory;
-
- /**
- * @param CartManagementInterface $cartManagement
- * @param GuestCartManagementInterface $guestCartManagement
- * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId
- * @param QuoteIdMaskFactory $quoteIdMaskFactory
+ * @param CreateEmptyCartForCustomer $createEmptyCartForCustomer
+ * @param CreateEmptyCartForGuest $createEmptyCartForGuest
+ * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
*/
public function __construct(
- CartManagementInterface $cartManagement,
- GuestCartManagementInterface $guestCartManagement,
- QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId,
- QuoteIdMaskFactory $quoteIdMaskFactory
+ CreateEmptyCartForCustomer $createEmptyCartForCustomer,
+ CreateEmptyCartForGuest $createEmptyCartForGuest,
+ MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
) {
- $this->cartManagement = $cartManagement;
- $this->guestCartManagement = $guestCartManagement;
- $this->quoteIdToMaskedId = $quoteIdToMaskedId;
- $this->quoteIdMaskFactory = $quoteIdMaskFactory;
+ $this->createEmptyCartForCustomer = $createEmptyCartForCustomer;
+ $this->createEmptyCartForGuest = $createEmptyCartForGuest;
+ $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId;
}
/**
@@ -65,19 +59,49 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
{
$customerId = $context->getUserId();
- if (0 !== $customerId && null !== $customerId) {
- $quoteId = $this->cartManagement->createEmptyCartForCustomer($customerId);
- $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quoteId);
-
- if (empty($maskedQuoteId)) {
- $quoteIdMask = $this->quoteIdMaskFactory->create();
- $quoteIdMask->setQuoteId($quoteId)->save();
- $maskedQuoteId = $quoteIdMask->getMaskedId();
- }
- } else {
- $maskedQuoteId = $this->guestCartManagement->createEmptyCart();
+ $predefinedMaskedQuoteId = null;
+ if (isset($args['input']['cart_id'])) {
+ $predefinedMaskedQuoteId = $args['input']['cart_id'];
+ $this->validateMaskedId($predefinedMaskedQuoteId);
}
+ $maskedQuoteId = (0 === $customerId || null === $customerId)
+ ? $this->createEmptyCartForGuest->execute($predefinedMaskedQuoteId)
+ : $this->createEmptyCartForCustomer->execute($customerId, $predefinedMaskedQuoteId);
return $maskedQuoteId;
}
+
+ /**
+ * Validate masked id
+ *
+ * @param string $maskedId
+ * @throws GraphQlAlreadyExistsException
+ * @throws GraphQlInputException
+ */
+ private function validateMaskedId(string $maskedId): void
+ {
+ if (mb_strlen($maskedId) != 32) {
+ throw new GraphQlInputException(__('Cart ID length should to be 32 symbols.'));
+ }
+
+ if ($this->isQuoteWithSuchMaskedIdAlreadyExists($maskedId)) {
+ throw new GraphQlAlreadyExistsException(__('Cart with ID "%1" already exists.', $maskedId));
+ }
+ }
+
+ /**
+ * Check is quote with such maskedId already exists
+ *
+ * @param string $maskedId
+ * @return bool
+ */
+ private function isQuoteWithSuchMaskedIdAlreadyExists(string $maskedId): bool
+ {
+ try {
+ $this->maskedQuoteIdToQuoteId->execute($maskedId);
+ return true;
+ } catch (NoSuchEntityException $e) {
+ return false;
+ }
+ }
}
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php
index 3bd46a664f2ab..1672474bb3ddd 100644
--- a/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php
@@ -65,6 +65,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
$cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId());
+ if ($context->getUserId() === 0) {
+ if (!$cart->getCustomerEmail()) {
+ throw new GraphQlInputException(__("Guest email for cart is missing. Please enter"));
+ }
+ $cart->setCheckoutMethod(CartManagementInterface::METHOD_GUEST);
+ }
+
try {
$orderId = $this->cartManagement->placeOrder($cart->getId());
$order = $this->orderRepository->get($orderId);
diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetGuestEmailOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetGuestEmailOnCart.php
new file mode 100644
index 0000000000000..d621057348b54
--- /dev/null
+++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetGuestEmailOnCart.php
@@ -0,0 +1,95 @@
+getCartForUser = $getCartForUser;
+ $this->cartRepository = $cartRepository;
+ $this->emailValidator = $emailValidator;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
+ {
+ if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) {
+ throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
+ }
+ $maskedCartId = $args['input']['cart_id'];
+
+ if (!isset($args['input']['email']) || empty($args['input']['email'])) {
+ throw new GraphQlInputException(__('Required parameter "email" is missing'));
+ }
+
+ if (false === $this->emailValidator->isValid($args['input']['email'])) {
+ throw new GraphQlInputException(__('Invalid email format'));
+ }
+ $email = $args['input']['email'];
+
+ $currentUserId = $context->getUserId();
+
+ if ($currentUserId !== 0) {
+ throw new GraphQlInputException(__('The request is not allowed for logged in customers'));
+ }
+
+ $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId);
+ $cart->setCustomerEmail($email);
+
+ try {
+ $this->cartRepository->save($cart);
+ } catch (CouldNotSaveException $e) {
+ throw new LocalizedException(__($e->getMessage()), $e);
+ }
+
+ return [
+ 'cart' => [
+ 'model' => $cart,
+ ],
+ ];
+ }
+}
diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
index 9ec3492f64531..a9784e97c8952 100644
--- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls
@@ -6,7 +6,7 @@ type Query {
}
type Mutation {
- createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates an empty shopping cart for a guest or logged in user")
+ createEmptyCart(input: createEmptyCartInput): String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates an empty shopping cart for a guest or logged in user")
addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart")
addVirtualProductsToCart(input: AddVirtualProductsToCartInput): AddVirtualProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart")
applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart")
@@ -17,9 +17,14 @@ type Mutation {
setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetBillingAddressOnCart")
setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart")
setPaymentMethodOnCart(input: SetPaymentMethodOnCartInput): SetPaymentMethodOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart")
+ setGuestEmailOnCart(input: SetGuestEmailOnCartInput): SetGuestEmailOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetGuestEmailOnCart")
placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder")
}
+input createEmptyCartInput {
+ cart_id: String
+}
+
input AddSimpleProductsToCartInput {
cart_id: String!
cartItems: [SimpleProductCartItemInput!]!
@@ -129,6 +134,24 @@ input PaymentMethodInput {
purchase_order_number: String @doc(description:"Purchase order number")
}
+input SetGuestEmailOnCartInput {
+ cart_id: String!
+ email: String!
+}
+
+type CartPrices {
+ grand_total: Money
+ subtotal_including_tax: Money
+ subtotal_excluding_tax: Money
+ subtotal_with_discount_excluding_tax: Money
+ applied_taxes: [CartTaxItem]
+}
+
+type CartTaxItem {
+ amount: Money!
+ label: String!
+}
+
type SetPaymentMethodOnCartOutput {
cart: Cart!
}
@@ -156,10 +179,12 @@ type PlaceOrderOutput {
type Cart {
items: [CartItemInterface] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartItems")
applied_coupon: AppliedCoupon @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupon")
+ email: String @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartEmail")
shipping_addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddresses")
billing_address: CartAddress! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress")
available_payment_methods: [AvailablePaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods")
selected_payment_method: SelectedPaymentMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SelectedPaymentMethod")
+ prices: CartPrices @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartPrices")
}
type CartAddress {
@@ -258,6 +283,10 @@ type RemoveItemFromCartOutput {
cart: Cart!
}
+type SetGuestEmailOnCartOutput {
+ cart: Cart!
+}
+
type SimpleCartItem implements CartItemInterface @doc(description: "Simple Cart Item") {
customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions")
}
diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php
index 1985db0b90e2a..2009cd3ff9d92 100644
--- a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php
+++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php
@@ -4,16 +4,16 @@
* See COPYING.txt for license details.
*/
+namespace Magento\Reports\Model\ResourceModel\Product\Downloads;
+
/**
* Product Downloads Report collection
*
* @author Magento Core Team
- */
-namespace Magento\Reports\Model\ResourceModel\Product\Downloads;
-
-/**
+ *
* @api
* @since 100.0.2
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
*/
class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection
{
@@ -97,4 +97,14 @@ public function addFieldToFilter($field, $condition = null)
}
return $this;
}
+
+ /**
+ * @inheritDoc
+ */
+ public function getSelectCountSql()
+ {
+ $countSelect = parent::getSelectCountSql();
+ $countSelect->reset(\Zend\Db\Sql\Select::GROUP);
+ return $countSelect;
+ }
}
diff --git a/app/code/Magento/Review/Block/Adminhtml/Edit/Form.php b/app/code/Magento/Review/Block/Adminhtml/Edit/Form.php
index 8a8395de72b62..4f7237a0b44be 100644
--- a/app/code/Magento/Review/Block/Adminhtml/Edit/Form.php
+++ b/app/code/Magento/Review/Block/Adminhtml/Edit/Form.php
@@ -4,11 +4,11 @@
* See COPYING.txt for license details.
*/
+namespace Magento\Review\Block\Adminhtml\Edit;
+
/**
* Adminhtml Review Edit Form
*/
-namespace Magento\Review\Block\Adminhtml\Edit;
-
class Form extends \Magento\Backend\Block\Widget\Form\Generic
{
/**
@@ -84,7 +84,8 @@ protected function _prepareForm()
'review/*/save',
[
'id' => $this->getRequest()->getParam('id'),
- 'ret' => $this->_coreRegistry->registry('ret')
+ 'ret' => $this->_coreRegistry->registry('ret'),
+ 'productId' => $this->getRequest()->getParam('productId')
]
),
'method' => 'post',
diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php
index 6217729f53e50..57b1e538ddb6b 100644
--- a/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php
+++ b/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php
@@ -73,6 +73,10 @@ public function execute()
} else {
$resultRedirect->setPath('*/*/');
}
+ $productId = (int)$this->getRequest()->getParam('productId');
+ if ($productId) {
+ $resultRedirect->setPath("catalog/product/edit/id/$productId");
+ }
return $resultRedirect;
}
$resultRedirect->setPath('review/*/');
diff --git a/app/code/Magento/Rule/Block/Editable.php b/app/code/Magento/Rule/Block/Editable.php
index d53213a7df876..57bbc72ff2660 100644
--- a/app/code/Magento/Rule/Block/Editable.php
+++ b/app/code/Magento/Rule/Block/Editable.php
@@ -58,11 +58,11 @@ public function render(\Magento\Framework\Data\Form\Element\AbstractElement $ele
'" name="' .
$this->escapeHtmlAttr($element->getName()) .
'" value="' .
- $element->getValue() .
+ $this->escapeHtmlAttr($element->getValue()) .
'" data-form-part="' .
- $element->getData('data-form-part') .
+ $this->escapeHtmlAttr($element->getData('data-form-part')) .
'"/> ' .
- htmlspecialchars(
+ $this->escapeHtml(
$valueName
) . ' ';
} else {
diff --git a/app/code/Magento/Rule/view/adminhtml/web/rules.js b/app/code/Magento/Rule/view/adminhtml/web/rules.js
index 202337c39da35..95175d272f9af 100644
--- a/app/code/Magento/Rule/view/adminhtml/web/rules.js
+++ b/app/code/Magento/Rule/view/adminhtml/web/rules.js
@@ -13,6 +13,7 @@ define([
'mage/translate',
'prototype'
], function (jQuery) {
+ 'use strict';
var VarienRulesForm = new Class.create();
@@ -125,7 +126,7 @@ define([
var values = this.updateElement.value.split(','),
s = '';
- for (i = 0; i < values.length; i++) {
+ for (var i = 0; i < values.length; i++) {
s = values[i].strip();
if (s != '') {
@@ -254,7 +255,7 @@ define([
if (elem && elem.options) {
var selectedOptions = [];
- for (i = 0; i < elem.options.length; i++) {
+ for (var i = 0; i < elem.options.length; i++) {
if (elem.options[i].selected) {
selectedOptions.push(elem.options[i].text);
}
@@ -299,14 +300,14 @@ define([
},
changeVisibilityForValueRuleParam: function(elem) {
- let parsedElementId = elem.id.split('__');
- if (parsedElementId[2] != 'operator') {
+ var parsedElementId = elem.id.split('__');
+ if (parsedElementId[2] !== 'operator') {
return false;
}
- let valueElement = jQuery('#' + parsedElementId[0] + '__' + parsedElementId[1] + '__value');
+ var valueElement = jQuery('#' + parsedElementId[0] + '__' + parsedElementId[1] + '__value');
- if(elem.value == '<=>') {
+ if(elem.value === '<=>') {
valueElement.closest('.rule-param').hide();
} else {
valueElement.closest('.rule-param').show();
diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Invoice/View.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Invoice/View.php
index fd6e5f403f2de..074aa99a5e791 100644
--- a/app/code/Magento/Sales/Block/Adminhtml/Order/Invoice/View.php
+++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Invoice/View.php
@@ -113,8 +113,8 @@ protected function _construct()
$orderPayment->canRefund() && !$this->getInvoice()->getIsUsedForRefund()
) {
$this->buttonList->add(
- 'capture',
- [ // capture?
+ 'credit-memo',
+ [
'label' => __('Credit Memo'),
'class' => 'credit-memo',
'onclick' => 'setLocation(\'' . $this->getCreditMemoUrl() . '\')'
diff --git a/app/code/Magento/Sales/Helper/Admin.php b/app/code/Magento/Sales/Helper/Admin.php
index 7053a696dcab5..f04584ea19c37 100644
--- a/app/code/Magento/Sales/Helper/Admin.php
+++ b/app/code/Magento/Sales/Helper/Admin.php
@@ -7,6 +7,8 @@
namespace Magento\Sales\Helper;
+use Magento\Framework\App\ObjectManager;
+
/**
* Sales admin helper.
*/
@@ -32,24 +34,33 @@ class Admin extends \Magento\Framework\App\Helper\AbstractHelper
*/
protected $escaper;
+ /**
+ * @var \DOMDocumentFactory
+ */
+ private $domDocumentFactory;
+
/**
* @param \Magento\Framework\App\Helper\Context $context
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Sales\Model\Config $salesConfig
* @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
* @param \Magento\Framework\Escaper $escaper
+ * @param \DOMDocumentFactory|null $domDocumentFactory
*/
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Sales\Model\Config $salesConfig,
\Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
- \Magento\Framework\Escaper $escaper
+ \Magento\Framework\Escaper $escaper,
+ \DOMDocumentFactory $domDocumentFactory = null
) {
$this->priceCurrency = $priceCurrency;
$this->_storeManager = $storeManager;
$this->_salesConfig = $salesConfig;
$this->escaper = $escaper;
+ $this->domDocumentFactory = $domDocumentFactory
+ ?: ObjectManager::getInstance()->get(\DOMDocumentFactory::class);
parent::__construct($context);
}
@@ -150,30 +161,41 @@ public function applySalableProductTypesFilter($collection)
public function escapeHtmlWithLinks($data, $allowedTags = null)
{
if (!empty($data) && is_array($allowedTags) && in_array('a', $allowedTags)) {
- $links = [];
- $i = 1;
- $data = str_replace('%', '%%', $data);
- $regexp = "#(?J).*?)\\1\s*)|(?:\S+\s*=\s*(['\"])(.*?)\\3)\s*)*)|>)"
- .">?(?:(?:(?.*?)(?:<\/a\s*>?|(?=<\w))|(?.*)))#si";
- while (preg_match($regexp, $data, $matches)) {
- $text = '';
- if (!empty($matches['text'])) {
- $text = str_replace('%%', '%', $matches['text']);
+ $wrapperElementId = uniqid();
+ $domDocument = $this->domDocumentFactory->create();
+
+ $internalErrors = libxml_use_internal_errors(true);
+
+ $domDocument->loadHTML(
+ '' . $data . ''
+ );
+
+ libxml_use_internal_errors($internalErrors);
+
+ $linkTags = $domDocument->getElementsByTagName('a');
+
+ foreach ($linkTags as $linkNode) {
+ $linkAttributes = [];
+ foreach ($linkNode->attributes as $attribute) {
+ $linkAttributes[$attribute->name] = $attribute->value;
+ }
+
+ foreach ($linkAttributes as $attributeName => $attributeValue) {
+ if ($attributeName === 'href') {
+ $url = $this->filterUrl($attributeValue ?? '');
+ $url = $this->escaper->escapeUrl($url);
+ $linkNode->setAttribute('href', $url);
+ } else {
+ $linkNode->removeAttribute($attributeName);
+ }
}
- $url = $this->filterUrl($matches['link'] ?? '');
- //Recreate a minimalistic secure a tag
- $links[] = sprintf(
- '%s',
- htmlspecialchars($url, ENT_QUOTES, 'UTF-8', false),
- $this->escaper->escapeHtml($text)
- );
- $data = str_replace($matches[0], '%' . $i . '$s', $data);
- ++$i;
}
- $data = $this->escaper->escapeHtml($data, $allowedTags);
- return vsprintf($data, $links);
+
+ $result = mb_convert_encoding($domDocument->saveHTML(), 'UTF-8', 'HTML-ENTITIES');
+ preg_match('/(.+)<\/body><\/html>$/si', $result, $matches);
+ $data = !empty($matches) ? $matches[1] : '';
}
+
return $this->escaper->escapeHtml($data, $allowedTags);
}
@@ -187,7 +209,7 @@ private function filterUrl(string $url): string
{
if ($url) {
//Revert the sprintf escaping
- $url = str_replace('%%', '%', $url);
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
$urlScheme = parse_url($url, PHP_URL_SCHEME);
$urlScheme = $urlScheme ? strtolower($urlScheme) : '';
if ($urlScheme !== 'http' && $urlScheme !== 'https') {
diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Sender/EmailSender.php
index ecd5670a319e7..3d2c13cbaaaa9 100644
--- a/app/code/Magento/Sales/Model/Order/Creditmemo/Sender/EmailSender.php
+++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Sender/EmailSender.php
@@ -89,6 +89,7 @@ public function __construct(
* @param bool $forceSyncMode
*
* @return bool
+ * @throws \Exception
*/
public function send(
\Magento\Sales\Api\Data\OrderInterface $order,
@@ -96,7 +97,7 @@ public function send(
\Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null,
$forceSyncMode = false
) {
- $creditmemo->setSendEmail(true);
+ $creditmemo->setSendEmail($this->identityContainer->isEnabled());
if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) {
$transport = [
@@ -145,6 +146,7 @@ public function send(
* @param \Magento\Sales\Api\Data\OrderInterface $order
*
* @return string
+ * @throws \Exception
*/
private function getPaymentHtml(\Magento\Sales\Api\Data\OrderInterface $order)
{
diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php
index 8004483583114..126fe4f93f1e0 100644
--- a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php
+++ b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php
@@ -57,10 +57,10 @@ class CreditmemoSender extends Sender
* @param CreditmemoIdentity $identityContainer
* @param Order\Email\SenderBuilderFactory $senderBuilderFactory
* @param \Psr\Log\LoggerInterface $logger
+ * @param Renderer $addressRenderer
* @param PaymentHelper $paymentHelper
* @param CreditmemoResource $creditmemoResource
* @param \Magento\Framework\App\Config\ScopeConfigInterface $globalConfig
- * @param Renderer $addressRenderer
* @param ManagerInterface $eventManager
*/
public function __construct(
@@ -96,10 +96,11 @@ public function __construct(
* @param Creditmemo $creditmemo
* @param bool $forceSyncMode
* @return bool
+ * @throws \Exception
*/
public function send(Creditmemo $creditmemo, $forceSyncMode = false)
{
- $creditmemo->setSendEmail(true);
+ $creditmemo->setSendEmail($this->identityContainer->isEnabled());
if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) {
$order = $creditmemo->getOrder();
@@ -146,6 +147,7 @@ public function send(Creditmemo $creditmemo, $forceSyncMode = false)
*
* @param Order $order
* @return string
+ * @throws \Exception
*/
protected function getPaymentHtml(Order $order)
{
diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php
index 994fd79945cfd..ba3895cfa1524 100644
--- a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php
+++ b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php
@@ -57,10 +57,10 @@ class InvoiceSender extends Sender
* @param InvoiceIdentity $identityContainer
* @param Order\Email\SenderBuilderFactory $senderBuilderFactory
* @param \Psr\Log\LoggerInterface $logger
+ * @param Renderer $addressRenderer
* @param PaymentHelper $paymentHelper
* @param InvoiceResource $invoiceResource
* @param \Magento\Framework\App\Config\ScopeConfigInterface $globalConfig
- * @param Renderer $addressRenderer
* @param ManagerInterface $eventManager
*/
public function __construct(
@@ -96,10 +96,11 @@ public function __construct(
* @param Invoice $invoice
* @param bool $forceSyncMode
* @return bool
+ * @throws \Exception
*/
public function send(Invoice $invoice, $forceSyncMode = false)
{
- $invoice->setSendEmail(true);
+ $invoice->setSendEmail($this->identityContainer->isEnabled());
if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) {
$order = $invoice->getOrder();
@@ -146,6 +147,7 @@ public function send(Invoice $invoice, $forceSyncMode = false)
*
* @param Order $order
* @return string
+ * @throws \Exception
*/
protected function getPaymentHtml(Order $order)
{
diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php
index 6729c746f5565..10e5e37a49394 100644
--- a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php
+++ b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php
@@ -57,10 +57,10 @@ class ShipmentSender extends Sender
* @param ShipmentIdentity $identityContainer
* @param Order\Email\SenderBuilderFactory $senderBuilderFactory
* @param \Psr\Log\LoggerInterface $logger
+ * @param Renderer $addressRenderer
* @param PaymentHelper $paymentHelper
* @param ShipmentResource $shipmentResource
* @param \Magento\Framework\App\Config\ScopeConfigInterface $globalConfig
- * @param Renderer $addressRenderer
* @param ManagerInterface $eventManager
*/
public function __construct(
@@ -96,10 +96,11 @@ public function __construct(
* @param Shipment $shipment
* @param bool $forceSyncMode
* @return bool
+ * @throws \Exception
*/
public function send(Shipment $shipment, $forceSyncMode = false)
{
- $shipment->setSendEmail(true);
+ $shipment->setSendEmail($this->identityContainer->isEnabled());
if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) {
$order = $shipment->getOrder();
@@ -146,6 +147,7 @@ public function send(Shipment $shipment, $forceSyncMode = false)
*
* @param Order $order
* @return string
+ * @throws \Exception
*/
protected function getPaymentHtml(Order $order)
{
diff --git a/app/code/Magento/Sales/Model/Order/Invoice/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Invoice/Sender/EmailSender.php
index aa0687bee504f..5ae3306ddf75b 100644
--- a/app/code/Magento/Sales/Model/Order/Invoice/Sender/EmailSender.php
+++ b/app/code/Magento/Sales/Model/Order/Invoice/Sender/EmailSender.php
@@ -89,6 +89,7 @@ public function __construct(
* @param bool $forceSyncMode
*
* @return bool
+ * @throws \Exception
*/
public function send(
\Magento\Sales\Api\Data\OrderInterface $order,
@@ -96,7 +97,7 @@ public function send(
\Magento\Sales\Api\Data\InvoiceCommentCreationInterface $comment = null,
$forceSyncMode = false
) {
- $invoice->setSendEmail(true);
+ $invoice->setSendEmail($this->identityContainer->isEnabled());
if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) {
$transport = [
@@ -145,6 +146,7 @@ public function send(
* @param \Magento\Sales\Api\Data\OrderInterface $order
*
* @return string
+ * @throws \Exception
*/
private function getPaymentHtml(\Magento\Sales\Api\Data\OrderInterface $order)
{
diff --git a/app/code/Magento/Sales/Model/Order/Payment.php b/app/code/Magento/Sales/Model/Order/Payment.php
index fc39755c94ee0..5d1d3f0d040a7 100644
--- a/app/code/Magento/Sales/Model/Order/Payment.php
+++ b/app/code/Magento/Sales/Model/Order/Payment.php
@@ -300,7 +300,7 @@ public function canCapture()
}
/**
- * Check refund availability
+ * Check refund availability.
*
* @return bool
*/
@@ -310,7 +310,7 @@ public function canRefund()
}
/**
- * Check partial refund availability for invoice
+ * Check partial refund availability for invoice.
*
* @return bool
*/
@@ -320,7 +320,7 @@ public function canRefundPartialPerInvoice()
}
/**
- * Check partial capture availability
+ * Check partial capture availability.
*
* @return bool
*/
@@ -546,9 +546,7 @@ public function cancelInvoice($invoice)
}
/**
- * Create new invoice with maximum qty for invoice for each item
- *
- * Register this invoice and capture
+ * Create new invoice with maximum qty for invoice for each item register this invoice and capture
*
* @return Invoice
*/
@@ -686,6 +684,7 @@ public function refund($creditmemo)
$gateway->refund($this, $baseAmountToRefund);
$creditmemo->setTransactionId($this->getLastTransId());
+ // phpcs:ignore Magento2.Exceptions.ThrowCatch
} catch (\Magento\Framework\Exception\LocalizedException $e) {
if (!$captureTxn) {
throw new \Magento\Framework\Exception\LocalizedException(
@@ -732,10 +731,14 @@ public function refund($creditmemo)
$message = $message = $this->prependMessage($message);
$message = $this->_appendTransactionToMessage($transaction, $message);
$orderState = $this->getOrderStateResolver()->getStateForOrder($this->getOrder());
+ $statuses = $this->getOrder()->getConfig()->getStateStatuses($orderState, false);
+ $status = in_array($this->getOrder()->getStatus(), $statuses, true)
+ ? $this->getOrder()->getStatus()
+ : $this->getOrder()->getConfig()->getStateDefaultStatus($orderState);
$this->getOrder()
->addStatusHistoryComment(
$message,
- $this->getOrder()->getConfig()->getStateDefaultStatus($orderState)
+ $status
)->setIsCustomerNotified($creditmemo->getOrder()->getCustomerNoteNotify());
$this->_eventManager->dispatch(
'sales_order_payment_refund',
@@ -1203,7 +1206,7 @@ public function addTransaction($type, $salesDocument = null, $failSafe = false)
}
/**
- * Add message to the specified transaction.
+ * Add transaction comments to order.
*
* @param Transaction|null $transaction
* @param string $message
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php
index 0a393548069f5..3657f84d4445d 100644
--- a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php
@@ -89,6 +89,7 @@ public function __construct(
* @param bool $forceSyncMode
*
* @return bool
+ * @throws \Exception
*/
public function send(
\Magento\Sales\Api\Data\OrderInterface $order,
@@ -96,7 +97,7 @@ public function send(
\Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null,
$forceSyncMode = false
) {
- $shipment->setSendEmail(true);
+ $shipment->setSendEmail($this->identityContainer->isEnabled());
if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) {
$transport = [
@@ -145,6 +146,7 @@ public function send(
* @param \Magento\Sales\Api\Data\OrderInterface $order
*
* @return string
+ * @throws \Exception
*/
private function getPaymentHtml(\Magento\Sales\Api\Data\OrderInterface $order)
{
diff --git a/app/code/Magento/Sales/Model/OrderRepository.php b/app/code/Magento/Sales/Model/OrderRepository.php
index 9a1392fbe9065..79548cb190754 100644
--- a/app/code/Magento/Sales/Model/OrderRepository.php
+++ b/app/code/Magento/Sales/Model/OrderRepository.php
@@ -6,7 +6,9 @@
namespace Magento\Sales\Model;
+use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Sales\Api\Data\OrderExtensionFactory;
@@ -16,7 +18,6 @@
use Magento\Sales\Api\Data\ShippingAssignmentInterface;
use Magento\Sales\Model\Order\ShippingAssignmentBuilder;
use Magento\Sales\Model\ResourceModel\Metadata;
-use Magento\Framework\App\ObjectManager;
use Magento\Tax\Api\OrderTaxManagementInterface;
use Magento\Payment\Api\Data\PaymentAdditionalInfoInterface;
use Magento\Payment\Api\Data\PaymentAdditionalInfoInterfaceFactory;
@@ -74,6 +75,11 @@ class OrderRepository implements \Magento\Sales\Api\OrderRepositoryInterface
*/
private $serializer;
+ /**
+ * @var JoinProcessorInterface
+ */
+ private $extensionAttributesJoinProcessor;
+
/**
* Constructor
*
@@ -84,6 +90,7 @@ class OrderRepository implements \Magento\Sales\Api\OrderRepositoryInterface
* @param OrderTaxManagementInterface|null $orderTaxManagement
* @param PaymentAdditionalInfoInterfaceFactory|null $paymentAdditionalInfoFactory
* @param JsonSerializer|null $serializer
+ * @param JoinProcessorInterface $extensionAttributesJoinProcessor
*/
public function __construct(
Metadata $metadata,
@@ -92,7 +99,8 @@ public function __construct(
\Magento\Sales\Api\Data\OrderExtensionFactory $orderExtensionFactory = null,
OrderTaxManagementInterface $orderTaxManagement = null,
PaymentAdditionalInfoInterfaceFactory $paymentAdditionalInfoFactory = null,
- JsonSerializer $serializer = null
+ JsonSerializer $serializer = null,
+ JoinProcessorInterface $extensionAttributesJoinProcessor = null
) {
$this->metadata = $metadata;
$this->searchResultFactory = $searchResultFactory;
@@ -106,6 +114,8 @@ public function __construct(
->get(PaymentAdditionalInfoInterfaceFactory::class);
$this->serializer = $serializer ?: ObjectManager::getInstance()
->get(JsonSerializer::class);
+ $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor
+ ?: ObjectManager::getInstance()->get(JoinProcessorInterface::class);
}
/**
@@ -198,6 +208,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr
{
/** @var \Magento\Sales\Api\Data\OrderSearchResultInterface $searchResult */
$searchResult = $this->searchResultFactory->create();
+ $this->extensionAttributesJoinProcessor->process($searchResult);
$this->collectionProcessor->process($searchCriteria, $searchResult);
$searchResult->setSearchCriteria($searchCriteria);
foreach ($searchResult->getItems() as $order) {
diff --git a/app/code/Magento/Sales/Model/RefundOrder.php b/app/code/Magento/Sales/Model/RefundOrder.php
index d79f5ecf857cb..07555cba1b7f7 100644
--- a/app/code/Magento/Sales/Model/RefundOrder.php
+++ b/app/code/Magento/Sales/Model/RefundOrder.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Sales\Model;
use Magento\Framework\App\ResourceConnection;
@@ -151,10 +152,13 @@ public function execute(
$creditmemo->setState(\Magento\Sales\Model\Order\Creditmemo::STATE_REFUNDED);
$order->setCustomerNoteNotify($notify);
$order = $this->refundAdapter->refund($creditmemo, $order);
- $order->setState(
- $this->orderStateResolver->getStateForOrder($order, [])
- );
- $order->setStatus($this->config->getStateDefaultStatus($order->getState()));
+ $orderState = $this->orderStateResolver->getStateForOrder($order, []);
+ $order->setState($orderState);
+ $statuses = $this->config->getStateStatuses($orderState, false);
+ $status = in_array($order->getStatus(), $statuses, true)
+ ? $order->getStatus()
+ : $this->config->getStateDefaultStatus($orderState);
+ $order->setStatus($status);
$order = $this->orderRepository->save($order);
$creditmemo = $this->creditmemoRepository->save($creditmemo);
diff --git a/app/code/Magento/Sales/Model/Service/InvoiceService.php b/app/code/Magento/Sales/Model/Service/InvoiceService.php
index 02242e92c8bf5..18efeba726c1b 100644
--- a/app/code/Magento/Sales/Model/Service/InvoiceService.php
+++ b/app/code/Magento/Sales/Model/Service/InvoiceService.php
@@ -149,6 +149,7 @@ public function setVoid($id)
*/
public function prepareInvoice(Order $order, array $qtys = [])
{
+ $isQtysEmpty = empty($qtys);
$invoice = $this->orderConverter->toInvoice($order);
$totalQty = 0;
$qtys = $this->prepareItemsQty($order, $qtys);
@@ -161,7 +162,7 @@ public function prepareInvoice(Order $order, array $qtys = [])
$qty = (double) $qtys[$orderItem->getId()];
} elseif ($orderItem->isDummy()) {
$qty = $orderItem->getQtyOrdered() ? $orderItem->getQtyOrdered() : 1;
- } elseif (empty($qtys)) {
+ } elseif ($isQtysEmpty) {
$qty = $orderItem->getQtyToInvoice();
} else {
$qty = 0;
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml
index 85ef563e10db7..315a097eb2323 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml
@@ -26,7 +26,7 @@
-
+
diff --git a/app/code/Magento/Sales/Test/Unit/Helper/AdminTest.php b/app/code/Magento/Sales/Test/Unit/Helper/AdminTest.php
index 389064b7274a7..286ebd0932b40 100644
--- a/app/code/Magento/Sales/Test/Unit/Helper/AdminTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Helper/AdminTest.php
@@ -71,7 +71,7 @@ protected function setUp()
->disableOriginalConstructor()
->getMock();
- $this->adminHelper = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
+ $this->adminHelper = (new ObjectManager($this))->getObject(
\Magento\Sales\Helper\Admin::class,
[
'context' => $this->contextMock,
@@ -330,72 +330,16 @@ public function applySalableProductTypesFilterDataProvider()
}
/**
- * @param string $data
- * @param string $expected
- * @param null|array $allowedTags
- * @dataProvider escapeHtmlWithLinksDataProvider
+ * @return void
*/
- public function testEscapeHtmlWithLinks($data, $expected, $allowedTags = null)
+ public function testEscapeHtmlWithLinks(): void
{
+ $expected = '<a>some text in tags</a>';
$this->escaperMock
->expects($this->any())
->method('escapeHtml')
->will($this->returnValue($expected));
- $actual = $this->adminHelper->escapeHtmlWithLinks($data, $allowedTags);
+ $actual = $this->adminHelper->escapeHtmlWithLinks('some text in tags');
$this->assertEquals($expected, $actual);
}
-
- /**
- * @return array
- */
- public function escapeHtmlWithLinksDataProvider()
- {
- return [
- [
- 'some text in tags',
- '<a>some text in tags</a>',
- 'allowedTags' => null
- ],
- [
- 'Transaction ID: "XX123XX"',
- 'Transaction ID: "XX123XX"',
- 'allowedTags' => ['b', 'br', 'strong', 'i', 'u', 'a']
- ],
- [
- 'some text in tags',
- 'some text in tags',
- 'allowedTags' => ['a']
- ],
- 'Not replacement with placeholders' => [
- "",
- '<script>alert(1)</script>',
- 'allowedTags' => ['a']
- ],
- 'Normal usage, url escaped' => [
- 'Foo',
- 'Foo',
- 'allowedTags' => ['a']
- ],
- 'Normal usage, url not escaped' => [
- "Foo",
- 'Foo',
- 'allowedTags' => ['a']
- ],
- 'XSS test' => [
- "Foo",
- 'Foo',
- 'allowedTags' => ['a']
- ],
- 'Additional regex test' => [
- "Foo",
- 'Foo',
- 'allowedTags' => ['a']
- ],
- 'Break of valid urls' => [
- "Foo",
- 'Foo',
- 'allowedTags' => ['a']
- ],
- ];
- }
}
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Sender/EmailSenderTest.php
index 9fd2a8b0d929f..467476c9bb406 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Sender/EmailSenderTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Sender/EmailSenderTest.php
@@ -1,5 +1,4 @@
creditmemoMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with($emailSendingResult);
if (!$configValue || $forceSyncMode) {
$transport = [
@@ -279,7 +278,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending
->method('setTemplateVars')
->with($transport->getData());
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn($emailSendingResult);
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php
index 31bf846689230..1f074d7262f4d 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php
@@ -7,6 +7,9 @@
use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender;
+/**
+ * Test for Magento\Sales\Model\Order\Email\Sender\CreditmemoSender class.
+ */
class CreditmemoSenderTest extends AbstractSenderTest
{
/**
@@ -90,7 +93,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema
$this->creditmemoMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with($emailSendingResult);
$this->globalConfig->expects($this->once())
->method('getValue')
@@ -130,7 +133,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema
]
);
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn($emailSendingResult);
@@ -197,6 +200,8 @@ public function sendDataProvider()
* @param bool $isVirtualOrder
* @param int $formatCallCount
* @param string|null $expectedShippingAddress
+ *
+ * @return void
* @dataProvider sendVirtualOrderDataProvider
*/
public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress)
@@ -207,7 +212,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte
$this->creditmemoMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with(false);
$this->globalConfig->expects($this->once())
->method('getValue')
@@ -242,7 +247,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte
]
);
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn(false);
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php
index 9c54c716e4207..d1aa5af53da4d 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php
@@ -7,6 +7,9 @@
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
+/**
+ * Test for Magento\Sales\Model\Order\Email\Sender\InvoiceSender class.
+ */
class InvoiceSenderTest extends AbstractSenderTest
{
/**
@@ -90,7 +93,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema
$this->invoiceMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with($emailSendingResult);
$this->globalConfig->expects($this->once())
->method('getValue')
@@ -136,7 +139,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema
]
);
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn($emailSendingResult);
@@ -212,7 +215,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte
$this->invoiceMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with(false);
$this->globalConfig->expects($this->once())
->method('getValue')
@@ -247,7 +250,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte
]
);
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn(false);
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php
index b1b18af63b590..2d7b42bccae5a 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php
@@ -7,6 +7,9 @@
use Magento\Sales\Model\Order\Email\Sender\ShipmentSender;
+/**
+ * Test for Magento\Sales\Model\Order\Email\Sender\ShipmentSender class.
+ */
class ShipmentSenderTest extends AbstractSenderTest
{
/**
@@ -90,7 +93,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema
$this->shipmentMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with($emailSendingResult);
$this->globalConfig->expects($this->once())
->method('getValue')
@@ -136,7 +139,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema
]
);
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn($emailSendingResult);
@@ -212,7 +215,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte
$this->shipmentMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with(false);
$this->globalConfig->expects($this->once())
->method('getValue')
@@ -247,7 +250,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte
]
);
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn(false);
$this->shipmentResourceMock->expects($this->once())
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Invoice/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Invoice/Sender/EmailSenderTest.php
index 8a4e2920ba207..dcf689cf7d53b 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/Invoice/Sender/EmailSenderTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Invoice/Sender/EmailSenderTest.php
@@ -247,7 +247,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending
$this->invoiceMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with($emailSendingResult);
if (!$configValue || $forceSyncMode) {
$transport = [
@@ -277,7 +277,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending
->method('setTemplateVars')
->with($transport->getData());
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn($emailSendingResult);
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php
index 30b584b8c4ebf..9d0f10a30e6ef 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/PaymentTest.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Sales\Test\Unit\Model\Order;
use Magento\Framework\Model\Context;
@@ -1526,7 +1527,7 @@ public function testRefund()
$this->orderStateResolver->expects($this->once())->method('getStateForOrder')
->with($this->order)
->willReturn(Order::STATE_CLOSED);
- $this->mockGetDefaultStatus(Order::STATE_CLOSED, $status);
+ $this->mockGetDefaultStatus(Order::STATE_CLOSED, $status, ['first, second']);
$this->assertOrderUpdated(Order::STATE_PROCESSING, $status, $message);
static::assertSame($this->payment, $this->payment->refund($this->creditMemoMock));
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php
index 94347e8b32d54..391e99ba6f835 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php
@@ -249,7 +249,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending
$this->shipmentMock->expects($this->once())
->method('setSendEmail')
- ->with(true);
+ ->with($emailSendingResult);
if (!$configValue || $forceSyncMode) {
$transport = [
@@ -279,7 +279,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending
->method('setTemplateVars')
->with($transport->getData());
- $this->identityContainerMock->expects($this->once())
+ $this->identityContainerMock->expects($this->exactly(2))
->method('isEnabled')
->willReturn($emailSendingResult);
diff --git a/app/code/Magento/Sales/Test/Unit/Model/RefundOrderTest.php b/app/code/Magento/Sales/Test/Unit/Model/RefundOrderTest.php
index c95b56d81d6f4..1ffeaa053cc2e 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/RefundOrderTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/RefundOrderTest.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Sales\Test\Unit\Model;
use Magento\Framework\App\ResourceConnection;
@@ -245,9 +246,9 @@ public function testOrderCreditmemo($orderId, $notify, $appendComment)
->method('setState')
->with(Order::STATE_CLOSED)
->willReturnSelf();
- $this->orderMock->expects($this->once())
- ->method('getState')
- ->willReturn(Order::STATE_CLOSED);
+ $this->configMock->expects($this->once())
+ ->method('getStateStatuses')
+ ->willReturn(['first, second']);
$this->configMock->expects($this->once())
->method('getStateDefaultStatus')
->with(Order::STATE_CLOSED)
diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/data.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/data.phtml
index 170fea937348d..17d43266ba524 100644
--- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/data.phtml
+++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/data.phtml
@@ -10,7 +10,7 @@
@@ -35,7 +35,7 @@
- = /* @escapeNotVerified */ __('Address Information') ?>
+ = $block->escapeHtml(__('Address Information')) ?>
@@ -69,11 +69,11 @@
- = /* @escapeNotVerified */ __('Order Total') ?>
+ = $block->escapeHtml(__('Order Total')) ?>
@@ -88,15 +88,15 @@
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less
index 5698afdaac7ae..66c9086c15aa7 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less
@@ -81,7 +81,7 @@
min-width: 0;
padding: 0;
- // Filedset section
+ // Fieldset section
.fieldset-wrapper {
&.admin__fieldset-section {
> .fieldset-wrapper-title {
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less
index 5d9bf80ce2255..71f57b765ff0e 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less
@@ -470,8 +470,6 @@ label.mage-error {
}
.admin__data-grid-header-row {
- &:extend(.abs-cleafix);
-
.action-select-multiselect {
-webkit-appearance: menulist-button;
appearance: menulist-button;
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_field-tooltips.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_field-tooltips.less
old mode 100644
new mode 100755
index 8184a5c4bb248..befd27fa57df6
--- a/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_field-tooltips.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_field-tooltips.less
@@ -22,7 +22,7 @@
@field-tooltip-content__width: 32rem;
@field-tooltip-content__z-index: 1;
-@field-tooltip-action__margin-left: 2rem;
+@field-tooltip-action__margin-left: 0;
//
// Form Fields
diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less
index d3b314836ae8e..299c138832064 100644
--- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less
+++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less
@@ -507,17 +507,6 @@
min-height: inherit;
}
}
-
- //
- // Category page 1 column layout
- // ---------------------------------------------
-
- .catalog-category-view.page-layout-1column {
- .column.main {
- min-height: inherit;
- }
- }
-
}
//
diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less
index 664726ddfd798..423923d5e6457 100644
--- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less
+++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less
@@ -55,6 +55,10 @@
}
}
+ .label {
+ .lib-visually-hidden();
+ }
+
.field-tooltip-action {
.lib-icon-font(
@checkout-tooltip-icon__content,
@@ -173,10 +177,10 @@
width: 0;
}
.field-tooltip .field-tooltip-content::before {
- border-bottom-color: @color-gray40;
+ .lib-css(border-bottom-color, @checkout-tooltip-content__border-color);
}
.field-tooltip .field-tooltip-content::after {
- border-bottom-color: @color-gray-light01;
+ .lib-css(border-bottom-color, @checkout-tooltip-content__background-color);
top: 1px;
}
}
diff --git a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less
index 215d7d8b322b4..b189d4e08ba17 100644
--- a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less
+++ b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less
@@ -68,8 +68,10 @@
}
// Remove address and phone number link color on iOS
-.address-details a {
- &:extend(.no-link a);
+.email-non-inline() {
+ .address-details a {
+ &:extend(.no-link a);
+ }
}
.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__xs) {
diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less
index 3f19d1020bab9..31c128e07e3a6 100644
--- a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less
+++ b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less
@@ -76,8 +76,10 @@
}
// Remove address and phone number link color on iOS
-.address-details a {
- &:extend(.no-link a);
+.email-non-inline() {
+ .address-details a {
+ &:extend(.no-link a);
+ }
}
.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__xs) {
diff --git a/app/design/frontend/Magento/luma/Magento_Theme/layout/default_head_blocks.xml b/app/design/frontend/Magento/luma/Magento_Theme/layout/default_head_blocks.xml
index 2dfb1d3ee6bf0..7e64e5f3f01cd 100644
--- a/app/design/frontend/Magento/luma/Magento_Theme/layout/default_head_blocks.xml
+++ b/app/design/frontend/Magento/luma/Magento_Theme/layout/default_head_blocks.xml
@@ -7,6 +7,6 @@
-->
-
+
diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
index 1f1ea93d0b54a..dfcc51e0a0a26 100644
--- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
+++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less
@@ -112,6 +112,10 @@
font-size: @font-size__base;
margin: 0 0 0 15px;
+ &.customer-welcome {
+ margin: 0 0 0 5px;
+ }
+
> a {
.lib-link(
@_link-color: @header-panel__text-color,
diff --git a/app/etc/di.xml b/app/etc/di.xml
index 47e7e419e3b50..476285878650b 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -1740,7 +1740,7 @@
- \Magento\Framework\App\Action\HttpOptionsActionInterface
- \Magento\Framework\App\Action\HttpGetActionInterface
- - \Magento\Framework\App\Action\HttpHeadActionInterface
+ - \Magento\Framework\App\Action\HttpGetActionInterface
- \Magento\Framework\App\Action\HttpPostActionInterface
- \Magento\Framework\App\Action\HttpPutActionInterface
- \Magento\Framework\App\Action\HttpPatchActionInterface
diff --git a/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/etc/extension_attributes.xml b/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/etc/extension_attributes.xml
index 3bd64a7aff728..8254a9d8e92cf 100644
--- a/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/etc/extension_attributes.xml
+++ b/dev/tests/api-functional/_files/Magento/TestModuleJoinDirectives/etc/extension_attributes.xml
@@ -31,4 +31,16 @@
+
+
+
+ firstname
+ lastname
+ email
+
+
+
diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php
index 4608b255459b6..769abadf20585 100644
--- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php
@@ -51,11 +51,13 @@ protected function getLinkData()
'link_type' => 'file',
'link_file_content' => [
'name' => 'link1_content.jpg',
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
'file_data' => base64_encode(file_get_contents($this->testImagePath)),
],
'sample_type' => 'file',
'sample_file_content' => [
'name' => 'link1_sample.jpg',
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
'file_data' => base64_encode(file_get_contents($this->testImagePath)),
],
],
@@ -114,6 +116,7 @@ protected function getSampleData()
'sample_type' => 'file',
'sample_file_content' => [
'name' => 'sample2.jpg',
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
'file_data' => base64_encode(file_get_contents($this->testImagePath)),
],
],
@@ -146,7 +149,9 @@ protected function createDownloadableProduct()
"price" => 10,
'attribute_set_id' => 4,
"extension_attributes" => [
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
"downloadable_product_links" => array_values($this->getLinkData()),
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
"downloadable_product_samples" => array_values($this->getSampleData()),
],
];
@@ -301,11 +306,13 @@ public function testUpdateDownloadableProductLinksWithNewFile()
'link_type' => 'file',
'link_file_content' => [
'name' => $linkFile . $extension,
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
'file_data' => base64_encode(file_get_contents($this->testImagePath)),
],
'sample_type' => 'file',
'sample_file_content' => [
'name' => $sampleFile . $extension,
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
'file_data' => base64_encode(file_get_contents($this->testImagePath)),
],
];
@@ -319,11 +326,13 @@ public function testUpdateDownloadableProductLinksWithNewFile()
'link_type' => 'file',
'link_file_content' => [
'name' => 'link2_content.jpg',
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
'file_data' => base64_encode(file_get_contents($this->testImagePath)),
],
'sample_type' => 'file',
'sample_file_content' => [
'name' => 'link2_sample.jpg',
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
'file_data' => base64_encode(file_get_contents($this->testImagePath)),
],
];
@@ -463,6 +472,7 @@ public function testUpdateDownloadableProductSamplesWithNewFile()
'sample_type' => 'file',
'sample_file_content' => [
'name' => 'sample1.jpg',
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
'file_data' => base64_encode(file_get_contents($this->testImagePath)),
],
];
@@ -472,6 +482,11 @@ public function testUpdateDownloadableProductSamplesWithNewFile()
'title' => 'sample2_updated',
'sort_order' => 2,
'sample_type' => 'file',
+ 'sample_file_content' => [
+ 'name' => 'sample2.jpg',
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+ ],
];
$response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"] =
@@ -606,7 +621,7 @@ protected function deleteProductBySku($productSku)
protected function saveProduct($product)
{
if (isset($product['custom_attributes'])) {
- for ($i=0; $icustomerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
+ $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testAddSimpleProductToCart()
+ {
+ $sku = 'simple_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+
+ self::assertArrayHasKey('cart', $response['addSimpleProductsToCart']);
+ self::assertEquals($qty, $response['addSimpleProductsToCart']['cart']['items'][0]['qty']);
+ self::assertEquals($sku, $response['addSimpleProductsToCart']['cart']['items'][0]['product']['sku']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id"
+ */
+ public function testAddProductToNonExistentCart()
+ {
+ $sku = 'simple_product';
+ $qty = 2;
+ $maskedQuoteId = 'non_existent_masked_id';
+
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a product with SKU "simple_product"
+ */
+ public function testNonExistentProductToCart()
+ {
+ $sku = 'simple_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ */
+ public function testAddSimpleProductToGuestCart()
+ {
+ $sku = 'simple_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/three_customers.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testAddSimpleProductToAnotherCustomerCart()
+ {
+ $sku = 'simple_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com'));
+ }
+
+ /**
+ * @param string $maskedQuoteId
+ * @param string $sku
+ * @param int $qty
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId, string $sku, int $qty): string
+ {
+ return <<customerTokenService->createCustomerAccessToken($username, $password);
+ $headerMap = ['Authorization' => 'Bearer ' . $customerToken];
+ return $headerMap;
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php
new file mode 100644
index 0000000000000..4ec25bb030079
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php
@@ -0,0 +1,179 @@
+customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
+ $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/virtual_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testAddVirtualProductToCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+
+ self::assertArrayHasKey('cart', $response['addVirtualProductsToCart']);
+ self::assertEquals($qty, $response['addVirtualProductsToCart']['cart']['items'][0]['qty']);
+ self::assertEquals($sku, $response['addVirtualProductsToCart']['cart']['items'][0]['product']['sku']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/virtual_product.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id"
+ */
+ public function testAddVirtualToNonExistentCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 2;
+ $nonExistentMaskedQuoteId = 'non_existent_masked_id';
+
+ $query = $this->getQuery($nonExistentMaskedQuoteId, $sku, $qty);
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a product with SKU "virtual_product"
+ */
+ public function testNonExistentProductToCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/virtual_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ */
+ public function testAddVirtualProductToGuestCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/three_customers.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/virtual_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testAddVirtualProductToAnotherCustomerCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com'));
+ }
+
+ /**
+ * @param string $maskedQuoteId
+ * @param string $sku
+ * @param int $qty
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId, string $sku, int $qty): string
+ {
+ return <<customerTokenService->createCustomerAccessToken($username, $password);
+ $headerMap = ['Authorization' => 'Bearer ' . $customerToken];
+ return $headerMap;
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CartTotalsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CartTotalsTest.php
new file mode 100644
index 0000000000000..bb8acfce629ff
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CartTotalsTest.php
@@ -0,0 +1,208 @@
+getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/apply_tax_for_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ */
+ public function testGetCartTotalsWithTaxApplied()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap());
+
+ self::assertArrayHasKey('prices', $response['cart']);
+ $pricesResponse = $response['cart']['prices'];
+ self::assertEquals(21.5, $pricesResponse['grand_total']['value']);
+ self::assertEquals(21.5, $pricesResponse['subtotal_including_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_excluding_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_with_discount_excluding_tax']['value']);
+
+ $appliedTaxesResponse = $pricesResponse['applied_taxes'];
+
+ self::assertEquals('US-TEST-*-Rate-1', $appliedTaxesResponse[0]['label']);
+ self::assertEquals(1.5, $appliedTaxesResponse[0]['amount']['value']);
+ self::assertEquals('USD', $appliedTaxesResponse[0]['amount']['currency']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ */
+ public function testGetTotalsWithNoTaxApplied()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap());
+
+ $pricesResponse = $response['cart']['prices'];
+ self::assertEquals(20, $pricesResponse['grand_total']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_including_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_excluding_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_with_discount_excluding_tax']['value']);
+ self::assertEmpty($pricesResponse['applied_taxes']);
+ }
+
+ /**
+ * The totals calculation is based on quote address.
+ * But the totals should be calculated even if no address is set
+ *
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ */
+ public function testGetCartTotalsWithNoAddressSet()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap());
+
+ $pricesResponse = $response['cart']['prices'];
+ self::assertEquals(20, $pricesResponse['grand_total']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_including_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_excluding_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_with_discount_excluding_tax']['value']);
+ self::assertEmpty($pricesResponse['applied_taxes']);
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/apply_tax_for_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ */
+ public function testGetTotalsFromGuestCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+ $this->graphQlQuery($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/three_customers.php
+ * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/apply_tax_for_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ */
+ public function testGetTotalsFromAnotherCustomerCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+ $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer3@search.example.com'));
+ }
+
+ /**
+ * Generates GraphQl query for retrieving cart totals
+ *
+ * @param string $maskedQuoteId
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId): string
+ {
+ return <<customerTokenService->createCustomerAccessToken($username, $password);
+ $headerMap = ['Authorization' => 'Bearer ' . $customerToken];
+ return $headerMap;
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CheckoutEndToEndTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CheckoutEndToEndTest.php
new file mode 100644
index 0000000000000..8592a986c5dce
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CheckoutEndToEndTest.php
@@ -0,0 +1,530 @@
+registry = $objectManager->get(Registry::class);
+ $this->quoteCollectionFactory = $objectManager->get(QuoteCollectionFactory::class);
+ $this->quoteResource = $objectManager->get(QuoteResource::class);
+ $this->quoteIdMaskFactory = $objectManager->get(QuoteIdMaskFactory::class);
+ $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
+ $this->orderCollectionFactory = $objectManager->get(CollectionFactory::class);
+ $this->orderRepository = $objectManager->get(OrderRepositoryInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_attribute.php
+ */
+ public function testCheckoutWorkflow()
+ {
+ $qty = 2;
+
+ $this->createCustomer();
+ $token = $this->loginCustomer();
+ $this->headers = ['Authorization' => 'Bearer ' . $token];
+
+ $sku = $this->findProduct();
+ $cartId = $this->createEmptyCart();
+ $this->addProductToCart($cartId, $qty, $sku);
+
+ $this->setBillingAddress($cartId);
+ $shippingAddress = $this->setShippingAddress($cartId);
+
+ $shippingMethod = current($shippingAddress['available_shipping_methods']);
+ $paymentMethod = $this->setShippingMethod($cartId, $shippingAddress['address_id'], $shippingMethod);
+ $this->setPaymentMethod($cartId, $paymentMethod);
+
+ $orderId = $this->placeOrder($cartId);
+ $this->checkOrderInHistory($orderId);
+ }
+
+ /**
+ * @return void
+ */
+ private function createCustomer(): void
+ {
+ $query = <<graphQlMutation($query);
+ }
+
+ /**
+ * @return string
+ */
+ private function loginCustomer(): string
+ {
+ $query = <<graphQlMutation($query);
+ self::assertArrayHasKey('generateCustomerToken', $response);
+ self::assertArrayHasKey('token', $response['generateCustomerToken']);
+ self::assertNotEmpty($response['generateCustomerToken']['token']);
+
+ return $response['generateCustomerToken']['token'];
+ }
+
+ /**
+ * @return string
+ */
+ private function findProduct(): string
+ {
+ $query = <<graphQlQuery($query);
+ self::assertArrayHasKey('products', $response);
+ self::assertArrayHasKey('items', $response['products']);
+ self::assertCount(1, $response['products']['items']);
+
+ $product = current($response['products']['items']);
+ self::assertArrayHasKey('sku', $product);
+ self::assertNotEmpty($product['sku']);
+
+ return $product['sku'];
+ }
+
+ /**
+ * @return string
+ */
+ private function createEmptyCart(): string
+ {
+ $query = <<graphQlMutation($query, [], '', $this->headers);
+ self::assertArrayHasKey('createEmptyCart', $response);
+ self::assertNotEmpty($response['createEmptyCart']);
+
+ return $response['createEmptyCart'];
+ }
+
+ /**
+ * @param string $cartId
+ * @param float $qty
+ * @param string $sku
+ * @return void
+ */
+ private function addProductToCart(string $cartId, float $qty, string $sku): void
+ {
+ $query = <<graphQlMutation($query, [], '', $this->headers);
+ }
+
+ /**
+ * @param string $cartId
+ * @param array $auth
+ * @return array
+ */
+ private function setBillingAddress(string $cartId): void
+ {
+ $query = <<graphQlMutation($query, [], '', $this->headers);
+ }
+
+ /**
+ * @param string $cartId
+ * @return array
+ */
+ private function setShippingAddress(string $cartId): array
+ {
+ $query = <<graphQlMutation($query, [], '', $this->headers);
+ self::assertArrayHasKey('setShippingAddressesOnCart', $response);
+ self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']);
+ self::assertArrayHasKey('shipping_addresses', $response['setShippingAddressesOnCart']['cart']);
+ self::assertCount(1, $response['setShippingAddressesOnCart']['cart']['shipping_addresses']);
+
+ $shippingAddress = current($response['setShippingAddressesOnCart']['cart']['shipping_addresses']);
+ self::assertArrayHasKey('address_id', $shippingAddress);
+ self::assertNotEmpty($shippingAddress['address_id']);
+ self::assertArrayHasKey('available_shipping_methods', $shippingAddress);
+ self::assertCount(1, $shippingAddress['available_shipping_methods']);
+
+ $availableShippingMethod = current($shippingAddress['available_shipping_methods']);
+ self::assertArrayHasKey('carrier_code', $availableShippingMethod);
+ self::assertNotEmpty($availableShippingMethod['carrier_code']);
+
+ self::assertArrayHasKey('method_code', $availableShippingMethod);
+ self::assertNotEmpty($availableShippingMethod['method_code']);
+
+ self::assertArrayHasKey('amount', $availableShippingMethod);
+ self::assertNotEmpty($availableShippingMethod['amount']);
+
+ return $shippingAddress;
+ }
+
+ /**
+ * @param string $cartId
+ * @param int $addressId
+ * @param array $method
+ * @return array
+ */
+ private function setShippingMethod(string $cartId, int $addressId, array $method): array
+ {
+ $query = <<graphQlMutation($query, [], '', $this->headers);
+ self::assertArrayHasKey('setShippingMethodsOnCart', $response);
+ self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']);
+ self::assertArrayHasKey('available_payment_methods', $response['setShippingMethodsOnCart']['cart']);
+ self::assertCount(1, $response['setShippingMethodsOnCart']['cart']['available_payment_methods']);
+
+ $availablePaymentMethod = current($response['setShippingMethodsOnCart']['cart']['available_payment_methods']);
+ self::assertArrayHasKey('code', $availablePaymentMethod);
+ self::assertNotEmpty($availablePaymentMethod['code']);
+ self::assertArrayHasKey('title', $availablePaymentMethod);
+ self::assertNotEmpty($availablePaymentMethod['title']);
+
+ return $availablePaymentMethod;
+ }
+
+ /**
+ * @param string $cartId
+ * @param array $method
+ * @return void
+ */
+ private function setPaymentMethod(string $cartId, array $method): void
+ {
+ $query = <<graphQlMutation($query, [], '', $this->headers);
+ }
+
+ /**
+ * @param string $cartId
+ * @return string
+ */
+ private function placeOrder(string $cartId): string
+ {
+ $query = <<graphQlMutation($query, [], '', $this->headers);
+ self::assertArrayHasKey('placeOrder', $response);
+ self::assertArrayHasKey('order', $response['placeOrder']);
+ self::assertArrayHasKey('order_id', $response['placeOrder']['order']);
+ self::assertNotEmpty($response['placeOrder']['order']['order_id']);
+
+ return $response['placeOrder']['order']['order_id'];
+ }
+
+ /**
+ * @param string $orderId
+ * @return void
+ */
+ private function checkOrderInHistory(string $orderId): void
+ {
+ $query = <<graphQlQuery($query, [], '', $this->headers);
+ self::assertArrayHasKey('customerOrders', $response);
+ self::assertArrayHasKey('items', $response['customerOrders']);
+ self::assertCount(1, $response['customerOrders']['items']);
+
+ $order = current($response['customerOrders']['items']);
+ self::assertArrayHasKey('increment_id', $order);
+ self::assertEquals($orderId, $order['increment_id']);
+
+ self::assertArrayHasKey('grand_total', $order);
+ }
+
+ public function tearDown()
+ {
+ $this->deleteCustomer();
+ $this->deleteQuote();
+ $this->deleteOrder();
+ parent::tearDown();
+ }
+
+ /**
+ * @return void
+ */
+ private function deleteCustomer(): void
+ {
+ $email = 'customer@example.com';
+ try {
+ $customer = $this->customerRepository->get($email);
+ } catch (\Exception $exception) {
+ return;
+ }
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', true);
+ $this->customerRepository->delete($customer);
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', false);
+ }
+
+ /**
+ * @return void
+ */
+ private function deleteQuote(): void
+ {
+ $quoteCollection = $this->quoteCollectionFactory->create();
+ foreach ($quoteCollection as $quote) {
+ $this->quoteResource->delete($quote);
+
+ $quoteIdMask = $this->quoteIdMaskFactory->create();
+ $quoteIdMask->setQuoteId($quote->getId())
+ ->delete();
+ }
+ }
+
+ /**
+ * @return void
+ */
+ private function deleteOrder()
+ {
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', true);
+
+ $orderCollection = $this->orderCollectionFactory->create();
+ foreach ($orderCollection as $order) {
+ $this->orderRepository->delete($order);
+ }
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', false);
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php
index fc66e4647e576..fbd0cf19740d7 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php
@@ -8,9 +8,8 @@
namespace Magento\GraphQl\Quote\Customer;
use Magento\Integration\Api\CustomerTokenServiceInterface;
-use Magento\Quote\Model\QuoteFactory;
-use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
use Magento\Quote\Model\QuoteIdMaskFactory;
+use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory;
use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -32,38 +31,27 @@ class CreateEmptyCartTest extends GraphQlAbstract
private $customerTokenService;
/**
- * @var QuoteResource
- */
- private $quoteResource;
-
- /**
- * @var QuoteFactory
+ * @var QuoteCollectionFactory
*/
- private $quoteFactory;
+ private $quoteCollectionFactory;
/**
- * @var MaskedQuoteIdToQuoteIdInterface
+ * @var QuoteResource
*/
- private $maskedQuoteIdToQuoteId;
+ private $quoteResource;
/**
* @var QuoteIdMaskFactory
*/
private $quoteIdMaskFactory;
- /**
- * @var string
- */
- private $maskedQuoteId;
-
protected function setUp()
{
$objectManager = Bootstrap::getObjectManager();
+ $this->quoteCollectionFactory = $objectManager->get(QuoteCollectionFactory::class);
$this->guestCartRepository = $objectManager->get(GuestCartRepositoryInterface::class);
$this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
$this->quoteResource = $objectManager->get(QuoteResource::class);
- $this->quoteFactory = $objectManager->get(QuoteFactory::class);
- $this->maskedQuoteIdToQuoteId = $objectManager->get(MaskedQuoteIdToQuoteIdInterface::class);
$this->quoteIdMaskFactory = $objectManager->get(QuoteIdMaskFactory::class);
}
@@ -79,7 +67,6 @@ public function testCreateEmptyCart()
self::assertNotEmpty($response['createEmptyCart']);
$guestCart = $this->guestCartRepository->get($response['createEmptyCart']);
- $this->maskedQuoteId = $response['createEmptyCart'];
self::assertNotNull($guestCart->getId());
self::assertEquals(1, $guestCart->getCustomer()->getId());
@@ -103,13 +90,71 @@ public function testCreateEmptyCartWithNotDefaultStore()
/* guestCartRepository is used for registered customer to get the cart hash */
$guestCart = $this->guestCartRepository->get($response['createEmptyCart']);
- $this->maskedQuoteId = $response['createEmptyCart'];
self::assertNotNull($guestCart->getId());
self::assertEquals(1, $guestCart->getCustomer()->getId());
self::assertEquals('fixture_second_store', $guestCart->getStore()->getCode());
}
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testCreateEmptyCartWithPredefinedCartId()
+ {
+ $predefinedCartId = '572cda51902b5b517c0e1a2b2fd004b4';
+
+ $query = <<graphQlMutation($query, [], '', $this->getHeaderMapWithCustomerToken());
+
+ self::assertArrayHasKey('createEmptyCart', $response);
+ self::assertEquals($predefinedCartId, $response['createEmptyCart']);
+
+ $guestCart = $this->guestCartRepository->get($response['createEmptyCart']);
+ self::assertNotNull($guestCart->getId());
+ self::assertEquals(1, $guestCart->getCustomer()->getId());
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Cart with ID "572cda51902b5b517c0e1a2b2fd004b4" already exists.
+ */
+ public function testCreateEmptyCartIfPredefinedCartIdAlreadyExists()
+ {
+ $predefinedCartId = '572cda51902b5b517c0e1a2b2fd004b4';
+
+ $query = <<graphQlMutation($query, [], '', $this->getHeaderMapWithCustomerToken());
+ $this->graphQlMutation($query, [], '', $this->getHeaderMapWithCustomerToken());
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Cart ID length should to be 32 symbols.
+ */
+ public function testCreateEmptyCartWithWrongPredefinedCartId()
+ {
+ $predefinedCartId = '572';
+
+ $query = <<graphQlMutation($query, [], '', $this->getHeaderMapWithCustomerToken());
+ }
+
/**
* @return string
*/
@@ -138,15 +183,12 @@ private function getHeaderMapWithCustomerToken(
public function tearDown()
{
- if (null !== $this->maskedQuoteId) {
- $quoteId = $this->maskedQuoteIdToQuoteId->execute($this->maskedQuoteId);
-
- $quote = $this->quoteFactory->create();
- $this->quoteResource->load($quote, $quoteId);
+ $quoteCollection = $this->quoteCollectionFactory->create();
+ foreach ($quoteCollection as $quote) {
$this->quoteResource->delete($quote);
$quoteIdMask = $this->quoteIdMaskFactory->create();
- $quoteIdMask->setQuoteId($quoteId)
+ $quoteIdMask->setQuoteId($quote->getId())
->delete();
}
parent::tearDown();
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartEmailTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartEmailTest.php
new file mode 100644
index 0000000000000..951fe08db5e3d
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartEmailTest.php
@@ -0,0 +1,125 @@
+getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testGetCartEmail()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap());
+
+ $this->assertArrayHasKey('cart', $response);
+ $this->assertArrayHasKey('email', $response['cart']);
+ $this->assertEquals('customer@example.com', $response['cart']['email']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id"
+ */
+ public function testGetCartEmailFromNonExistentCart()
+ {
+ $maskedQuoteId = 'non_existent_masked_id';
+ $query = $this->getQuery($maskedQuoteId);
+
+ $this->graphQlQuery($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ */
+ public function testGetEmailFromGuestCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"{$maskedQuoteId}\""
+ );
+ $this->graphQlQuery($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/three_customers.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testGetEmailFromAnotherCustomerCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"{$maskedQuoteId}\""
+ );
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer3@search.example.com'));
+ }
+
+ /**
+ * @param string $maskedQuoteId
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId): string
+ {
+ return <<customerTokenService->createCustomerAccessToken($username, $password);
+ $headerMap = ['Authorization' => 'Bearer ' . $customerToken];
+ return $headerMap;
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php
index b7d3b546ba194..47d0d661fb33c 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php
@@ -7,7 +7,6 @@
namespace Magento\GraphQl\Quote\Customer;
-use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Registry;
use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId;
use Magento\Integration\Api\CustomerTokenServiceInterface;
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php
index 553408880baa0..6b15f947a2477 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php
@@ -493,6 +493,24 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st
$this->graphQlMutation($query);
}
+ /**
+ * @return array
+ */
+ public function dataProviderSetWithoutRequiredParameters(): array
+ {
+ return [
+ 'missed_billing_address' => [
+ 'cart_id: "cart_id_value"',
+ 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!'
+ . ' was not provided.',
+ ],
+ 'missed_cart_id' => [
+ 'billing_address: {}',
+ 'Required parameter "cart_id" is missing'
+ ]
+ ];
+ }
+
/**
* @magentoApiDataFixture Magento/Customer/_files/customer.php
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
@@ -536,24 +554,6 @@ public function testSetNewBillingAddressWithRedundantStreetLine()
$this->graphQlMutation($query, [], '', $this->getHeaderMap());
}
- /**
- * @return array
- */
- public function dataProviderSetWithoutRequiredParameters(): array
- {
- return [
- 'missed_billing_address' => [
- 'cart_id: "cart_id_value"',
- 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!'
- . ' was not provided.',
- ],
- 'missed_cart_id' => [
- 'billing_address: {}',
- 'Required parameter "cart_id" is missing'
- ]
- ];
- }
-
/**
* Verify the all the whitelisted fields for a New Address Object
*
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetGuestEmailOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetGuestEmailOnCartTest.php
new file mode 100644
index 0000000000000..a4a84c2f8c740
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetGuestEmailOnCartTest.php
@@ -0,0 +1,104 @@
+getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage The request is not allowed for logged in customers
+ */
+ public function testSetGuestEmailOnCartForLoggedInCustomer()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $email = 'some@user.com';
+
+ $query = $this->getQuery($maskedQuoteId, $email);
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage The request is not allowed for logged in customers
+ */
+ public function testSetGuestEmailOnGuestCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $email = 'some@user.com';
+
+ $query = $this->getQuery($maskedQuoteId, $email);
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ }
+
+ /**
+ * Returns GraphQl mutation query for setting email address for a guest
+ *
+ * @param string $maskedQuoteId
+ * @param string $email
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId, string $email): string
+ {
+ return <<customerTokenService->createCustomerAccessToken($username, $password);
+ $headerMap = ['Authorization' => 'Bearer ' . $customerToken];
+ return $headerMap;
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php
index 25221b628e7fb..9e0693b160851 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php
@@ -31,14 +31,14 @@ protected function setUp()
}
/**
- * @magentoApiDataFixture Magento/Catalog/_files/products.php
- * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
*/
public function testAddSimpleProductToCart()
{
- $sku = 'simple';
+ $sku = 'simple_product';
$qty = 2;
- $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1');
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
$query = $this->getQuery($maskedQuoteId, $sku, $qty);
$response = $this->graphQlMutation($query);
@@ -49,14 +49,14 @@ public function testAddSimpleProductToCart()
}
/**
- * @magentoApiDataFixture Magento/Catalog/_files/products.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
*
* @expectedException \Exception
* @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id"
*/
public function testAddProductToNonExistentCart()
{
- $sku = 'simple';
+ $sku = 'simple_product';
$qty = 1;
$maskedQuoteId = 'non_existent_masked_id';
@@ -64,6 +64,41 @@ public function testAddProductToNonExistentCart()
$this->graphQlMutation($query);
}
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a product with SKU "simple_product"
+ */
+ public function testNonExistentProductToCart()
+ {
+ $sku = 'simple_product';
+ $qty = 1;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testAddSimpleProductToCustomerCart()
+ {
+ $sku = 'simple_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+
+ $this->graphQlMutation($query);
+ }
+
/**
* @param string $maskedQuoteId
* @param string $sku
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php
new file mode 100644
index 0000000000000..3f2d734635c3e
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php
@@ -0,0 +1,138 @@
+getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/virtual_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ */
+ public function testAddVirtualProductToCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $response = $this->graphQlMutation($query);
+
+ self::assertArrayHasKey('cart', $response['addVirtualProductsToCart']);
+ self::assertEquals($qty, $response['addVirtualProductsToCart']['cart']['items'][0]['qty']);
+ self::assertEquals($sku, $response['addVirtualProductsToCart']['cart']['items'][0]['product']['sku']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/virtual_product.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id"
+ */
+ public function testAddVirtualToNonExistentCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 1;
+ $maskedQuoteId = 'non_existent_masked_id';
+
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a product with SKU "virtual_product"
+ */
+ public function testNonExistentProductToCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 1;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/virtual_product.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testAddVirtualProductToCustomerCart()
+ {
+ $sku = 'virtual_product';
+ $qty = 2;
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId, $sku, $qty);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @param string $maskedQuoteId
+ * @param string $sku
+ * @param int $qty
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId, string $sku, int $qty): string
+ {
+ return <<getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/apply_tax_for_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ */
+ public function testGetCartTotalsWithTaxApplied()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlQuery($query);
+
+ self::assertArrayHasKey('prices', $response['cart']);
+ $pricesResponse = $response['cart']['prices'];
+ self::assertEquals(21.5, $pricesResponse['grand_total']['value']);
+ self::assertEquals(21.5, $pricesResponse['subtotal_including_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_excluding_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_with_discount_excluding_tax']['value']);
+
+ $appliedTaxesResponse = $pricesResponse['applied_taxes'];
+
+ self::assertEquals('US-TEST-*-Rate-1', $appliedTaxesResponse[0]['label']);
+ self::assertEquals(1.5, $appliedTaxesResponse[0]['amount']['value']);
+ self::assertEquals('USD', $appliedTaxesResponse[0]['amount']['currency']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ */
+ public function testGetTotalsWithNoTaxApplied()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlQuery($query);
+
+ $pricesResponse = $response['cart']['prices'];
+ self::assertEquals(20, $pricesResponse['grand_total']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_including_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_excluding_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_with_discount_excluding_tax']['value']);
+ self::assertEmpty($pricesResponse['applied_taxes']);
+ }
+
+ /**
+ * The totals calculation is based on quote address.
+ * But the totals should be calculated even if no address is set
+ *
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @group recent
+ */
+ public function testGetCartTotalsWithNoAddressSet()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlQuery($query);
+
+ $pricesResponse = $response['cart']['prices'];
+ self::assertEquals(20, $pricesResponse['grand_total']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_including_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_excluding_tax']['value']);
+ self::assertEquals(20, $pricesResponse['subtotal_with_discount_excluding_tax']['value']);
+ self::assertEmpty($pricesResponse['applied_taxes']);
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/apply_tax_for_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ */
+ public function testGetSelectedShippingMethodFromCustomerCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+ $this->graphQlQuery($query);
+ }
+
+ /**
+ * Generates GraphQl query for retrieving cart totals
+ *
+ * @param string $maskedQuoteId
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId): string
+ {
+ return <<registry = $objectManager->get(Registry::class);
+ $this->quoteCollectionFactory = $objectManager->get(QuoteCollectionFactory::class);
+ $this->quoteResource = $objectManager->get(QuoteResource::class);
+ $this->quoteIdMaskFactory = $objectManager->get(QuoteIdMaskFactory::class);
+ $this->orderCollectionFactory = $objectManager->get(CollectionFactory::class);
+ $this->orderRepository = $objectManager->get(OrderRepositoryInterface::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Catalog/_files/products_with_layered_navigation_attribute.php
+ */
+ public function testCheckoutWorkflow()
+ {
+ $qty = 2;
+
+ $sku = $this->findProduct();
+ $cartId = $this->createEmptyCart();
+ $this->setGuestEmailOnCart($cartId);
+ $this->addProductToCart($cartId, $qty, $sku);
+
+ $this->setBillingAddress($cartId);
+ $shippingAddress = $this->setShippingAddress($cartId);
+
+ $shippingMethod = current($shippingAddress['available_shipping_methods']);
+ $paymentMethod = $this->setShippingMethod($cartId, $shippingAddress['address_id'], $shippingMethod);
+ $this->setPaymentMethod($cartId, $paymentMethod);
+
+ $this->placeOrder($cartId);
+ }
+
+ /**
+ * @return string
+ */
+ private function findProduct(): string
+ {
+ $query = <<graphQlQuery($query);
+ self::assertArrayHasKey('products', $response);
+ self::assertArrayHasKey('items', $response['products']);
+ self::assertCount(1, $response['products']['items']);
+
+ $product = current($response['products']['items']);
+ self::assertArrayHasKey('sku', $product);
+ self::assertNotEmpty($product['sku']);
+
+ return $product['sku'];
+ }
+
+ /**
+ * @return string
+ */
+ private function createEmptyCart(): string
+ {
+ $query = <<graphQlMutation($query);
+ self::assertArrayHasKey('createEmptyCart', $response);
+ self::assertNotEmpty($response['createEmptyCart']);
+
+ return $response['createEmptyCart'];
+ }
+
+ /**
+ * @param string $cartId
+ * @return void
+ */
+ private function setGuestEmailOnCart(string $cartId): void
+ {
+ $query = <<graphQlMutation($query);
+ }
+
+ /**
+ * @param string $cartId
+ * @param float $qty
+ * @param string $sku
+ * @return void
+ */
+ private function addProductToCart(string $cartId, float $qty, string $sku): void
+ {
+ $query = <<graphQlMutation($query);
+ }
+
+ /**
+ * @param string $cartId
+ * @param array $auth
+ * @return array
+ */
+ private function setBillingAddress(string $cartId): void
+ {
+ $query = <<graphQlMutation($query);
+ }
+
+ /**
+ * @param string $cartId
+ * @return array
+ */
+ private function setShippingAddress(string $cartId): array
+ {
+ $query = <<graphQlMutation($query);
+ self::assertArrayHasKey('setShippingAddressesOnCart', $response);
+ self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']);
+ self::assertArrayHasKey('shipping_addresses', $response['setShippingAddressesOnCart']['cart']);
+ self::assertCount(1, $response['setShippingAddressesOnCart']['cart']['shipping_addresses']);
+
+ $shippingAddress = current($response['setShippingAddressesOnCart']['cart']['shipping_addresses']);
+ self::assertArrayHasKey('address_id', $shippingAddress);
+ self::assertNotEmpty($shippingAddress['address_id']);
+ self::assertArrayHasKey('available_shipping_methods', $shippingAddress);
+ self::assertCount(1, $shippingAddress['available_shipping_methods']);
+
+ $availableShippingMethod = current($shippingAddress['available_shipping_methods']);
+ self::assertArrayHasKey('carrier_code', $availableShippingMethod);
+ self::assertNotEmpty($availableShippingMethod['carrier_code']);
+
+ self::assertArrayHasKey('method_code', $availableShippingMethod);
+ self::assertNotEmpty($availableShippingMethod['method_code']);
+
+ self::assertArrayHasKey('amount', $availableShippingMethod);
+ self::assertNotEmpty($availableShippingMethod['amount']);
+
+ return $shippingAddress;
+ }
+
+ /**
+ * @param string $cartId
+ * @param int $addressId
+ * @param array $method
+ * @return array
+ */
+ private function setShippingMethod(string $cartId, int $addressId, array $method): array
+ {
+ $query = <<graphQlMutation($query);
+ self::assertArrayHasKey('setShippingMethodsOnCart', $response);
+ self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']);
+ self::assertArrayHasKey('available_payment_methods', $response['setShippingMethodsOnCart']['cart']);
+ self::assertCount(1, $response['setShippingMethodsOnCart']['cart']['available_payment_methods']);
+
+ $availablePaymentMethod = current($response['setShippingMethodsOnCart']['cart']['available_payment_methods']);
+ self::assertArrayHasKey('code', $availablePaymentMethod);
+ self::assertNotEmpty($availablePaymentMethod['code']);
+ self::assertArrayHasKey('title', $availablePaymentMethod);
+ self::assertNotEmpty($availablePaymentMethod['title']);
+
+ return $availablePaymentMethod;
+ }
+
+ /**
+ * @param string $cartId
+ * @param array $method
+ * @return void
+ */
+ private function setPaymentMethod(string $cartId, array $method): void
+ {
+ $query = <<graphQlMutation($query);
+ }
+
+ /**
+ * @param string $cartId
+ * @return void
+ */
+ private function placeOrder(string $cartId): void
+ {
+ $query = <<graphQlMutation($query);
+ self::assertArrayHasKey('placeOrder', $response);
+ self::assertArrayHasKey('order', $response['placeOrder']);
+ self::assertArrayHasKey('order_id', $response['placeOrder']['order']);
+ self::assertNotEmpty($response['placeOrder']['order']['order_id']);
+ }
+
+ public function tearDown()
+ {
+ $this->deleteQuote();
+ $this->deleteOrder();
+ parent::tearDown();
+ }
+
+ /**
+ * @return void
+ */
+ private function deleteQuote(): void
+ {
+ $quoteCollection = $this->quoteCollectionFactory->create();
+ foreach ($quoteCollection as $quote) {
+ $this->quoteResource->delete($quote);
+
+ $quoteIdMask = $this->quoteIdMaskFactory->create();
+ $quoteIdMask->setQuoteId($quote->getId())
+ ->delete();
+ }
+ }
+
+ /**
+ * @return void
+ */
+ private function deleteOrder()
+ {
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', true);
+
+ $orderCollection = $this->orderCollectionFactory->create();
+ foreach ($orderCollection as $order) {
+ $this->orderRepository->delete($order);
+ }
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', false);
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
index adb2879e186b1..6ed91d21f0ae2 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
@@ -7,9 +7,8 @@
namespace Magento\GraphQl\Quote\Guest;
-use Magento\Quote\Model\QuoteFactory;
-use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
use Magento\Quote\Model\QuoteIdMaskFactory;
+use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory;
use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -26,37 +25,26 @@ class CreateEmptyCartTest extends GraphQlAbstract
private $guestCartRepository;
/**
- * @var QuoteResource
- */
- private $quoteResource;
-
- /**
- * @var QuoteFactory
+ * @var QuoteCollectionFactory
*/
- private $quoteFactory;
+ private $quoteCollectionFactory;
/**
- * @var MaskedQuoteIdToQuoteIdInterface
+ * @var QuoteResource
*/
- private $maskedQuoteIdToQuoteId;
+ private $quoteResource;
/**
* @var QuoteIdMaskFactory
*/
private $quoteIdMaskFactory;
- /**
- * @var string
- */
- private $maskedQuoteId;
-
protected function setUp()
{
$objectManager = Bootstrap::getObjectManager();
$this->guestCartRepository = $objectManager->get(GuestCartRepositoryInterface::class);
+ $this->quoteCollectionFactory = $objectManager->get(QuoteCollectionFactory::class);
$this->quoteResource = $objectManager->get(QuoteResource::class);
- $this->quoteFactory = $objectManager->get(QuoteFactory::class);
- $this->maskedQuoteIdToQuoteId = $objectManager->get(MaskedQuoteIdToQuoteIdInterface::class);
$this->quoteIdMaskFactory = $objectManager->get(QuoteIdMaskFactory::class);
}
@@ -69,7 +57,6 @@ public function testCreateEmptyCart()
self::assertNotEmpty($response['createEmptyCart']);
$guestCart = $this->guestCartRepository->get($response['createEmptyCart']);
- $this->maskedQuoteId = $response['createEmptyCart'];
self::assertNotNull($guestCart->getId());
self::assertNull($guestCart->getCustomer()->getId());
@@ -96,6 +83,65 @@ public function testCreateEmptyCartWithNotDefaultStore()
self::assertSame('fixture_second_store', $guestCart->getStore()->getCode());
}
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testCreateEmptyCartWithPredefinedCartId()
+ {
+ $predefinedCartId = '572cda51902b5b517c0e1a2b2fd004b4';
+
+ $query = <<graphQlMutation($query);
+
+ self::assertArrayHasKey('createEmptyCart', $response);
+ self::assertEquals($predefinedCartId, $response['createEmptyCart']);
+
+ $guestCart = $this->guestCartRepository->get($response['createEmptyCart']);
+ self::assertNotNull($guestCart->getId());
+ self::assertNull($guestCart->getCustomer()->getId());
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Cart with ID "572cda51902b5b517c0e1a2b2fd004b4" already exists.
+ */
+ public function testCreateEmptyCartIfPredefinedCartIdAlreadyExists()
+ {
+ $predefinedCartId = '572cda51902b5b517c0e1a2b2fd004b4';
+
+ $query = <<graphQlMutation($query);
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ *
+ * @expectedException \Exception
+ * @expectedExceptionMessage Cart ID length should to be 32 symbols.
+ */
+ public function testCreateEmptyCartWithWrongPredefinedCartId()
+ {
+ $predefinedCartId = '572';
+
+ $query = <<graphQlMutation($query);
+ }
+
/**
* @return string
*/
@@ -110,15 +156,12 @@ private function getQuery(): string
public function tearDown()
{
- if (null !== $this->maskedQuoteId) {
- $quoteId = $this->maskedQuoteIdToQuoteId->execute($this->maskedQuoteId);
-
- $quote = $this->quoteFactory->create();
- $this->quoteResource->load($quote, $quoteId);
+ $quoteCollection = $this->quoteCollectionFactory->create();
+ foreach ($quoteCollection as $quote) {
$this->quoteResource->delete($quote);
$quoteIdMask = $this->quoteIdMaskFactory->create();
- $quoteIdMask->setQuoteId($quoteId)
+ $quoteIdMask->setQuoteId($quote->getId())
->delete();
}
parent::tearDown();
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartEmailTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartEmailTest.php
new file mode 100644
index 0000000000000..8c6ecd075049f
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartEmailTest.php
@@ -0,0 +1,88 @@
+getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ */
+ public function testGetCartEmail()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlQuery($query);
+
+ $this->assertArrayHasKey('cart', $response);
+ $this->assertArrayHasKey('email', $response['cart']);
+ $this->assertEquals('guest@example.com', $response['cart']['email']);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id"
+ */
+ public function testGetCartEmailFromNonExistentCart()
+ {
+ $maskedQuoteId = 'non_existent_masked_id';
+ $query = $this->getQuery($maskedQuoteId);
+
+ $this->graphQlQuery($query);
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testGetCartEmailFromCustomerCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $query = $this->getQuery($maskedQuoteId);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"{$maskedQuoteId}\""
+ );
+ $this->graphQlQuery($query);
+ }
+
+ /**
+ * @param string $maskedQuoteId
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId): string
+ {
+ return <<getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ $this->orderCollectionFactory = $objectManager->get(CollectionFactory::class);
+ $this->orderRepository = $objectManager->get(OrderRepositoryInterface::class);
+ $this->registry = Bootstrap::getObjectManager()->get(Registry::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php
+ */
+ public function testPlaceOrder()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlMutation($query);
+
+ self::assertArrayHasKey('placeOrder', $response);
+ self::assertArrayHasKey('order_id', $response['placeOrder']['order']);
+ self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_id']);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php
+ */
+ public function testPlaceOrderWithNoEmail()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ $query = $this->getQuery($maskedQuoteId);
+
+ self::expectExceptionMessage("Guest email for cart is missing. Please enter");
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ */
+ public function testPlaceOrderWithNoItemsInCart()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ $query = $this->getQuery($maskedQuoteId);
+
+ self::expectExceptionMessage(
+ 'Unable to place order: A server error stopped your order from being placed. ' .
+ 'Please try to place your order again'
+ );
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ */
+ public function testPlaceOrderWithNoShippingAddress()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ $query = $this->getQuery($maskedQuoteId);
+
+ self::expectExceptionMessage(
+ 'Unable to place order: Some addresses can\'t be used due to the configurations for specific countries'
+ );
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ */
+ public function testPlaceOrderWithNoShippingMethod()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ $query = $this->getQuery($maskedQuoteId);
+
+ self::expectExceptionMessage(
+ 'Unable to place order: The shipping method is missing. Select the shipping method and try again'
+ );
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php
+ */
+ public function testPlaceOrderWithNoBillingAddress()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ $query = $this->getQuery($maskedQuoteId);
+
+ self::expectExceptionMessageRegExp(
+ '/Unable to place order: Please check the billing address information*/'
+ );
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php
+ */
+ public function testPlaceOrderWithNoPaymentMethod()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ $query = $this->getQuery($maskedQuoteId);
+
+ self::expectExceptionMessage('Unable to place order: Enter a valid payment method and try again');
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/set_simple_product_out_of_stock.php
+ */
+ public function testPlaceOrderWithOutOfStockProduct()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ $query = $this->getQuery($maskedQuoteId);
+
+ self::expectExceptionMessage('Unable to place order: Some of the products are out of stock');
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/three_customers.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php
+ */
+ public function testPlaceOrderOfCustomerCart()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+ $query = $this->getQuery($maskedQuoteId);
+
+ self::expectExceptionMessageRegExp('/The current user cannot perform operations on cart*/');
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @param string $maskedQuoteId
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId): string
+ {
+ return <<registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', true);
+
+ $orderCollection = $this->orderCollectionFactory->create();
+ foreach ($orderCollection as $order) {
+ $this->orderRepository->delete($order);
+ }
+ $this->registry->unregister('isSecureArea');
+ $this->registry->register('isSecureArea', false);
+
+ parent::tearDown();
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php
index 5b0e4cfdb6c52..d2d53220f0042 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php
@@ -313,6 +313,24 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st
$this->graphQlMutation($query);
}
+ /**
+ * @return array
+ */
+ public function dataProviderSetWithoutRequiredParameters(): array
+ {
+ return [
+ 'missed_billing_address' => [
+ 'cart_id: "cart_id_value"',
+ 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!'
+ . ' was not provided.',
+ ],
+ 'missed_cart_id' => [
+ 'billing_address: {}',
+ 'Required parameter "cart_id" is missing'
+ ]
+ ];
+ }
+
/**
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
* @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
@@ -356,24 +374,6 @@ public function testSetNewBillingAddressRedundantStreetLine()
$this->graphQlMutation($query);
}
- /**
- * @return array
- */
- public function dataProviderSetWithoutRequiredParameters(): array
- {
- return [
- 'missed_billing_address' => [
- 'cart_id: "cart_id_value"',
- 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!'
- . ' was not provided.',
- ],
- 'missed_cart_id' => [
- 'billing_address: {}',
- 'Required parameter "cart_id" is missing'
- ]
- ];
- }
-
/**
* Verify the all the whitelisted fields for a New Address Object
*
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php
new file mode 100644
index 0000000000000..b877dccdeba37
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php
@@ -0,0 +1,141 @@
+getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ */
+ public function testSetGuestEmailOnCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $email = 'some@user.com';
+
+ $query = $this->getQuery($maskedQuoteId, $email);
+ $response = $this->graphQlMutation($query);
+
+ $this->assertArrayHasKey('setGuestEmailOnCart', $response);
+ $this->assertArrayHasKey('cart', $response['setGuestEmailOnCart']);
+ $this->assertEquals($email, $response['setGuestEmailOnCart']['cart']['email']);
+ }
+
+ /**
+ * _security
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ */
+ public function testSetGuestEmailOnCustomerCart()
+ {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+ $email = 'some@user.com';
+
+ $query = $this->getQuery($maskedQuoteId, $email);
+
+ $this->expectExceptionMessage(
+ "The current user cannot perform operations on cart \"$maskedQuoteId\""
+ );
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ *
+ * @dataProvider incorrectEmailDataProvider
+ * @param string $email
+ * @param string $exceptionMessage
+ */
+ public function testSetGuestEmailOnCartWithIncorrectEmail(
+ string $email,
+ string $exceptionMessage
+ ) {
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote');
+
+ $query = $this->getQuery($maskedQuoteId, $email);
+ $this->expectExceptionMessage($exceptionMessage);
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @return array
+ */
+ public function incorrectEmailDataProvider(): array
+ {
+ return [
+ 'wrong_email' => ['some', 'Invalid email format'],
+ 'no_email' => ['', 'Required parameter "email" is missing'],
+ ];
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id"
+ */
+ public function testSetGuestEmailOnNonExistentCart()
+ {
+ $maskedQuoteId = 'non_existent_masked_id';
+ $email = 'some@user.com';
+
+ $query = $this->getQuery($maskedQuoteId, $email);
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Required parameter "cart_id" is missing
+ */
+ public function testSetGuestEmailWithEmptyCartId()
+ {
+ $maskedQuoteId = '';
+ $email = 'some@user.com';
+
+ $query = $this->getQuery($maskedQuoteId, $email);
+ $this->graphQlMutation($query);
+ }
+
+ /**
+ * Returns GraphQl mutation query for setting email address for a guest
+ *
+ * @param string $maskedQuoteId
+ * @param string $email
+ * @return string
+ */
+ private function getQuery(string $maskedQuoteId, string $email): string
+ {
+ return <<quoteResource = $objectManager->get(QuoteResource::class);
- $this->quoteFactory = $objectManager->get(QuoteFactory::class);
- $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class);
$this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
+ $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ $this->getQuoteShippingAddressIdByReservedQuoteId = $objectManager->get(
+ GetQuoteShippingAddressIdByReservedQuoteId::class
+ );
}
/**
- * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php
- * @magentoApiDataFixture Magento/Ups/_files/enable_ups_shipping_method.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Ups/_files/enable_ups_shipping_method.php
+ *
+ * @dataProvider dataProviderShippingMethods
+ * @param string $methodCode
+ * @param string $methodLabel
*/
- public function testSetUpsShippingMethod()
+ public function testSetUpsShippingMethod(string $methodCode, string $methodLabel)
{
- $quote = $this->quoteFactory->create();
- $this->quoteResource->load($quote, 'test_order_1', 'reserved_order_id');
- $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId());
- $shippingAddressId = (int)$quote->getShippingAddress()->getId();
-
- $query = $this->getAddUpsShippingMethodQuery(
- $maskedQuoteId,
- $shippingAddressId,
- self::CARRIER_CODE,
- self::CARRIER_METHOD_CODE_GROUND
+ $quoteReservedId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($quoteReservedId);
+ $shippingAddressId = $this->getQuoteShippingAddressIdByReservedQuoteId->execute($quoteReservedId);
+
+ $query = $this->getQuery($maskedQuoteId, $shippingAddressId, self::CARRIER_CODE, $methodCode);
+ $response = $this->sendRequestWithToken($query);
+
+ self::assertArrayHasKey('setShippingMethodsOnCart', $response);
+ self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']);
+ self::assertArrayHasKey('shipping_addresses', $response['setShippingMethodsOnCart']['cart']);
+ self::assertCount(1, $response['setShippingMethodsOnCart']['cart']['shipping_addresses']);
+
+ $shippingAddress = current($response['setShippingMethodsOnCart']['cart']['shipping_addresses']);
+ self::assertArrayHasKey('selected_shipping_method', $shippingAddress);
+
+ self::assertArrayHasKey('carrier_code', $shippingAddress['selected_shipping_method']);
+ self::assertEquals(self::CARRIER_CODE, $shippingAddress['selected_shipping_method']['carrier_code']);
+
+ self::assertArrayHasKey('method_code', $shippingAddress['selected_shipping_method']);
+ self::assertEquals($methodCode, $shippingAddress['selected_shipping_method']['method_code']);
+
+ self::assertArrayHasKey('label', $shippingAddress['selected_shipping_method']);
+ self::assertEquals(
+ self::CARRIER_LABEL . ' - ' . $methodLabel,
+ $shippingAddress['selected_shipping_method']['label']
);
+ }
+ /**
+ * @return array
+ */
+ public function dataProviderShippingMethods(): array
+ {
+ return [
+ 'Next Day Air Early AM' => ['1DM', 'Next Day Air Early AM'],
+ 'Next Day Air' => ['1DA', 'Next Day Air'],
+ '2nd Day Air' => ['2DA', '2nd Day Air'],
+ '3 Day Select' => ['3DS', '3 Day Select'],
+ 'Ground' => ['GND', 'Ground'],
+ ];
+ }
+
+ /**
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_canada_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Ups/_files/enable_ups_shipping_method.php
+ *
+ * @dataProvider dataProviderShippingMethodsBasedOnCanadaAddress
+ * @param string $methodCode
+ * @param string $methodLabel
+ */
+ public function testSetUpsShippingMethodBasedOnCanadaAddress(string $methodCode, string $methodLabel)
+ {
+ $quoteReservedId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($quoteReservedId);
+ $shippingAddressId = $this->getQuoteShippingAddressIdByReservedQuoteId->execute($quoteReservedId);
+
+ $query = $this->getQuery($maskedQuoteId, $shippingAddressId, self::CARRIER_CODE, $methodCode);
$response = $this->sendRequestWithToken($query);
- $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses'];
- $expectedResult = [
- 'carrier_code' => self::CARRIER_CODE,
- 'method_code' => self::CARRIER_METHOD_CODE_GROUND,
- 'label' => 'United Parcel Service - Ground',
+
+ self::assertArrayHasKey('setShippingMethodsOnCart', $response);
+ self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']);
+ self::assertArrayHasKey('shipping_addresses', $response['setShippingMethodsOnCart']['cart']);
+ self::assertCount(1, $response['setShippingMethodsOnCart']['cart']['shipping_addresses']);
+
+ $shippingAddress = current($response['setShippingMethodsOnCart']['cart']['shipping_addresses']);
+ self::assertArrayHasKey('selected_shipping_method', $shippingAddress);
+
+ self::assertArrayHasKey('carrier_code', $shippingAddress['selected_shipping_method']);
+ self::assertEquals(self::CARRIER_CODE, $shippingAddress['selected_shipping_method']['carrier_code']);
+
+ self::assertArrayHasKey('method_code', $shippingAddress['selected_shipping_method']);
+ self::assertEquals($methodCode, $shippingAddress['selected_shipping_method']['method_code']);
+
+ self::assertArrayHasKey('label', $shippingAddress['selected_shipping_method']);
+ self::assertEquals(
+ self::CARRIER_LABEL . ' - ' . $methodLabel,
+ $shippingAddress['selected_shipping_method']['label']
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function dataProviderShippingMethodsBasedOnCanadaAddress(): array
+ {
+ return [
+ 'Canada Standard' => ['STD', 'Canada Standard'],
+ 'Worldwide Express' => ['XPR', 'Worldwide Express'],
+ 'Worldwide Express Saver' => ['WXS', 'Worldwide Express Saver'],
+ 'Worldwide Express Plus' => ['XDM', 'Worldwide Express Plus'],
+ 'Worldwide Expedited' => ['XPD', 'Worldwide Expedited'],
];
- self::assertEquals($addressesInformation[0]['selected_shipping_method'], $expectedResult);
}
/**
@@ -98,7 +207,7 @@ public function testSetUpsShippingMethod()
* @param string $methodCode
* @return string
*/
- private function getAddUpsShippingMethodQuery(
+ private function getQuery(
string $maskedQuoteId,
int $shippingAddressId,
string $carrierCode,
diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/RefundOrderTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/RefundOrderTest.php
index 8e5373ea76576..92942d7acc6f2 100644
--- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/RefundOrderTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/RefundOrderTest.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Sales\Service\V1;
use Magento\Sales\Model\Order;
@@ -201,6 +202,73 @@ public function testFullRequest()
}
}
+ /**
+ * Test order will keep same(custom) status after partial refund, if state has not been changed.
+ *
+ * @magentoApiDataFixture Magento/Sales/_files/order_with_invoice_and_custom_status.php
+ */
+ public function testOrderStatusPartialRefund()
+ {
+ /** @var \Magento\Sales\Model\Order $existingOrder */
+ $existingOrder = $this->objectManager->create(\Magento\Sales\Model\Order::class)
+ ->loadByIncrementId('100000001');
+
+ $items = $this->getOrderItems($existingOrder);
+ $items[0]['qty'] -= 1;
+ $result = $this->_webApiCall(
+ $this->getServiceData($existingOrder),
+ [
+ 'orderId' => $existingOrder->getEntityId(),
+ 'items' => $items,
+ ]
+ );
+
+ $this->assertNotEmpty(
+ $result,
+ 'Failed asserting that the received response is correct'
+ );
+
+ /** @var \Magento\Sales\Model\Order $updatedOrder */
+ $updatedOrder = $this->objectManager->create(\Magento\Sales\Model\Order::class)
+ ->loadByIncrementId($existingOrder->getIncrementId());
+
+ $this->assertSame('custom_processing', $updatedOrder->getStatus());
+ $this->assertSame('processing', $updatedOrder->getState());
+ }
+
+ /**
+ * Test order will change custom status after total refund, when state has been changed.
+ *
+ * @magentoApiDataFixture Magento/Sales/_files/order_with_invoice_and_custom_status.php
+ */
+ public function testOrderStatusTotalRefund()
+ {
+ /** @var \Magento\Sales\Model\Order $existingOrder */
+ $existingOrder = $this->objectManager->create(\Magento\Sales\Model\Order::class)
+ ->loadByIncrementId('100000001');
+
+ $items = $this->getOrderItems($existingOrder);
+ $result = $this->_webApiCall(
+ $this->getServiceData($existingOrder),
+ [
+ 'orderId' => $existingOrder->getEntityId(),
+ 'items' => $items,
+ ]
+ );
+
+ $this->assertNotEmpty(
+ $result,
+ 'Failed asserting that the received response is correct'
+ );
+
+ /** @var \Magento\Sales\Model\Order $updatedOrder */
+ $updatedOrder = $this->objectManager->create(\Magento\Sales\Model\Order::class)
+ ->loadByIncrementId($existingOrder->getIncrementId());
+
+ $this->assertSame('complete', $updatedOrder->getStatus());
+ $this->assertSame('complete', $updatedOrder->getState());
+ }
+
/**
* Prepares and returns info for API service.
*
diff --git a/dev/tests/api-functional/testsuite/Magento/Webapi/JoinDirectivesTest.php b/dev/tests/api-functional/testsuite/Magento/Webapi/JoinDirectivesTest.php
index 5f967758e21bd..8beb14e81be71 100644
--- a/dev/tests/api-functional/testsuite/Magento/Webapi/JoinDirectivesTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Webapi/JoinDirectivesTest.php
@@ -6,12 +6,14 @@
namespace Magento\Webapi;
+use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\SearchCriteriaBuilder;
-use Magento\Framework\Api\SortOrderBuilder;
use Magento\Framework\Api\SortOrder;
-use Magento\Framework\Api\SearchCriteria;
-use Magento\Framework\Api\FilterBuilder;
+use Magento\Framework\Api\SortOrderBuilder;
+/**
+ * Test join directives.
+ */
class JoinDirectivesTest extends \Magento\TestFramework\TestCase\WebapiAbstract
{
/**
@@ -44,7 +46,8 @@ protected function setUp()
}
/**
- * Rollback rules
+ * Rollback rules.
+ *
* @magentoApiDataFixture Magento/SalesRule/_files/rules_rollback.php
* @magentoApiDataFixture Magento/Sales/_files/quote.php
*/
@@ -124,6 +127,49 @@ public function testAutoGeneratedGetList()
$this->assertEquals($expectedExtensionAttributes['email'], $testAttribute['email']);
}
+ /**
+ * Test get list of orders with extension attributes.
+ *
+ * @magentoApiDataFixture Magento/Sales/_files/order.php
+ */
+ public function testGetOrdertList()
+ {
+ $filter = $this->filterBuilder
+ ->setField('increment_id')
+ ->setValue('100000001')
+ ->setConditionType('eq')
+ ->create();
+ $this->searchBuilder->addFilters([$filter]);
+ $searchData = $this->searchBuilder->create()->__toArray();
+
+ $requestData = ['searchCriteria' => $searchData];
+
+ $restResourcePath = '/V1/orders/';
+ $soapService = 'salesOrderRepositoryV1';
+ $expectedExtensionAttributes = $this->getExpectedExtensionAttributes();
+
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => $restResourcePath . '?' . http_build_query($requestData),
+ 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
+ ],
+ 'soap' => [
+ 'service' => $soapService,
+ 'operation' => $soapService . 'GetList',
+ ],
+ ];
+ $searchResult = $this->_webApiCall($serviceInfo, $requestData);
+
+ $this->assertArrayHasKey('items', $searchResult);
+ $itemData = array_pop($searchResult['items']);
+ $this->assertArrayHasKey('extension_attributes', $itemData);
+ $this->assertArrayHasKey('order_api_test_attribute', $itemData['extension_attributes']);
+ $testAttribute = $itemData['extension_attributes']['order_api_test_attribute'];
+ $this->assertEquals($expectedExtensionAttributes['firstname'], $testAttribute['first_name']);
+ $this->assertEquals($expectedExtensionAttributes['lastname'], $testAttribute['last_name']);
+ $this->assertEquals($expectedExtensionAttributes['email'], $testAttribute['email']);
+ }
+
/**
* Retrieve the admin user's information.
*
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php
index f0abd280f3ebc..8fa22122cce89 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php
@@ -8,7 +8,6 @@
use Magento\Mtf\Util\Protocol\CurlInterface;
use Magento\Mtf\Util\Protocol\CurlTransport;
-use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator;
/**
* Perform bin/magento commands from command line for functional tests executions.
@@ -18,7 +17,7 @@ class Cli
/**
* Url to command.php.
*/
- const URL = '/dev/tests/functional/utils/command.php';
+ const URL = 'dev/tests/functional/utils/command.php';
/**
* Curl transport protocol.
@@ -27,21 +26,12 @@ class Cli
*/
private $transport;
- /**
- * Webapi handler.
- *
- * @var WebapiDecorator
- */
- private $webapiHandler;
-
/**
* @param CurlTransport $transport
- * @param WebapiDecorator $webapiHandler
*/
- public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler)
+ public function __construct(CurlTransport $transport)
{
$this->transport = $transport;
- $this->webapiHandler = $webapiHandler;
}
/**
@@ -53,31 +43,22 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan
*/
public function execute($command, $options = [])
{
- $this->transport->write(
- rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL,
- $this->prepareParamArray($command, $options),
- CurlInterface::POST,
- []
- );
- $this->transport->read();
- $this->transport->close();
+ $curl = $this->transport;
+ $curl->write($this->prepareUrl($command, $options), [], CurlInterface::GET);
+ $curl->read();
+ $curl->close();
}
/**
- * Prepare parameter array.
+ * Prepare url.
*
* @param string $command
* @param array $options [optional]
- * @return array
+ * @return string
*/
- private function prepareParamArray($command, $options = [])
+ private function prepareUrl($command, $options = [])
{
- if (!empty($options)) {
- $command .= ' ' . implode(' ', $options);
- }
- return [
- 'token' => urlencode($this->webapiHandler->getWebapiToken()),
- 'command' => urlencode($command)
- ];
+ $command .= ' ' . implode(' ', $options);
+ return $_ENV['app_frontend_url'] . self::URL . '?command=' . urlencode($command);
}
}
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php
index 69df78a5cad64..d7336b51a18e2 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php
@@ -3,12 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Mtf\Util\Command\File\Export;
use Magento\Mtf\ObjectManagerInterface;
use Magento\Mtf\Util\Protocol\CurlTransport;
use Magento\Mtf\Util\Protocol\CurlInterface;
-use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator;
/**
* File reader for Magento export files.
@@ -36,29 +36,16 @@ class Reader implements ReaderInterface
*/
private $transport;
- /**
- * Webapi handler.
- *
- * @var WebapiDecorator
- */
- private $webapiHandler;
-
/**
* @param ObjectManagerInterface $objectManager
* @param CurlTransport $transport
- * @param WebapiDecorator $webapiHandler
* @param string $template
*/
- public function __construct(
- ObjectManagerInterface $objectManager,
- CurlTransport $transport,
- WebapiDecorator $webapiHandler,
- $template
- ) {
+ public function __construct(ObjectManagerInterface $objectManager, CurlTransport $transport, $template)
+ {
$this->objectManager = $objectManager;
$this->template = $template;
$this->transport = $transport;
- $this->webapiHandler = $webapiHandler;
}
/**
@@ -83,27 +70,20 @@ public function getData()
*/
private function getFiles()
{
- $this->transport->write(
- rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL,
- $this->prepareParamArray(),
- CurlInterface::POST,
- []
- );
+ $this->transport->write($this->prepareUrl(), [], CurlInterface::GET);
$serializedFiles = $this->transport->read();
$this->transport->close();
- return unserialize($serializedFiles);
+ // phpcs:ignore Magento2.Security.InsecureFunction
+ return unserialize($serializedFiles, ['allowed_classes' => false]);
}
/**
- * Prepare parameter array.
+ * Prepare url.
*
- * @return array
+ * @return string
*/
- private function prepareParamArray()
+ private function prepareUrl()
{
- return [
- 'token' => urlencode($this->webapiHandler->getWebapiToken()),
- 'template' => urlencode($this->template)
- ];
+ return $_ENV['app_frontend_url'] . self::URL . '?template=' . urlencode($this->template);
}
}
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php
index 3666e8643efa3..93f7cf1ce9764 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php
@@ -14,7 +14,7 @@ interface ReaderInterface
/**
* Url to export.php.
*/
- const URL = '/dev/tests/functional/utils/export.php';
+ const URL = 'dev/tests/functional/utils/export.php';
/**
* Exporting files as Data object from Magento.
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php
index 820a5b0a82228..f4e55682857a2 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php
@@ -7,7 +7,6 @@
namespace Magento\Mtf\Util\Command\File;
use Magento\Mtf\Util\Protocol\CurlTransport;
-use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator;
/**
* Get content of log file in var/log folder.
@@ -17,7 +16,7 @@ class Log
/**
* Url to log.php.
*/
- const URL = '/dev/tests/functional/utils/log.php';
+ const URL = 'dev/tests/functional/utils/log.php';
/**
* Curl transport protocol.
@@ -26,21 +25,12 @@ class Log
*/
private $transport;
- /**
- * Webapi handler.
- *
- * @var WebapiDecorator
- */
- private $webapiHandler;
-
/**
* @param CurlTransport $transport
- * @param WebapiDecorator $webapiHandler
*/
- public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler)
+ public function __construct(CurlTransport $transport)
{
$this->transport = $transport;
- $this->webapiHandler = $webapiHandler;
}
/**
@@ -51,28 +41,22 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan
*/
public function getFileContent($name)
{
- $this->transport->write(
- rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL,
- $this->prepareParamArray($name),
- CurlInterface::POST,
- []
- );
- $data = $this->transport->read();
- $this->transport->close();
+ $curl = $this->transport;
+ $curl->write($this->prepareUrl($name), [], CurlTransport::GET);
+ $data = $curl->read();
+ $curl->close();
+ // phpcs:ignore Magento2.Security.InsecureFunction
return unserialize($data);
}
/**
- * Prepare parameter array.
+ * Prepare url.
*
* @param string $name
- * @return array
+ * @return string
*/
- private function prepareParamArray($name)
+ private function prepareUrl($name)
{
- return [
- 'token' => urlencode($this->webapiHandler->getWebapiToken()),
- 'name' => urlencode($name)
- ];
+ return $_ENV['app_frontend_url'] . self::URL . '?name=' . urlencode($name);
}
}
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php
index a9fefa25ffa24..dde3409ed1562 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php
@@ -7,7 +7,6 @@
use Magento\Mtf\Util\Protocol\CurlInterface;
use Magento\Mtf\Util\Protocol\CurlTransport;
-use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator;
/**
* GeneratedCode removes generated code of Magento (like generated/code and generated/metadata).
@@ -17,7 +16,7 @@ class GeneratedCode
/**
* Url to deleteMagentoGeneratedCode.php.
*/
- const URL = '/dev/tests/functional/utils/deleteMagentoGeneratedCode.php';
+ const URL = 'dev/tests/functional/utils/deleteMagentoGeneratedCode.php';
/**
* Curl transport protocol.
@@ -26,21 +25,12 @@ class GeneratedCode
*/
private $transport;
- /**
- * Webapi handler.
- *
- * @var WebapiDecorator
- */
- private $webapiHandler;
-
/**
* @param CurlTransport $transport
- * @param WebapiDecorator $webapiHandler
*/
- public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler)
+ public function __construct(CurlTransport $transport)
{
$this->transport = $transport;
- $this->webapiHandler = $webapiHandler;
}
/**
@@ -50,25 +40,10 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan
*/
public function delete()
{
- $this->transport->write(
- rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL,
- $this->prepareParamArray(),
- CurlInterface::POST,
- []
- );
- $this->transport->read();
- $this->transport->close();
- }
-
- /**
- * Prepare parameter array.
- *
- * @return array
- */
- private function prepareParamArray()
- {
- return [
- 'token' => urlencode($this->webapiHandler->getWebapiToken())
- ];
+ $url = $_ENV['app_frontend_url'] . self::URL;
+ $curl = $this->transport;
+ $curl->write($url, [], CurlInterface::GET);
+ $curl->read();
+ $curl->close();
}
}
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php
index a55d803f43087..f669d91f2f2e5 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php
@@ -7,7 +7,6 @@
use Magento\Mtf\Util\Protocol\CurlInterface;
use Magento\Mtf\Util\Protocol\CurlTransport;
-use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator;
/**
* Returns array of locales depends on fetching type.
@@ -27,7 +26,7 @@ class Locales
/**
* Url to locales.php.
*/
- const URL = '/dev/tests/functional/utils/locales.php';
+ const URL = 'dev/tests/functional/utils/locales.php';
/**
* Curl transport protocol.
@@ -36,21 +35,12 @@ class Locales
*/
private $transport;
- /**
- * Webapi handler.
- *
- * @var WebapiDecorator
- */
- private $webapiHandler;
-
/**
* @param CurlTransport $transport Curl transport protocol
- * @param WebapiDecorator $webapiHandler
*/
- public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler)
+ public function __construct(CurlTransport $transport)
{
$this->transport = $transport;
- $this->webapiHandler = $webapiHandler;
}
/**
@@ -61,28 +51,12 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan
*/
public function getList($type = self::TYPE_ALL)
{
- $this->transport->write(
- rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL,
- $this->prepareParamArray($type),
- CurlInterface::POST,
- []
- );
- $result = $this->transport->read();
- $this->transport->close();
- return explode('|', $result);
- }
+ $url = $_ENV['app_frontend_url'] . self::URL . '?type=' . $type;
+ $curl = $this->transport;
+ $curl->write($url, [], CurlInterface::GET);
+ $result = $curl->read();
+ $curl->close();
- /**
- * Prepare parameter array.
- *
- * @param string $type
- * @return array
- */
- private function prepareParamArray($type)
- {
- return [
- 'token' => urlencode($this->webapiHandler->getWebapiToken()),
- 'type' => urlencode($type)
- ];
+ return explode('|', $result);
}
}
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php
index 4b12f6eec87aa..fd1f746a6f09c 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php
@@ -7,7 +7,6 @@
use Magento\Mtf\Util\Protocol\CurlInterface;
use Magento\Mtf\Util\Protocol\CurlTransport;
-use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator;
/**
* PathChecker checks that path to file or directory exists.
@@ -17,7 +16,7 @@ class PathChecker
/**
* Url to checkPath.php.
*/
- const URL = '/dev/tests/functional/utils/pathChecker.php';
+ const URL = 'dev/tests/functional/utils/pathChecker.php';
/**
* Curl transport protocol.
@@ -27,21 +26,11 @@ class PathChecker
private $transport;
/**
- * Webapi handler.
- *
- * @var WebapiDecorator
- */
- private $webapiHandler;
-
- /**
- * @constructor
* @param CurlTransport $transport
- * @param WebapiDecorator $webapiHandler
*/
- public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler)
+ public function __construct(CurlTransport $transport)
{
$this->transport = $transport;
- $this->webapiHandler = $webapiHandler;
}
/**
@@ -52,28 +41,12 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan
*/
public function pathExists($path)
{
- $this->transport->write(
- rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL,
- $this->prepareParamArray($path),
- CurlInterface::POST,
- []
- );
- $result = $this->transport->read();
- $this->transport->close();
- return strpos($result, 'path exists: true') !== false;
- }
+ $url = $_ENV['app_frontend_url'] . self::URL . '?path=' . urlencode($path);
+ $curl = $this->transport;
+ $curl->write($url, [], CurlInterface::GET);
+ $result = $curl->read();
+ $curl->close();
- /**
- * Prepare parameter array.
- *
- * @param string $path
- * @return array
- */
- private function prepareParamArray($path)
- {
- return [
- 'token' => urlencode($this->webapiHandler->getWebapiToken()),
- 'path' => urlencode($path)
- ];
+ return strpos($result, 'path exists: true') !== false;
}
}
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php
index fec20bb2a8715..7d73634c0360d 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php
@@ -3,11 +3,11 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Mtf\Util\Command;
use Magento\Mtf\Util\Protocol\CurlInterface;
use Magento\Mtf\Util\Protocol\CurlTransport;
-use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator;
/**
* Perform Website folder creation for functional tests executions.
@@ -17,7 +17,7 @@ class Website
/**
* Url to website.php.
*/
- const URL = '/dev/tests/functional/utils/website.php';
+ const URL = 'dev/tests/functional/utils/website.php';
/**
* Curl transport protocol.
@@ -26,22 +26,13 @@ class Website
*/
private $transport;
- /**
- * Webapi handler.
- *
- * @var WebapiDecorator
- */
- private $webapiHandler;
-
/**
* @constructor
* @param CurlTransport $transport
- * @param WebapiDecorator $webapiHandler
*/
- public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler)
+ public function __construct(CurlTransport $transport)
{
$this->transport = $transport;
- $this->webapiHandler = $webapiHandler;
}
/**
@@ -52,28 +43,21 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan
*/
public function create($websiteCode)
{
- $this->transport->addOption(CURLOPT_HEADER, 1);
- $this->transport->write(
- rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL,
- $this->prepareParamArray($websiteCode),
- CurlInterface::POST,
- []
- );
- $this->transport->read();
- $this->transport->close();
+ $curl = $this->transport;
+ $curl->addOption(CURLOPT_HEADER, 1);
+ $curl->write($this->prepareUrl($websiteCode), [], CurlInterface::GET);
+ $curl->read();
+ $curl->close();
}
/**
- * Prepare parameter array.
+ * Prepare url.
*
* @param string $websiteCode
- * @return array
+ * @return string
*/
- private function prepareParamArray($websiteCode)
+ private function prepareUrl($websiteCode)
{
- return [
- 'token' => urlencode($this->webapiHandler->getWebapiToken()),
- 'website_code' => urlencode($websiteCode)
- ];
+ return $_ENV['app_frontend_url'] . self::URL . '?website_code=' . urlencode($websiteCode);
}
}
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php
index d7026e9b8efb3..b1c552370835c 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php
@@ -63,56 +63,24 @@ public function __construct(CurlTransport $transport, DataInterface $configurati
*/
protected function authorize()
{
- // There are situations where magento application backend url could be slightly different from the environment
- // variable we know. It could be intentionally (e.g. InstallTest) or unintentionally. We would still want tests
- // to run in this case.
- // When the original app_backend_url does not work, we will try 4 variants of the it. i.e. with and without
- // url rewrite, http and https.
- $urls = [];
- $originalUrl = rtrim($_ENV['app_backend_url'], '/') . '/';
- $urls[] = $originalUrl;
- // It could be the case that the page needs a refresh, so we will try the original one twice.
- $urls[] = $originalUrl;
- if (strpos($originalUrl, '/index.php') !== false) {
- $url2 = str_replace('/index.php', '', $originalUrl);
- } else {
- $url2 = $originalUrl . 'index.php/';
- }
- $urls[] = $url2;
- if (strpos($originalUrl, 'https') !== false) {
- $urls[] = str_replace('https', 'http', $originalUrl);
- } else {
- $urls[] = str_replace('http', 'https', $url2);
- }
-
- $isAuthorized = false;
- foreach ($urls as $url) {
- try {
- // Perform GET to backend url so form_key is set
- $this->transport->write($url, [], CurlInterface::GET);
- $this->read();
-
- $authUrl = $url . $this->configuration->get('application/0/backendLoginUrl/0/value');
- $data = [
- 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'),
- 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'),
- 'form_key' => $this->formKey,
- ];
-
- $this->transport->write($authUrl, $data, CurlInterface::POST);
- $response = $this->read();
- if (strpos($response, 'login-form') !== false) {
- continue;
- }
- $isAuthorized = true;
- $_ENV['app_backend_url'] = $url;
- break;
- } catch (\Exception $e) {
- continue;
- }
- }
- if ($isAuthorized == false) {
- throw new \Exception('Admin user cannot be logged in by curl handler!');
+ // Perform GET to backend url so form_key is set
+ $url = $_ENV['app_backend_url'];
+ $this->transport->write($url, [], CurlInterface::GET);
+ $this->read();
+
+ $url = $_ENV['app_backend_url'] . $this->configuration->get('application/0/backendLoginUrl/0/value');
+ $data = [
+ 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'),
+ 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'),
+ 'form_key' => $this->formKey,
+ ];
+ $this->transport->write($url, $data, CurlInterface::POST);
+ $response = $this->read();
+ if (strpos($response, 'login-form') !== false) {
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
+ throw new \Exception(
+ 'Admin user cannot be logged in by curl handler!'
+ );
}
}
@@ -144,6 +112,7 @@ public function write($url, $params = [], $method = CurlInterface::POST, $header
if ($this->formKey) {
$params['form_key'] = $this->formKey;
} else {
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
throw new \Exception(sprintf('Form key is absent! Url: "%s" Response: "%s"', $url, $this->response));
}
$this->transport->write($url, http_build_query($params), $method, $headers);
diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php
index df5ab45a3f96d..3aa756904ab00 100644
--- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php
+++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php
@@ -70,13 +70,6 @@ class WebapiDecorator implements CurlInterface
*/
protected $response;
- /**
- * Webapi token.
- *
- * @var string
- */
- protected $webapiToken;
-
/**
* @construct
* @param ObjectManager $objectManager
@@ -117,9 +110,6 @@ protected function init()
$integration->persist();
$this->setConfiguration($integration);
- $this->webapiToken = $integration->getToken();
- } else {
- $this->webapiToken = $integrationToken;
}
}
@@ -171,13 +161,7 @@ protected function setConfiguration(Integration $integration)
*/
protected function isValidIntegration()
{
- $url = rtrim($_ENV['app_frontend_url'], '/');
- if (strpos($url, 'index.php') === false) {
- $url .= '/index.php/rest/V1/modules';
- } else {
- $url .= '/rest/V1/modules';
- }
- $this->write($url, [], CurlInterface::GET);
+ $this->write($_ENV['app_frontend_url'] . 'rest/V1/modules', [], CurlInterface::GET);
$response = json_decode($this->read(), true);
return (null !== $response) && !isset($response['message']);
@@ -235,18 +219,4 @@ public function close()
{
$this->transport->close();
}
-
- /**
- * Return webapiToken.
- *
- * @return string
- */
- public function getWebapiToken()
- {
- // Request token if integration is no longer valid
- if (!$this->isValidIntegration()) {
- $this->init();
- }
- return $this->webapiToken;
- }
}
diff --git a/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.php b/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.php
index fefe0d2c126e5..c2c684c89d06b 100644
--- a/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.php
+++ b/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.php
@@ -140,9 +140,9 @@ public function test(
if ($website) {
$website->persist();
$this->setupCurrencyForCustomWebsite($website, $currencyCustomWebsite);
- $this->cron->run();
- $this->cron->run();
}
+ $this->cron->run();
+ $this->cron->run();
$products = $this->prepareProducts($products, $website);
$this->cron->run();
$this->cron->run();
@@ -165,7 +165,8 @@ public function test(
if (!empty($advancedPricingAttributes)) {
$products = [$products[0]];
}
-
+ $this->cron->run();
+ $this->cron->run();
return [
'products' => $products
];
diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/GlobalSearchEntityTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/GlobalSearchEntityTest.xml
index dca6e1d15024f..6d9c50b4317c8 100644
--- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/GlobalSearchEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/GlobalSearchEntityTest.xml
@@ -8,31 +8,26 @@
- stable:no
search shows admin preview
Some search term
- stable:no
search with 2 sign return no results
:)
- stable:no
search product by sku
orderInjectable::default::product::sku
- stable:no
search existed customer
customer::johndoe_unique::lastname
- stable:no
search order (by order id)
orderInjectable::default::id
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml
index ea6808ee2a7f5..69093b8adb8db 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml
@@ -17,7 +17,6 @@
- MAGETWO-60635: [CE][Categories] Design update dates are incorrect after save
Create root category with all fields
addRootCategory
Yes
@@ -58,7 +57,6 @@
- MAGETWO-60635: [CE][Categories] Design update dates are incorrect after save
Create not anchor subcategory specifying all fields
addSubcategory
default_category
@@ -146,7 +144,7 @@
- test_type:acceptance_test, test_type:extended_acceptance_test, stable:no
+ test_type:acceptance_test, test_type:extended_acceptance_test
addSubcategory
default_category
Yes
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/DeleteProductAttributeEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/DeleteProductAttributeEntityTest.xml
index 11ba7266ce564..0dd47942c8fe0 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/DeleteProductAttributeEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/DeleteProductAttributeEntityTest.xml
@@ -16,7 +16,6 @@
- stable:no
attribute_type_dropdown
diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/UpdateProductAttributeEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/UpdateProductAttributeEntityTest.xml
index 40cf8e40ae33f..d6420ff431ce6 100644
--- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/UpdateProductAttributeEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/UpdateProductAttributeEntityTest.xml
@@ -8,7 +8,6 @@
- to_maintain:yes
custom_attribute_set
attribute_type_text_field
Text_Field_%isolation%
@@ -29,7 +28,6 @@
- to_maintain:yes
custom_attribute_set
attribute_type_dropdown
Dropdown_%isolation%
@@ -55,6 +53,8 @@
+ MAGETWO-46494: [FT] UpdateProductAttributeEntityTestVariation3 does not actually create an attribute to check
+ to_maintain:yes
custom_attribute_set
tax_class_id
Yes
@@ -62,7 +62,6 @@
- FPC
- to_maintain:yes
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php
index 0bf9b37c75e12..1013404f42df1 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php
@@ -102,7 +102,7 @@ class Shipping extends Form
*
* @var string
*/
- private $emailError = '#customer-email-error';
+ private $emailError = '#checkout-customer-email-error';
/**
* Get email error.
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.xml
index 71115e402880c..0973b968cba95 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.xml
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.xml
@@ -8,7 +8,7 @@
- #customer-email
+ #checkout-customer-email
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.xml
index 8d054c0230873..c0df4b16b92e6 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.xml
@@ -22,7 +22,6 @@
- to_maintain:yes, severity:S2
bundleProduct::bundle_fixed_product
761
756
@@ -35,7 +34,6 @@
- to_maintain:yes, severity:S0
catalogProductSimple::with_two_custom_option
345
340
@@ -48,7 +46,6 @@
- to_maintain:yes, severity:S1
catalogProductVirtual::product_50_dollar
50
50
@@ -100,8 +97,7 @@
- to_maintain:yes, severity:S0
- Errors on configuration step. Skipped.
+ severity:S0
catalogProductSimple::with_two_custom_option
catalogProductVirtual::product_50_dollar
downloadableProduct::with_two_separately_links
diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.xml
index 90c42505585a2..8290d825593af 100644
--- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ValidateEmailOnCheckoutTest.xml
@@ -10,17 +10,20 @@
johndoe
John
+ mftf_migrated:yes
johndoe#example.com
John
+ mftf_migrated:yes
johndoe@example.c
John
+ mftf_migrated:yes
diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/UpdateConfigurableProductEntityTest.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/UpdateConfigurableProductEntityTest.php
index c7d66781738b7..bb88bc854f756 100644
--- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/UpdateConfigurableProductEntityTest.php
+++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/UpdateConfigurableProductEntityTest.php
@@ -32,7 +32,6 @@ class UpdateConfigurableProductEntityTest extends Scenario
{
/* tags */
const MVP = 'yes';
- const TO_MAINTAIN = 'yes';
/* end tags */
/**
diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteCustomerGroupEntityTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteCustomerGroupEntityTest.xml
index cffbbca8ad5cb..4251a1c5ee9c5 100644
--- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteCustomerGroupEntityTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteCustomerGroupEntityTest.xml
@@ -8,6 +8,7 @@
+ mftf_migrated:yes
customer_with_new_customer_group
default
General
diff --git a/dev/tests/functional/tests/app/Magento/CustomerImportExport/Test/TestCase/ExportCustomerAddressesTest.php b/dev/tests/functional/tests/app/Magento/CustomerImportExport/Test/TestCase/ExportCustomerAddressesTest.php
index 6b92891ada2b4..17dfb4fb8cdaf 100644
--- a/dev/tests/functional/tests/app/Magento/CustomerImportExport/Test/TestCase/ExportCustomerAddressesTest.php
+++ b/dev/tests/functional/tests/app/Magento/CustomerImportExport/Test/TestCase/ExportCustomerAddressesTest.php
@@ -87,7 +87,8 @@ public function test(
$exportData->persist();
$this->adminExportIndex->getExportForm()->fill($exportData);
$this->adminExportIndex->getFilterExport()->clickContinue();
-
+ $this->cron->run();
+ $this->cron->run();
return [
'customer' => $customer
];
diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml
index e45609bb90c83..fdb396bbbd052 100644
--- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml
@@ -18,7 +18,6 @@
- stable:no
default
free
freeshipping_freeshipping
diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendPartOneTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendPartOneTest.xml
index 3e7b8fad1f04d..880c66483d5f2 100644
--- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendPartOneTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendPartOneTest.xml
@@ -8,7 +8,6 @@
- https://github.com/magento-engcom/msi/issues/1624
Create order with simple product for registered US customer using Fixed shipping method and Cash on Delivery payment method
catalogProductSimple::with_one_custom_option
default
@@ -70,7 +69,6 @@
- to_maintain:yes
Create order with virtual product for registered UK customer using Bank Transfer payment method
catalogProductVirtual::default
default
diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.xml
index c4cfe3f7f274c..3bb370a15e977 100644
--- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewUserTest.xml
@@ -9,7 +9,7 @@
user_lockout_failures
- severity:S2
+ severity:S2,mftf_migrated:yes
custom_admin_with_default_role
AdminUser%isolation%
FirstName%isolation%
diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/ResetUserPasswordFailedTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/ResetUserPasswordFailedTest.xml
index 55f1b69504363..f43469358aa9c 100644
--- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/ResetUserPasswordFailedTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/ResetUserPasswordFailedTest.xml
@@ -8,7 +8,7 @@
- severity:S1
+ severity:S1,mftf_migrated:yes
custom_admin_with_default_role
2
diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.xml
index a4abfc5daf29e..4d3677076d303 100644
--- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.xml
+++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/AccessAdminWithStoreCodeInUrlTest.xml
@@ -11,6 +11,7 @@
add_store_code_to_urls
default
default
+ mftf_migrated:yes
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertAccessTokensErrorRevokeMessage.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertAccessTokensErrorRevokeMessage.php
deleted file mode 100644
index b1a64c7c7e713..0000000000000
--- a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertAccessTokensErrorRevokeMessage.php
+++ /dev/null
@@ -1,46 +0,0 @@
-getMessagesBlock()->getErrorMessage()
- );
- }
-
- /**
- * Return string representation of object
- *
- * @return string
- */
- public function toString()
- {
- return self::ERROR_MESSAGE . ' error message is present on UserEdit page.';
- }
-}
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertAccessTokensSuccessfullyRevoked.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertAccessTokensSuccessfullyRevoked.php
new file mode 100644
index 0000000000000..b2e52f6a15a10
--- /dev/null
+++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertAccessTokensSuccessfullyRevoked.php
@@ -0,0 +1,45 @@
+getMessagesBlock()->getSuccessMessage()
+ );
+ }
+
+ /**
+ * Return string representation of object
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return self::SUCCESS_MESSAGE . ' message is present on UserEdit page.';
+ }
+}
diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/RevokeAllAccessTokensForAdminWithoutTokensTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/RevokeAllAccessTokensForAdminWithoutTokensTest.xml
index e5fcba9b72c25..afdb72d8c561e 100644
--- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/RevokeAllAccessTokensForAdminWithoutTokensTest.xml
+++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/RevokeAllAccessTokensForAdminWithoutTokensTest.xml
@@ -9,7 +9,7 @@
custom_admin
-
+
diff --git a/dev/tests/functional/utils/authenticate.php b/dev/tests/functional/utils/authenticate.php
deleted file mode 100644
index 15851f6e8000a..0000000000000
--- a/dev/tests/functional/utils/authenticate.php
+++ /dev/null
@@ -1,29 +0,0 @@
-create($_SERVER);
- $tokenModel = $magentoObjectManager->get(\Magento\Integration\Model\Oauth\Token::class);
-
- $tokenPassedIn = $token;
- // Token returned will be null if the token we passed in is invalid
- $tokenFromMagento = $tokenModel->loadByToken($tokenPassedIn)->getToken();
- if (!empty($tokenFromMagento) && ($tokenFromMagento == $tokenPassedIn)) {
- return true;
- } else {
- return false;
- }
-}
diff --git a/dev/tests/functional/utils/command.php b/dev/tests/functional/utils/command.php
index 4e18598a935ad..99025dd1cffcc 100644
--- a/dev/tests/functional/utils/command.php
+++ b/dev/tests/functional/utils/command.php
@@ -3,25 +3,26 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-include __DIR__ . '/authenticate.php';
+
+// phpcs:ignore Magento2.Security.IncludeFile
require_once __DIR__ . '/../../../../app/bootstrap.php';
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\NullOutput;
-if (!empty($_POST['token']) && !empty($_POST['command'])) {
- if (authenticate(urldecode($_POST['token']))) {
- $command = urldecode($_POST['command']);
- $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER);
- $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER);
- $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class);
- $input = new StringInput(escapeshellcmd($command));
- $input->setInteractive(false);
- $output = new NullOutput();
- $cli->doRun($input, $output);
- } else {
- echo "Command not unauthorized.";
- }
+// phpcs:ignore Magento2.Security.Superglobal
+if (isset($_GET['command'])) {
+ // phpcs:ignore Magento2.Security.Superglobal
+ $command = urldecode($_GET['command']);
+ // phpcs:ignore Magento2.Security.Superglobal
+ $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER);
+ // phpcs:ignore Magento2.Security.Superglobal
+ $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER);
+ $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class);
+ $input = new StringInput($command);
+ $input->setInteractive(false);
+ $output = new NullOutput();
+ $cli->doRun($input, $output);
} else {
- echo "'token' or 'command' parameter is not set.";
+ throw new \InvalidArgumentException("Command GET parameter is not set.");
}
diff --git a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php
index 17e3575c87686..17260bd1da635 100644
--- a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php
+++ b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php
@@ -3,14 +3,6 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-include __DIR__ . '/authenticate.php';
-if (!empty($_POST['token']) && !empty($_POST['path'])) {
- if (authenticate(urldecode($_POST['token']))) {
- exec('rm -rf ../../../../generated/*');
- } else {
- echo "Command not unauthorized.";
- }
-} else {
- echo "'token' parameter is not set.";
-}
+// phpcs:ignore Magento2.Security.InsecureFunction
+exec('rm -rf ../../../../generated/*');
diff --git a/dev/tests/functional/utils/export.php b/dev/tests/functional/utils/export.php
index e3eff6e3fec17..fa50bc729d0f6 100644
--- a/dev/tests/functional/utils/export.php
+++ b/dev/tests/functional/utils/export.php
@@ -3,30 +3,32 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-include __DIR__ . '/authenticate.php';
-if (!empty($_POST['token']) && !empty($_POST['template'])) {
- if (authenticate(urldecode($_POST['token']))) {
- $varDir = '../../../../var/export/';
- $template = urldecode($_POST['template']);
- $fileList = scandir($varDir, SCANDIR_SORT_NONE);
- $files = [];
+// phpcs:ignore Magento2.Security.Superglobal
+if (!isset($_GET['template'])) {
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
+ throw new \InvalidArgumentException('Argument "template" must be set.');
+}
- foreach ($fileList as $fileName) {
- if (preg_match("`$template`", $fileName) === 1) {
- $filePath = $varDir . $fileName;
- $files[] = [
- 'content' => file_get_contents($filePath),
- 'name' => $fileName,
- 'date' => filectime($filePath),
- ];
- }
- }
+$varDir = '../../../../var/export/';
+// phpcs:ignore Magento2.Security.Superglobal
+$template = urldecode($_GET['template']);
+// phpcs:ignore Magento2.Functions.DiscouragedFunction
+$fileList = scandir($varDir, SCANDIR_SORT_NONE);
+$files = [];
- echo serialize($files);
- } else {
- echo "Command not unauthorized.";
+foreach ($fileList as $fileName) {
+ if (preg_match("`$template`", $fileName) === 1) {
+ $filePath = $varDir . $fileName;
+ $files[] = [
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'content' => file_get_contents($filePath),
+ 'name' => $fileName,
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ 'date' => filectime($filePath),
+ ];
}
-} else {
- echo "'token' or 'template' parameter is not set.";
}
+
+// phpcs:ignore Magento2.Security.LanguageConstruct, Magento2.Security.InsecureFunction
+echo serialize($files);
diff --git a/dev/tests/functional/utils/locales.php b/dev/tests/functional/utils/locales.php
index a3b4ec05eed65..11e1e2b70fa50 100644
--- a/dev/tests/functional/utils/locales.php
+++ b/dev/tests/functional/utils/locales.php
@@ -3,23 +3,20 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-include __DIR__ . '/authenticate.php';
-if (!empty($_POST['token'])) {
- if (authenticate(urldecode($_POST['token']))) {
- if ($_POST['type'] == 'deployed') {
- $themePath = isset($_POST['theme_path']) ? $_POST['theme_path'] : 'adminhtml/Magento/backend';
- $directory = __DIR__ . '/../../../../pub/static/' . $themePath;
- $locales = array_diff(scandir($directory), ['..', '.']);
- } else {
- require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php';
- $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class);
- $locales = $localeConfig->getAllowedLocales();
- }
- echo implode('|', $locales);
- } else {
- echo "Command not unauthorized.";
- }
+// phpcs:ignore Magento2.Security.Superglobal
+if (isset($_GET['type']) && $_GET['type'] == 'deployed') {
+ // phpcs:ignore Magento2.Security.Superglobal
+ $themePath = isset($_GET['theme_path']) ? $_GET['theme_path'] : 'adminhtml/Magento/backend';
+ $directory = __DIR__ . '/../../../../pub/static/' . $themePath;
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ $locales = array_diff(scandir($directory), ['..', '.']);
} else {
- echo "'token' parameter is not set.";
+ // phpcs:ignore Magento2.Security.IncludeFile
+ require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php';
+ $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class);
+ $locales = $localeConfig->getAllowedLocales();
}
+
+// phpcs:ignore Magento2.Security.LanguageConstruct
+echo implode('|', $locales);
diff --git a/dev/tests/functional/utils/log.php b/dev/tests/functional/utils/log.php
index 889056bfbdd63..30783ae8e1d28 100644
--- a/dev/tests/functional/utils/log.php
+++ b/dev/tests/functional/utils/log.php
@@ -3,20 +3,21 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-declare(strict_types=1);
-include __DIR__ . '/authenticate.php';
-if (!empty($_POST['token']) && !empty($_POST['name'])) {
- if (authenticate(urldecode($_POST['token']))) {
- $name = urldecode($_POST['name']);
- if (preg_match('/\.\.(\\\|\/)/', $name)) {
- throw new \InvalidArgumentException('Invalid log file name');
- }
+declare(strict_types=1);
+// phpcs:ignore Magento2.Security.Superglobal
+if (!isset($_GET['name'])) {
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
+ throw new \InvalidArgumentException(
+ 'The name of log file is required for getting logs.'
+ );
+}
- echo serialize(file_get_contents('../../../../var/log' . '/' . $name));
- } else {
- echo "Command not unauthorized.";
- }
-} else {
- echo "'token' or 'name' parameter is not set.";
+// phpcs:ignore Magento2.Security.Superglobal
+$name = urldecode($_GET['name']);
+if (preg_match('/\.\.(\\\|\/)/', $name)) {
+ throw new \InvalidArgumentException('Invalid log file name');
}
+
+// phpcs:ignore Magento2.Security.InsecureFunction, Magento2.Functions.DiscouragedFunction, Magento2.Security.LanguageConstruct
+echo serialize(file_get_contents('../../../../var/log' .'/' .$name));
diff --git a/dev/tests/functional/utils/pathChecker.php b/dev/tests/functional/utils/pathChecker.php
index b5a2ddb405bde..217cf90af0a56 100644
--- a/dev/tests/functional/utils/pathChecker.php
+++ b/dev/tests/functional/utils/pathChecker.php
@@ -3,20 +3,20 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-include __DIR__ . '/authenticate.php';
-if (!empty($_POST['token']) && !empty($_POST['path'])) {
- if (authenticate(urldecode($_POST['token']))) {
- $path = urldecode($_POST['path']);
-
- if (file_exists('../../../../' . $path)) {
- echo 'path exists: true';
- } else {
- echo 'path exists: false';
- }
+// phpcs:ignore Magento2.Security.Superglobal
+if (isset($_GET['path'])) {
+ // phpcs:ignore Magento2.Security.Superglobal
+ $path = urldecode($_GET['path']);
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ if (file_exists('../../../../' . $path)) {
+ // phpcs:ignore Magento2.Security.LanguageConstruct
+ echo 'path exists: true';
} else {
- echo "Command not unauthorized.";
+ // phpcs:ignore Magento2.Security.LanguageConstruct
+ echo 'path exists: false';
}
} else {
- echo "'token' or 'path' parameter is not set.";
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
+ throw new \InvalidArgumentException("GET parameter 'path' is not set.");
}
diff --git a/dev/tests/functional/utils/website.php b/dev/tests/functional/utils/website.php
index ab8e3742f55ae..720b4962aedd4 100644
--- a/dev/tests/functional/utils/website.php
+++ b/dev/tests/functional/utils/website.php
@@ -3,35 +3,36 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-include __DIR__ . '/authenticate.php';
-if (!empty($_POST['token']) && !empty($_POST['website_code'])) {
- if (authenticate(urldecode($_POST['token']))) {
- $websiteCode = urldecode($_POST['website_code']);
- $rootDir = '../../../../';
- $websiteDir = $rootDir . 'websites/' . $websiteCode . '/';
- $contents = file_get_contents($rootDir . 'index.php');
+// phpcs:ignore Magento2.Security.Superglobal
+if (!isset($_GET['website_code'])) {
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
+ throw new \Exception("website_code GET parameter is not set.");
+}
+
+// phpcs:ignore Magento2.Security.Superglobal
+$websiteCode = urldecode($_GET['website_code']);
+$rootDir = '../../../../';
+$websiteDir = $rootDir . 'websites/' . $websiteCode . '/';
+// phpcs:ignore Magento2.Functions.DiscouragedFunction
+$contents = file_get_contents($rootDir . 'index.php');
- $websiteParam = <<setData('entity_id', $entityId);
}
+
+ /**
+ * @return string
+ */
+ public function getOutputPath()
+ {
+ return $this->_get('outputPath');
+ }
+
+ /**
+ * @param string $path
+ * @return $this
+ */
+ public function setOutputPath($path)
+ {
+ return $this->setData('outputPath', $path);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/DataObjectRepository.php b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/DataObjectRepository.php
similarity index 62%
rename from dev/tests/integration/testsuite/Magento/MysqlMq/Model/DataObjectRepository.php
rename to dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/DataObjectRepository.php
index 879872315ec51..942298e49972f 100644
--- a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/DataObjectRepository.php
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/DataObjectRepository.php
@@ -3,7 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-namespace Magento\MysqlMq\Model;
+namespace Magento\TestModuleMysqlMq\Model;
class DataObjectRepository
{
@@ -11,15 +11,17 @@ class DataObjectRepository
* @param DataObject $dataObject
* @param string $requiredParam
* @param int|null $optionalParam
- * @return bool
+ * @return null
*/
public function delayedOperation(
- \Magento\MysqlMq\Model\DataObject $dataObject,
+ \Magento\TestModuleMysqlMq\Model\DataObject $dataObject,
$requiredParam,
$optionalParam = null
) {
- echo "Processed '{$dataObject->getEntityId()}'; "
+ $output = "Processed '{$dataObject->getEntityId()}'; "
. "Required param '{$requiredParam}'; Optional param '{$optionalParam}'\n";
- return true;
+ file_put_contents($dataObject->getOutputPath(), $output);
+
+ return null;
}
}
diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/Processor.php b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/Processor.php
new file mode 100644
index 0000000000000..fb6fd4c5c2802
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/Processor.php
@@ -0,0 +1,71 @@
+getOutputPath(),
+ "Processed {$message->getEntityId()}" . PHP_EOL,
+ FILE_APPEND
+ );
+ }
+
+ /**
+ * @param \Magento\TestModuleMysqlMq\Model\DataObject $message
+ */
+ public function processObjectCreated($message)
+ {
+ file_put_contents(
+ $message->getOutputPath(),
+ "Processed object created {$message->getEntityId()}" . PHP_EOL,
+ FILE_APPEND
+ );
+ }
+
+ /**
+ * @param \Magento\TestModuleMysqlMq\Model\DataObject $message
+ */
+ public function processCustomObjectCreated($message)
+ {
+ file_put_contents(
+ $message->getOutputPath(),
+ "Processed custom object created {$message->getEntityId()}" . PHP_EOL,
+ FILE_APPEND
+ );
+ }
+
+ /**
+ * @param \Magento\TestModuleMysqlMq\Model\DataObject $message
+ */
+ public function processObjectUpdated($message)
+ {
+ file_put_contents(
+ $message->getOutputPath(),
+ "Processed object updated {$message->getEntityId()}" . PHP_EOL,
+ FILE_APPEND
+ );
+ }
+
+ /**
+ * @param \Magento\TestModuleMysqlMq\Model\DataObject $message
+ */
+ public function processMessageWithException($message)
+ {
+ file_put_contents($message->getOutputPath(), "Exception processing {$message->getEntityId()}");
+ throw new \LogicException(
+ "Exception during message processing happened. Entity: {{$message->getEntityId()}}"
+ );
+ }
+}
diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/communication.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/communication.xml
new file mode 100644
index 0000000000000..4d6269dbb7920
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/communication.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/module.xml
new file mode 100644
index 0000000000000..8b6ea0f44ce9c
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/module.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue.xml
new file mode 100644
index 0000000000000..362237c0c5e62
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_consumer.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_consumer.xml
new file mode 100644
index 0000000000000..bb495a123a05d
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_consumer.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_publisher.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_publisher.xml
new file mode 100644
index 0000000000000..a665e10ef5f14
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_publisher.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_topology.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_topology.xml
new file mode 100644
index 0000000000000..2df5485ee3447
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_topology.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/registration.php b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/registration.php
new file mode 100644
index 0000000000000..4250e95bd7cc3
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/registration.php
@@ -0,0 +1,12 @@
+getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleMysqlMq') === null) {
+ ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleMysqlMq', __DIR__);
+}
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php
index 17863cd709580..497deb2c99110 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Backend\Block\Dashboard;
/**
@@ -27,6 +29,6 @@ protected function setUp()
public function testGetChartUrl()
{
- $this->assertStringStartsWith('http://chart.apis.google.com/chart', $this->_block->getChartUrl());
+ $this->assertStringStartsWith('https://image-charts.com/chart', $this->_block->getChartUrl());
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
index 89f1e5e5d53d6..0eb98379b4571 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Backend\Controller\Adminhtml;
/**
@@ -19,10 +21,15 @@ public function testAjaxBlockAction()
$this->assertContains('dashboard-diagram', $actual);
}
+ /**
+ * Tests tunnelAction
+ *
+ * @throws \Exception
+ * @return void
+ */
public function testTunnelAction()
{
- $this->markTestSkipped('MAGETWO-98800: TunnelAction fails when Google Chart API is not available');
-
+ // phpcs:disable Magento2.Functions.DiscouragedFunction
$testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World';
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $testUrl);
@@ -36,6 +43,7 @@ public function testTunnelAction()
curl_close($handle);
throw $e;
}
+ // phpcs:enable
$gaData = [
'cht' => 'lc',
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/DescriptionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/DescriptionTest.php
new file mode 100644
index 0000000000000..e097109ff63bc
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/DescriptionTest.php
@@ -0,0 +1,54 @@
+block = $objectManager->create(Description::class, [
+ 'data' => [
+ 'template' => 'Magento_Catalog::product/view/attribute.phtml'
+ ]
+ ]);
+
+ $this->registry = $objectManager->get(Registry::class);
+ $this->registry->unregister('product');
+ }
+
+ public function testGetProductWhenNoProductIsRegistered()
+ {
+ $html = $this->block->toHtml();
+ $this->assertEmpty($html);
+ }
+
+ public function testGetProductWhenInvalidProductIsRegistered()
+ {
+ $this->registry->register('product', new \stdClass());
+ $html = $this->block->toHtml();
+ $this->assertEmpty($html);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/expected_categories.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/expected_categories.php
index 744d368a467d5..beaa9f5dfb7f2 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/expected_categories.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/expected_categories.php
@@ -10,17 +10,20 @@
'value' => '2',
'is_active' => '1',
'label' => 'Default Category',
+ '__disableTmpl' => true,
'optgroup' =>
[
0 => [
'value' => '3',
'is_active' => '1',
'label' => 'Category 1',
+ '__disableTmpl' => true,
'optgroup' => [
0 => [
'value' => '4',
'is_active' => '1',
'label' => 'Category 1.1',
+ '__disableTmpl' => true,
'optgroup' =>
[
0 =>
@@ -28,6 +31,7 @@
'value' => '5',
'is_active' => '1',
'label' => 'Category 1.1.1',
+ '__disableTmpl' => true,
],
],
],
@@ -35,6 +39,7 @@
'value' => '13',
'is_active' => '1',
'label' => 'Category 1.2',
+ '__disableTmpl' => true,
],
],
],
@@ -42,36 +47,43 @@
'value' => '6',
'is_active' => '1',
'label' => 'Category 2',
+ '__disableTmpl' => true,
],
2 => [
'value' => '7',
'is_active' => '1',
'label' => 'Movable',
+ '__disableTmpl' => true,
],
3 => [
'value' => '8',
'is_active' => '0',
'label' => 'Inactive',
+ '__disableTmpl' => true,
],
4 => [
'value' => '9',
'is_active' => '1',
'label' => 'Movable Position 1',
+ '__disableTmpl' => true,
],
5 => [
'value' => '10',
'is_active' => '1',
'label' => 'Movable Position 2',
+ '__disableTmpl' => true,
],
6 => [
'value' => '11',
'is_active' => '1',
'label' => 'Movable Position 3',
+ '__disableTmpl' => true,
],
7 => [
'value' => '12',
'is_active' => '1',
'label' => 'Category 12',
+ '__disableTmpl' => true,
],
],
],
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
index c4c6d3ba2d1d2..67446960e15dc 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
@@ -34,6 +34,7 @@
* @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_catalog_product_reindex_schedule.php
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * phpcs:disable Generic.PHP.NoSilencedErrors, Generic.Metrics.NestingLevel, Magento2.Functions.StaticFunction
*/
class ProductTest extends \Magento\TestFramework\Indexer\TestCase
{
@@ -567,6 +568,7 @@ public function testSaveDatetimeAttribute()
*/
protected function getExpectedOptionsData(string $pathToFile, string $storeCode = ''): array
{
+ // phpcs:disable Magento2.Functions.DiscouragedFunction
$productData = $this->csvToArray(file_get_contents($pathToFile));
$expectedOptionId = 0;
$expectedOptions = [];
@@ -1590,6 +1592,28 @@ public function testAddUpdateProductWithInvalidUrlKeys() : void
}
}
+ /**
+ * Make sure the non existing image in the csv file won't erase the qty key of the existing products.
+ *
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ */
+ public function testImportWithNonExistingImage()
+ {
+ $products = [
+ 'simple_new' => 100,
+ ];
+
+ $this->importFile('products_to_import_with_non_existing_image.csv');
+
+ $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+ foreach ($products as $productSku => $productQty) {
+ $product = $productRepository->get($productSku);
+ $stockItem = $product->getExtensionAttributes()->getStockItem();
+ $this->assertEquals($productQty, $stockItem->getQty());
+ }
+ }
+
/**
* @magentoDataFixture Magento/Catalog/_files/product_simple_with_url_key.php
* @magentoDbIsolation disabled
@@ -1781,6 +1805,7 @@ function (ProductInterface $item) {
if ($product->getId()) {
$productRepository->delete($product);
}
+ // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
//Product already removed
}
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_existing_image.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_existing_image.csv
new file mode 100644
index 0000000000000..8122433a8c9e1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_existing_image.csv
@@ -0,0 +1,2 @@
+sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus
+simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,/no/exists/image/magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two",,,,,,,,
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_virtual_product_saved.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_virtual_product_saved.php
index 835b2ab812856..833e5a57ac34f 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_virtual_product_saved.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_virtual_product_saved.php
@@ -13,6 +13,7 @@
->setIsMultiShipping(false)
->setReservedOrderId('test_order_with_virtual_product_without_address')
->setEmail('store@example.com')
+ ->setCustomerEmail('store@example.com')
->addProduct(
$product->load($product->getId()),
1
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php
index c574869a83cab..af495841b9672 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolderTest.php
@@ -7,6 +7,7 @@
namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\Response\HttpFactory as ResponseFactory;
/**
* Test for \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFolder class.
@@ -38,6 +39,11 @@ class DeleteFolderTest extends \PHPUnit\Framework\TestCase
*/
private $filesystem;
+ /**
+ * @var HttpFactory
+ */
+ private $responseFactory;
+
/**
* @inheritdoc
*/
@@ -49,6 +55,7 @@ protected function setUp()
/** @var \Magento\Cms\Helper\Wysiwyg\Images $imagesHelper */
$this->imagesHelper = $objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class);
$this->fullDirectoryPath = $this->imagesHelper->getStorageRoot();
+ $this->responseFactory = $objectManager->get(ResponseFactory::class);
$this->model = $objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFolder::class);
}
@@ -83,6 +90,7 @@ public function testExecute()
* can be removed.
*
* @magentoDataFixture Magento/Cms/_files/linked_media.php
+ * @magentoAppIsolation enabled
*/
public function testExecuteWithLinkedMedia()
{
@@ -106,6 +114,7 @@ public function testExecuteWithLinkedMedia()
* under media directory.
*
* @return void
+ * @magentoAppIsolation enabled
*/
public function testExecuteWithWrongDirectoryName()
{
@@ -116,6 +125,31 @@ public function testExecuteWithWrongDirectoryName()
$this->assertFileExists($this->fullDirectoryPath . $directoryName);
}
+ /**
+ * Execute method to check that there is no ability to remove folder which is in excluded directories list.
+ *
+ * @return void
+ * @magentoAppIsolation enabled
+ */
+ public function testExecuteWithExcludedDirectoryName()
+ {
+ $directoryName = 'downloadable';
+ $expectedResponseMessage = 'We cannot delete directory /downloadable.';
+ $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
+ $mediaDirectory->create($directoryName);
+ $this->assertFileExists($this->fullDirectoryPath . $directoryName);
+
+ $this->model->getRequest()->setParams(['node' => $this->imagesHelper->idEncode($directoryName)]);
+ $this->model->getRequest()->setMethod('POST');
+ $jsonResponse = $this->model->execute();
+ $jsonResponse->renderResult($response = $this->responseFactory->create());
+ $data = json_decode($response->getBody(), true);
+
+ $this->assertTrue($data['error']);
+ $this->assertEquals($expectedResponseMessage, $data['message']);
+ $this->assertFileExists($this->fullDirectoryPath . $directoryName);
+ }
+
/**
* @inheritdoc
*/
@@ -128,5 +162,8 @@ public static function tearDownAfterClass()
if ($directory->isExist('wysiwyg')) {
$directory->delete('wysiwyg');
}
+ if ($directory->isExist('downloadable')) {
+ $directory->delete('downloadable');
+ }
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php
index 00f56e5700415..9303a5eac7868 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php
@@ -33,6 +33,11 @@ class UploadTest extends \PHPUnit\Framework\TestCase
*/
private $fullDirectoryPath;
+ /**
+ * @var string
+ */
+ private $fullExcludedDirectoryPath;
+
/**
* @var string
*/
@@ -60,11 +65,13 @@ protected function setUp()
{
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$directoryName = 'directory1';
+ $excludedDirName = 'downloadable';
$this->filesystem = $this->objectManager->get(\Magento\Framework\Filesystem::class);
/** @var \Magento\Cms\Helper\Wysiwyg\Images $imagesHelper */
$imagesHelper = $this->objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class);
$this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$this->fullDirectoryPath = $imagesHelper->getStorageRoot() . DIRECTORY_SEPARATOR . $directoryName;
+ $this->fullExcludedDirectoryPath = $imagesHelper->getStorageRoot() . DIRECTORY_SEPARATOR . $excludedDirName;
$this->mediaDirectory->create($this->mediaDirectory->getRelativePath($this->fullDirectoryPath));
$this->responseFactory = $this->objectManager->get(ResponseFactory::class);
$this->model = $this->objectManager->get(\Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\Upload::class);
@@ -115,6 +122,34 @@ public function testExecute()
$this->assertEquals($keys, $dataKeys);
}
+ /**
+ * Execute method with excluded directory path and file name to check that file can't be uploaded.
+ *
+ * @return void
+ * @magentoAppIsolation enabled
+ */
+ public function testExecuteWithExcludedDirectory()
+ {
+ $expectedError = 'We can\'t upload the file to current folder right now. Please try another folder.';
+ $this->model->getRequest()->setParams(['type' => 'image/png']);
+ $this->model->getRequest()->setMethod('POST');
+ $this->model->getStorage()->getSession()->setCurrentPath($this->fullExcludedDirectoryPath);
+ /** @var JsonResponse $jsonResponse */
+ $jsonResponse = $this->model->execute();
+ /** @var Response $response */
+ $jsonResponse->renderResult($response = $this->responseFactory->create());
+ $data = json_decode($response->getBody(), true);
+
+ $this->assertEquals($expectedError, $data['error']);
+ $this->assertFalse(
+ $this->mediaDirectory->isExist(
+ $this->mediaDirectory->getRelativePath(
+ $this->fullExcludedDirectoryPath . DIRECTORY_SEPARATOR . $this->fileName
+ )
+ )
+ );
+ }
+
/**
* Execute method with correct directory path and file name to check that file can be uploaded to the directory
* located under linked folder.
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
index fd450e9fef593..5d256f2234a53 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
@@ -107,6 +107,31 @@ public function testGetThumbsPath(): void
);
}
+ /**
+ * @return void
+ */
+ public function testDeleteDirectory(): void
+ {
+ $path = $this->objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class)->getCurrentPath();
+ $dir = 'testDeleteDirectory';
+ $fullPath = $path . $dir;
+ $this->storage->createDirectory($dir, $path);
+ $this->assertFileExists($fullPath);
+ $this->storage->deleteDirectory($fullPath);
+ $this->assertFileNotExists($fullPath);
+ }
+
+ /**
+ * @return void
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage We cannot delete directory /downloadable.
+ */
+ public function testDeleteDirectoryWithExcludedDirPath(): void
+ {
+ $dir = $this->objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class)->getCurrentPath() . 'downloadable';
+ $this->storage->deleteDirectory($dir);
+ }
+
/**
* @return void
*/
@@ -126,11 +151,39 @@ public function testUploadFile(): void
'error' => 0,
'size' => 12500,
];
+
$this->storage->uploadFile(self::$_baseDir);
$this->assertTrue(is_file(self::$_baseDir . DIRECTORY_SEPARATOR . $fileName));
// phpcs:enable
}
+ /**
+ * @return void
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage We can't upload the file to current folder right now. Please try another folder.
+ */
+ public function testUploadFileWithExcludedDirPath(): void
+ {
+ $fileName = 'magento_small_image.jpg';
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $filePath = $tmpDirectory->getAbsolutePath($fileName);
+ // phpcs:disable
+ $fixtureDir = realpath(__DIR__ . '/../../../../Catalog/_files');
+ copy($fixtureDir . DIRECTORY_SEPARATOR . $fileName, $filePath);
+
+ $_FILES['image'] = [
+ 'name' => $fileName,
+ 'type' => 'image/jpeg',
+ 'tmp_name' => $filePath,
+ 'error' => 0,
+ 'size' => 12500,
+ ];
+
+ $dir = $this->objectManager->get(\Magento\Cms\Helper\Wysiwyg\Images::class)->getCurrentPath() . 'downloadable';
+ $this->storage->uploadFile($dir);
+ // phpcs:enable
+ }
+
/**
* @param string $fileName
* @param string $fileType
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/CartTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/CartTest.php
index 3bb10baff6572..3ade17d90fe99 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/CartTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/View/CartTest.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Customer\Block\Adminhtml\Edit\Tab\View;
use Magento\Customer\Controller\RegistryConstants;
@@ -100,15 +101,4 @@ public function testToHtmlCartItem()
$this->assertContains('$10.00', $html);
$this->assertContains('catalog/product/edit/id/1', $html);
}
-
- /**
- * Verify that the customer has a single item in his cart.
- *
- * @magentoDataFixture Magento/Customer/_files/customer.php
- * @magentoDataFixture Magento/Customer/_files/quote.php
- */
- public function testGetCollection()
- {
- $this->assertEquals(1, $this->block->getCollection()->getSize());
- }
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/Group/MultiselectTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/Group/MultiselectTest.php
index 918b085580420..9f121268135f8 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/Group/MultiselectTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/Group/MultiselectTest.php
@@ -35,22 +35,23 @@ public function testToOptionArray()
[
[
'value' => 1,
- 'label' => 'Default (General)'
+ 'label' => 'Default (General)',
+ '__disableTmpl' => true,
],
[
'value' => 1,
'label' => 'General',
- '__disableTmpl' => true
+ '__disableTmpl' => true,
],
[
'value' => 2,
'label' => 'Wholesale',
- '__disableTmpl' => true
+ '__disableTmpl' => true,
],
[
'value' => 3,
'label' => 'Retailer',
- '__disableTmpl' => true
+ '__disableTmpl' => true,
],
]
);
diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php b/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php
index dd55dcc8b47c7..7e16115ed2fef 100644
--- a/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php
+++ b/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php
@@ -267,7 +267,7 @@ public function cssDirectiveDataProvider()
'Empty or missing file' => [
TemplateTypesInterface::TYPE_HTML,
'file="css/non-existent-file.css"',
- '/* Contents of css/non-existent-file.css could not be loaded or is empty */'
+ '/* Contents of the specified CSS file could not be loaded or is empty */'
],
'File with compilation error results in error message' => [
TemplateTypesInterface::TYPE_HTML,
diff --git a/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/communication.xml b/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/communication.xml
index 0fc50f0432b93..1cc5d6cd3b714 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/communication.xml
+++ b/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/communication.xml
@@ -7,6 +7,6 @@
-->
-
+
diff --git a/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/valid_expected_queue.php b/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/valid_expected_queue.php
index b5f5145c32c72..9a813b4424eaa 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/valid_expected_queue.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/valid_expected_queue.php
@@ -40,7 +40,7 @@
"name" => "publisher5.topic",
"schema" => [
"schema_type" => "object",
- "schema_value" => '\\' . \Magento\MysqlMq\Model\DataObject::class
+ "schema_value" => '\\' . \Magento\TestModuleMysqlMq\Model\DataObject::class
],
"response_schema" => [
"schema_type" => "object",
@@ -58,7 +58,7 @@
"handlers" => [
"topic.broker.test" => [
"0" => [
- "type" => \Magento\MysqlMq\Model\Processor::class,
+ "type" => \Magento\TestModuleMysqlMq\Model\Processor::class,
"method" => "processMessage"
]
]
diff --git a/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/valid_queue_input.php b/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/valid_queue_input.php
index fdd4a7d3007a7..ed6e13cfe9fae 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/valid_queue_input.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/MessageQueue/_files/valid_queue_input.php
@@ -23,11 +23,11 @@
"name" => "publisher5.topic",
"schema" => [
"schema_type" => "object",
- "schema_value" => "Magento\\MysqlMq\\Model\\DataObject"
+ "schema_value" => \Magento\TestModuleMysqlMq\Model\DataObject::class
],
"response_schema" => [
"schema_type" => "object",
- "schema_value" => "Magento\\Customer\\Api\\Data\\CustomerInterface"
+ "schema_value" => \Magento\Customer\Api\Data\CustomerInterface::class
],
"publisher" => "test-publisher-5"
]
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/apply_tax_for_simple_product.php b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/apply_tax_for_simple_product.php
new file mode 100644
index 0000000000000..9968704517ecd
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/apply_tax_for_simple_product.php
@@ -0,0 +1,26 @@
+get(ProductRepositoryInterface::class);
+$product = $productRepository->get('simple_product');
+
+/** @var TaxClassCollectionFactory $taxClassCollectionFactory */
+$taxClassCollectionFactory = $objectManager->get(TaxClassCollectionFactory::class);
+$taxClassCollection = $taxClassCollectionFactory->create();
+
+/** @var TaxClassModel $taxClass */
+$taxClassCollection->addFieldToFilter('class_type', TaxClassModel::TAX_CLASS_TYPE_PRODUCT);
+$taxClass = $taxClassCollection->getFirstItem();
+
+$product->setCustomAttribute('tax_class_id', $taxClass->getClassId());
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/virtual_product.php b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/virtual_product.php
new file mode 100644
index 0000000000000..e4472464e17ae
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/virtual_product.php
@@ -0,0 +1,45 @@
+get(ProductInterfaceFactory::class);
+/** @var DataObjectHelper $dataObjectHelper */
+$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+$product = $productFactory->create();
+$productData = [
+ ProductInterface::TYPE_ID => Type::TYPE_VIRTUAL,
+ ProductInterface::ATTRIBUTE_SET_ID => 4,
+ ProductInterface::SKU => 'virtual_product',
+ ProductInterface::NAME => 'Virtual Product',
+ ProductInterface::PRICE => 10,
+ ProductInterface::VISIBILITY => Visibility::VISIBILITY_BOTH,
+ ProductInterface::STATUS => Status::STATUS_ENABLED,
+];
+$dataObjectHelper->populateWithArray($product, $productData, ProductInterface::class);
+/** Out of interface */
+$product
+ ->setWebsiteIds([1])
+ ->setStockData([
+ 'qty' => 85.5,
+ 'is_in_stock' => true,
+ 'manage_stock' => true,
+ 'is_qty_decimal' => true
+ ]);
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/virtual_product_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/virtual_product_rollback.php
new file mode 100644
index 0000000000000..f8d329f574626
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/virtual_product_rollback.php
@@ -0,0 +1,31 @@
+get(ProductRepositoryInterface::class);
+/** @var Registry $registry */
+$registry = $objectManager->get(Registry::class);
+
+$currentArea = $registry->registry('isSecureArea');
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+try {
+ $productRepository->deleteById('virtual_product');
+} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+ /**
+ * Tests which are wrapped with MySQL transaction clear all data by transaction rollback.
+ */
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', $currentArea);
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/set_guest_email.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/set_guest_email.php
new file mode 100644
index 0000000000000..c8084b2552395
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/set_guest_email.php
@@ -0,0 +1,24 @@
+get(QuoteFactory::class);
+/** @var CartRepositoryInterface $cartRepository */
+$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class);
+/** @var QuoteResource $quoteResource */
+$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class);
+
+$quote = $quoteFactory->create();
+$quoteResource->load($quote, 'test_quote', 'reserved_order_id');
+
+$quote->setCustomerEmail('guest@example.com');
+$cartRepository->save($quote);
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_address.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_address.php
index e17b9e61f82db..54f4d8d0c6e75 100644
--- a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_address.php
@@ -26,7 +26,7 @@
$quoteAddressData = [
AddressInterface::KEY_TELEPHONE => 3468676,
- AddressInterface::KEY_POSTCODE => 75477,
+ AddressInterface::KEY_POSTCODE => '75477',
AddressInterface::KEY_COUNTRY_ID => 'US',
AddressInterface::KEY_CITY => 'CityM',
AddressInterface::KEY_COMPANY => 'CompanyName',
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_canada_address.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_canada_address.php
new file mode 100644
index 0000000000000..8e60dc904bd4e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_canada_address.php
@@ -0,0 +1,43 @@
+get(QuoteFactory::class);
+/** @var QuoteResource $quoteResource */
+$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class);
+/** @var AddressInterfaceFactory $quoteAddressFactory */
+$quoteAddressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class);
+/** @var DataObjectHelper $dataObjectHelper */
+$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class);
+/** @var ShippingAddressManagementInterface $shippingAddressManagement */
+$shippingAddressManagement = Bootstrap::getObjectManager()->get(ShippingAddressManagementInterface::class);
+
+$quoteAddressData = [
+ AddressInterface::KEY_TELEPHONE => 3468676,
+ AddressInterface::KEY_POSTCODE => 'M4L 1V3',
+ AddressInterface::KEY_COUNTRY_ID => 'CA',
+ AddressInterface::KEY_CITY => 'Toronto',
+ AddressInterface::KEY_COMPANY => 'CompanyName',
+ AddressInterface::KEY_STREET => '500 Kingston Rd',
+ AddressInterface::KEY_LASTNAME => 'Smith',
+ AddressInterface::KEY_FIRSTNAME => 'John',
+ AddressInterface::KEY_REGION_CODE => 'ON',
+];
+$quoteAddress = $quoteAddressFactory->create();
+$dataObjectHelper->populateWithArray($quoteAddress, $quoteAddressData, AddressInterfaceFactory::class);
+
+$quote = $quoteFactory->create();
+$quoteResource->load($quote, 'test_quote', 'reserved_order_id');
+$shippingAddressManagement->assign($quote->getId(), $quoteAddress);
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php b/dev/tests/integration/testsuite/Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php
new file mode 100644
index 0000000000000..aca55bd8414f6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Tax/_files/tax_rule_for_region_1.php
@@ -0,0 +1,53 @@
+get(DataObjectHelper::class);
+/** @var RateFactory $rateFactory */
+$rateFactory = $objectManager->get(RateFactory::class);
+/** @var RuleFactory $ruleFactory */
+$ruleFactory = $objectManager->get(RuleFactory::class);
+/** @var RateRepository $rateRepository */
+$rateRepository = $objectManager->get(TaxRateRepositoryInterface::class);
+/** @var TaxRuleRepository $ruleRepository */
+$ruleRepository = $objectManager->get(TaxRuleRepositoryInterface::class);
+/** @var Rate $rate */
+$rate = $rateFactory->create();
+$rateData = [
+ Rate::KEY_COUNTRY_ID => 'US',
+ Rate::KEY_REGION_ID => '1',
+ Rate::KEY_POSTCODE => '*',
+ Rate::KEY_CODE => 'US-TEST-*-Rate-1',
+ Rate::KEY_PERCENTAGE_RATE => '7.5',
+];
+$dataObjectHelper->populateWithArray($rate, $rateData, TaxRateInterface::class);
+$rateRepository->save($rate);
+
+$rule = $ruleFactory->create();
+$ruleData = [
+ Rule::KEY_CODE=> 'GraphQl Test Rule',
+ Rule::KEY_PRIORITY => '0',
+ Rule::KEY_POSITION => '0',
+ Rule::KEY_CUSTOMER_TAX_CLASS_IDS => [3],
+ Rule::KEY_PRODUCT_TAX_CLASS_IDS => [2],
+ Rule::KEY_TAX_RATE_IDS => [$rate->getId()],
+];
+$dataObjectHelper->populateWithArray($rule, $ruleData, TaxRuleInterface::class);
+$ruleRepository->save($rule);
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Tax/_files/tax_rule_for_region_1_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Tax/_files/tax_rule_for_region_1_rollback.php
new file mode 100644
index 0000000000000..aba1960624ed4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Tax/_files/tax_rule_for_region_1_rollback.php
@@ -0,0 +1,38 @@
+get(RateFactory::class);
+/** @var RuleFactory $ruleFactory */
+$ruleFactory = $objectManager->get(RuleFactory::class);
+/** @var RateRepository $rateRepository */
+$rateRepository = $objectManager->get(TaxRateRepositoryInterface::class);
+/** @var TaxRuleRepository $ruleRepository */
+$ruleRepository = $objectManager->get(TaxRuleRepositoryInterface::class);
+/** @var RateResource $rateResource */
+$rateResource = $objectManager->get(RateResource::class);
+/** @var RuleResource $ruleResource */
+$ruleResource = $objectManager->get(RuleResource::class);
+
+$rate = $rateFactory->create();
+$rateResource->load($rate, 'US-TEST-*-Rate-1', Rate::KEY_CODE);
+$rule = $ruleFactory->create();
+$ruleResource->load($rule, 'GraphQl Test Rule', Rule::KEY_CODE);
+$ruleRepository->delete($rule);
+$rateRepository->delete($rate);
diff --git a/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method.php b/dev/tests/integration/testsuite/Magento/GraphQl/Ups/_files/enable_ups_shipping_method.php
similarity index 81%
rename from dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method.php
rename to dev/tests/integration/testsuite/Magento/GraphQl/Ups/_files/enable_ups_shipping_method.php
index 5c6c60866fafb..42931db75a433 100644
--- a/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method.php
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Ups/_files/enable_ups_shipping_method.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167
declare(strict_types=1);
use Magento\Framework\App\Config\Storage\Writer;
@@ -15,6 +16,7 @@
$configWriter = $objectManager->get(WriterInterface::class);
$configWriter->save('carriers/ups/active', 1);
+$configWriter->save('carriers/ups/type', "UPS");
$scopeConfig = $objectManager->get(ScopeConfigInterface::class);
$scopeConfig->clean();
diff --git a/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Ups/_files/enable_ups_shipping_method_rollback.php
similarity index 78%
rename from dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method_rollback.php
rename to dev/tests/integration/testsuite/Magento/GraphQl/Ups/_files/enable_ups_shipping_method_rollback.php
index 6d7894879f97b..cf6dc08dd91a4 100644
--- a/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Ups/_files/enable_ups_shipping_method_rollback.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167
declare(strict_types=1);
use Magento\Framework\App\Config\Storage\Writer;
@@ -14,3 +15,4 @@
$configWriter = $objectManager->create(WriterInterface::class);
$configWriter->delete('carriers/ups/active');
+$configWriter->delete('carriers/ups/type');
diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php
index 93fa04806d577..0c1f2d2fcc8d7 100644
--- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php
+++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php
@@ -134,13 +134,24 @@ public function testValidateSourceException()
$this->_model->validateSource($source);
}
- public function testGetEntity()
+ /**
+ * @expectedException \Magento\Framework\Exception\LocalizedException
+ * @expectedExceptionMessage Entity is unknown
+ */
+ public function testGetUnknownEntity()
{
$entityName = 'entity_name';
$this->_model->setEntity($entityName);
$this->assertSame($entityName, $this->_model->getEntity());
}
+ public function testGetEntity()
+ {
+ $entityName = 'catalog_product';
+ $this->_model->setEntity($entityName);
+ $this->assertSame($entityName, $this->_model->getEntity());
+ }
+
/**
* @expectedException \Magento\Framework\Exception\LocalizedException
* @expectedExceptionMessage Entity is unknown
diff --git a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/Processor.php b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/Processor.php
deleted file mode 100644
index 3b2a76104a2cd..0000000000000
--- a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/Processor.php
+++ /dev/null
@@ -1,28 +0,0 @@
-getEntityId()}\n";
- }
-
- /**
- * @param \Magento\MysqlMq\Model\DataObject $message
- */
- public function processMessageWithException($message)
- {
- throw new \LogicException("Exception during message processing happened. Entity: {{$message->getEntityId()}}");
- }
-}
diff --git a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/PublisherConsumerTest.php b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/PublisherConsumerTest.php
index f03d03d3a25fd..f911165bd27fb 100644
--- a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/PublisherConsumerTest.php
+++ b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/PublisherConsumerTest.php
@@ -3,87 +3,45 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\MysqlMq\Model;
-use Magento\Framework\MessageQueue\PublisherInterface;
+use Magento\Framework\MessageQueue\UseCase\QueueTestCaseAbstract;
+use Magento\MysqlMq\Model\ResourceModel\MessageCollection;
+use Magento\MysqlMq\Model\ResourceModel\MessageStatusCollection;
/**
* Test for MySQL publisher class.
*
* @magentoDbIsolation disabled
*/
-class PublisherConsumerTest extends \PHPUnit\Framework\TestCase
+class PublisherConsumerTest extends QueueTestCaseAbstract
{
const MAX_NUMBER_OF_TRIALS = 3;
/**
- * @var \Magento\Framework\MessageQueue\PublisherInterface
+ * @var string[]
*/
- protected $publisher;
-
- /**
- * @var \Magento\Framework\ObjectManagerInterface
- */
- protected $objectManager;
-
- protected function setUp()
- {
- $this->markTestIncomplete('Should be converted to queue config v2.');
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
-
- $configPath = __DIR__ . '/../etc/queue.xml';
- $fileResolverMock = $this->createMock(\Magento\Framework\Config\FileResolverInterface::class);
- $fileResolverMock->expects($this->any())
- ->method('get')
- ->willReturn([$configPath => file_get_contents(($configPath))]);
-
- /** @var \Magento\Framework\MessageQueue\Config\Reader\Xml $xmlReader */
- $xmlReader = $this->objectManager->create(
- \Magento\Framework\MessageQueue\Config\Reader\Xml::class,
- ['fileResolver' => $fileResolverMock]
- );
-
- $newData = $xmlReader->read();
-
- /** @var \Magento\Framework\MessageQueue\Config\Data $configData */
- $configData = $this->objectManager->get(\Magento\Framework\MessageQueue\Config\Data::class);
- $configData->reset();
- $configData->merge($newData);
-
- $this->publisher = $this->objectManager->create(\Magento\Framework\MessageQueue\PublisherInterface::class);
- }
-
- protected function tearDown()
- {
- $this->markTestIncomplete('Should be converted to queue config v2.');
- $this->consumeMessages('demoConsumerQueueOne', PHP_INT_MAX);
- $this->consumeMessages('demoConsumerQueueTwo', PHP_INT_MAX);
- $this->consumeMessages('demoConsumerQueueThree', PHP_INT_MAX);
- $this->consumeMessages('demoConsumerQueueFour', PHP_INT_MAX);
- $this->consumeMessages('demoConsumerQueueFive', PHP_INT_MAX);
- $this->consumeMessages('demoConsumerQueueOneWithException', PHP_INT_MAX);
-
- $objectManagerConfiguration = [\Magento\Framework\MessageQueue\Config\Reader\Xml::class => [
- 'arguments' => [
- 'fileResolver' => ['instance' => \Magento\Framework\Config\FileResolverInterface::class],
- ],
- ],
- ];
- $this->objectManager->configure($objectManagerConfiguration);
- /** @var \Magento\Framework\MessageQueue\Config\Data $queueConfig */
- $queueConfig = $this->objectManager->get(\Magento\Framework\MessageQueue\Config\Data::class);
- $queueConfig->reset();
- }
+ protected $consumers = [
+ 'demoConsumerQueueOne',
+ 'demoConsumerQueueTwo',
+ 'demoConsumerQueueThree',
+ 'delayedOperationConsumer',
+ 'demoConsumerWithException'
+ ];
/**
* @magentoDataFixture Magento/MysqlMq/_files/queues.php
*/
public function testPublishConsumeFlow()
{
- /** @var \Magento\MysqlMq\Model\DataObjectFactory $objectFactory */
- $objectFactory = $this->objectManager->create(\Magento\MysqlMq\Model\DataObjectFactory::class);
- /** @var \Magento\MysqlMq\Model\DataObject $object */
+ /** @var \Magento\TestModuleMysqlMq\Model\DataObjectFactory $objectFactory */
+ $objectFactory = $this->objectManager->create(\Magento\TestModuleMysqlMq\Model\DataObjectFactory::class);
+ /** @var \Magento\TestModuleMysqlMq\Model\DataObject $object */
$object = $objectFactory->create();
+ $object->setOutputPath($this->logFilePath);
+ file_put_contents($this->logFilePath, '');
for ($i = 0; $i < 10; $i++) {
$object->setName('Object name ' . $i)->setEntityId($i);
$this->publisher->publish('demo.object.created', $object);
@@ -96,105 +54,87 @@ public function testPublishConsumeFlow()
$object->setName('Object name ' . $i)->setEntityId($i);
$this->publisher->publish('demo.object.custom.created', $object);
}
-
- $outputPattern = '/(Processed \d+\s)/';
- /** There are total of 10 messages in the first queue, total expected consumption is 7, 3 then 0 */
- $this->consumeMessages('demoConsumerQueueOne', 7, 7, $outputPattern);
- /** Consumer all messages which left in this queue */
- $this->consumeMessages('demoConsumerQueueOne', PHP_INT_MAX, 3, $outputPattern);
- $this->consumeMessages('demoConsumerQueueOne', 7, 0, $outputPattern);
-
- /** Verify that messages were added correctly to second queue for update and create topics */
- $this->consumeMessages('demoConsumerQueueTwo', 20, 15, $outputPattern);
-
- /** Verify that messages were NOT added to fourth queue */
- $this->consumeMessages('demoConsumerQueueFour', 11, 0, $outputPattern);
-
- /** Verify that messages were added correctly by '*' pattern in bind config to third queue */
- $this->consumeMessages('demoConsumerQueueThree', 20, 15, $outputPattern);
-
- /** Verify that messages were added correctly by '#' pattern in bind config to fifth queue */
- $this->consumeMessages('demoConsumerQueueFive', 20, 18, $outputPattern);
+ $this->waitForAsynchronousResult(18, $this->logFilePath);
+
+ //Check lines in file
+ $createdPattern = '/Processed object created \d+/';
+ $updatedPattern = '/Processed object updated \d+/';
+ $customCreatedPattern = '/Processed custom object created \d+/';
+ $logFileContents = file_get_contents($this->logFilePath);
+
+ preg_match_all($createdPattern, $logFileContents, $createdMatches);
+ $this->assertEquals(10, count($createdMatches[0]));
+ preg_match_all($updatedPattern, $logFileContents, $updatedMatches);
+ $this->assertEquals(5, count($updatedMatches[0]));
+ preg_match_all($customCreatedPattern, $logFileContents, $customCreatedMatches);
+ $this->assertEquals(3, count($customCreatedMatches[0]));
}
/**
* @magentoDataFixture Magento/MysqlMq/_files/queues.php
*/
- public function testPublishAndConsumeWithFailedJobs()
+ public function testPublishAndConsumeSchemaDefinedByMethod()
{
- /** @var \Magento\MysqlMq\Model\DataObjectFactory $objectFactory */
- $objectFactory = $this->objectManager->create(\Magento\MysqlMq\Model\DataObjectFactory::class);
- /** @var \Magento\MysqlMq\Model\DataObject $object */
- /** Try consume messages for MAX_NUMBER_OF_TRIALS and then consumer them without exception */
+ $topic = 'test.schema.defined.by.method';
+ /** @var \Magento\TestModuleMysqlMq\Model\DataObjectFactory $objectFactory */
+ $objectFactory = $this->objectManager->create(\Magento\TestModuleMysqlMq\Model\DataObjectFactory::class);
+ /** @var \Magento\TestModuleMysqlMq\Model\DataObject $object */
$object = $objectFactory->create();
- for ($i = 0; $i < 5; $i++) {
- $object->setName('Object name ' . $i)->setEntityId($i);
- $this->publisher->publish('demo.object.created', $object);
- }
- $outputPattern = '/(Processed \d+\s)/';
- for ($i = 0; $i < self::MAX_NUMBER_OF_TRIALS; $i++) {
- $this->consumeMessages('demoConsumerQueueOneWithException', PHP_INT_MAX, 0, $outputPattern);
- }
- $this->consumeMessages('demoConsumerQueueOne', PHP_INT_MAX, 0, $outputPattern);
+ $id = 33;
+ $object->setName('Object name ' . $id)->setEntityId($id);
+ $object->setOutputPath($this->logFilePath);
+ $requiredStringParam = 'Required value';
+ $optionalIntParam = 44;
+ $this->publisher->publish($topic, [$object, $requiredStringParam, $optionalIntParam]);
- /** Try consume messages for MAX_NUMBER_OF_TRIALS+1 and then consumer them without exception */
- for ($i = 0; $i < 5; $i++) {
- $object->setName('Object name ' . $i)->setEntityId($i);
- $this->publisher->publish('demo.object.created', $object);
- }
- /** Try consume messages for MAX_NUMBER_OF_TRIALS and then consumer them without exception */
- for ($i = 0; $i < self::MAX_NUMBER_OF_TRIALS + 1; $i++) {
- $this->consumeMessages('demoConsumerQueueOneWithException', PHP_INT_MAX, 0, $outputPattern);
- }
- /** Make sure that messages are not accessible anymore after number of trials is exceeded */
- $this->consumeMessages('demoConsumerQueueOne', PHP_INT_MAX, 0, $outputPattern);
+ $expectedOutput = "Processed '{$object->getEntityId()}'; "
+ . "Required param '{$requiredStringParam}'; Optional param '{$optionalIntParam}'";
+
+ $this->waitForAsynchronousResult(1, $this->logFilePath);
+
+ $this->assertEquals($expectedOutput, trim(file_get_contents($this->logFilePath)));
}
/**
* @magentoDataFixture Magento/MysqlMq/_files/queues.php
*/
- public function testPublishAndConsumeSchemaDefinedByMethod()
+ public function testConsumeWithException()
{
- /** @var \Magento\MysqlMq\Model\DataObjectFactory $objectFactory */
- $objectFactory = $this->objectManager->create(\Magento\MysqlMq\Model\DataObjectFactory::class);
- /** @var \Magento\MysqlMq\Model\DataObject $object */
+ $topic = 'demo.exception';
+ /** @var \Magento\TestModuleMysqlMq\Model\DataObjectFactory $objectFactory */
+ $objectFactory = $this->objectManager->create(\Magento\TestModuleMysqlMq\Model\DataObjectFactory::class);
+ /** @var \Magento\TestModuleMysqlMq\Model\DataObject $object */
$object = $objectFactory->create();
- $id = 33;
+ $id = 99;
+
$object->setName('Object name ' . $id)->setEntityId($id);
- $requiredStringParam = 'Required value';
- $optionalIntParam = 44;
- $this->publisher->publish('test.schema.defined.by.method', [$object, $requiredStringParam, $optionalIntParam]);
- $outputPattern = "/Processed '{$object->getEntityId()}'; "
- . "Required param '{$requiredStringParam}'; Optional param '{$optionalIntParam}'/";
- $this->consumeMessages('delayedOperationConsumer', PHP_INT_MAX, 1, $outputPattern);
+ $object->setOutputPath($this->logFilePath);
+ $this->publisher->publish($topic, $object);
+ $expectedOutput = "Exception processing {$id}";
+ $this->waitForAsynchronousResult(1, $this->logFilePath);
+ $message = $this->getTopicLatestMessage($topic);
+ $this->assertEquals($expectedOutput, trim(file_get_contents($this->logFilePath)));
+ $this->assertEquals(QueueManagement::MESSAGE_STATUS_ERROR, $message->getStatus());
}
/**
- * Make sure that consumers consume correct number of messages.
- *
- * @param string $consumerName
- * @param int|null $messagesToProcess
- * @param int|null $expectedNumberOfProcessedMessages
- * @param string|null $outputPattern
+ * @param string $topic
+ * @return Message
*/
- protected function consumeMessages(
- $consumerName,
- $messagesToProcess,
- $expectedNumberOfProcessedMessages = null,
- $outputPattern = null
- ) {
- /** @var \Magento\Framework\MessageQueue\ConsumerFactory $consumerFactory */
- $consumerFactory = $this->objectManager->create(\Magento\Framework\MessageQueue\ConsumerFactory::class);
- $consumer = $consumerFactory->get($consumerName);
- ob_start();
- $consumer->process($messagesToProcess);
- $consumersOutput = ob_get_contents();
- ob_end_clean();
- if ($outputPattern) {
- $this->assertEquals(
- $expectedNumberOfProcessedMessages,
- preg_match_all($outputPattern, $consumersOutput)
- );
- }
+ private function getTopicLatestMessage(string $topic) : Message
+ {
+ // Assert message status is error
+ $messageCollection = $this->objectManager->create(MessageCollection::class);
+ $messageStatusCollection = $this->objectManager->create(MessageStatusCollection::class);
+
+ $messageCollection->addFilter('topic_name', $topic);
+ $messageCollection->join(
+ ['status' => $messageStatusCollection->getMainTable()],
+ "status.message_id = main_table.id"
+ );
+ $messageCollection->addOrder('updated_at', MessageCollection::SORT_ORDER_DESC);
+
+ $message = $messageCollection->getFirstItem();
+ return $message;
}
}
diff --git a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php
index 197df29233297..56dd77d3da17c 100644
--- a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php
+++ b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php
@@ -5,8 +5,6 @@
*/
namespace Magento\MysqlMq\Model;
-use Magento\MysqlMq\Model\QueueManagement;
-
/**
* Test for Queue Management class.
*/
diff --git a/dev/tests/integration/testsuite/Magento/MysqlMq/etc/queue.xml b/dev/tests/integration/testsuite/Magento/MysqlMq/etc/queue.xml
deleted file mode 100644
index fd618d504df07..0000000000000
--- a/dev/tests/integration/testsuite/Magento/MysqlMq/etc/queue.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dev/tests/integration/testsuite/Magento/NewRelicReporting/Model/Module/CollectTest.php b/dev/tests/integration/testsuite/Magento/NewRelicReporting/Model/Module/CollectTest.php
new file mode 100644
index 0000000000000..5e5051163cc1f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/NewRelicReporting/Model/Module/CollectTest.php
@@ -0,0 +1,38 @@
+collect = Bootstrap::getObjectManager()->create(Collect::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function testReport()
+ {
+ $this->collect->getModuleData();
+ $moduleData = $this->collect->getModuleData();
+ $this->assertEmpty($moduleData['changes']);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php
new file mode 100644
index 0000000000000..17464b6d65861
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php
@@ -0,0 +1,134 @@
+ $paypalExpDate,
+ 'AMT' => '0.00',
+ 'RESPMSG' => 'Verified',
+ 'CVV2MATCH' => 'Y',
+ 'PNREF' => 'A10AAD866C87',
+ 'SECURETOKEN' => '3HYEHfG06skydAdBXbpIl8QJZ',
+ 'AVSDATA' => 'YNY',
+ 'RESULT' => '0',
+ 'IAVS' => 'N',
+ 'AVSADDR' => 'Y',
+ 'SECURETOKENID' => 'yqanLisRZbI0HAG8q3SbbKbhiwjNZAGf',
+ ];
+
+ $quote = $this->getQuote($reservedOrderId);
+ $this->getRequest()->setPostValue($postData);
+
+ /** @var Session $checkoutSession */
+ $checkoutSession = $this->_objectManager->get(GenericSession::class);
+ $checkoutSession->setQuoteId($quote->getId());
+ $this->setCurrentDateTime($currentDateTime);
+
+ $this->dispatch('paypal/transparent/response');
+
+ /** @var PaymentMethodManagementInterface $paymentManagment */
+ $paymentManagment = $this->_objectManager->get(PaymentMethodManagementInterface::class);
+ $payment = $paymentManagment->get($quote->getId());
+
+ $this->assertEquals($expectedCcMonth, $payment->getCcExpMonth());
+ $this->assertEquals($expectedCcYear, $payment->getCcExpYear());
+ }
+
+ /**
+ * @return array
+ */
+ public function paymentCcExpirationDateDataProvider(): array
+ {
+ return [
+ 'Expiration year in current century' => [
+ 'currentDateTime' => '2019-07-05 00:00:00',
+ 'paypalExpDate' => '0321',
+ 'expectedCcMonth' => 3,
+ 'expectedCcYear' => 2021
+ ],
+ 'Expiration year in next century' => [
+ 'currentDateTime' => '2099-01-01 00:00:00',
+ 'paypalExpDate' => '1002',
+ 'expectedCcMonth' => 10,
+ 'expectedCcYear' => 2102
+ ]
+ ];
+ }
+
+ /**
+ * Sets current date and time.
+ *
+ * @param string $date
+ */
+ private function setCurrentDateTime(string $dateTime): void
+ {
+ $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
+ $dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dateTimeFactory->method('create')
+ ->willReturn($dateTime);
+
+ $this->_objectManager->addSharedInstance($dateTimeFactory, DateTimeFactory::class);
+ }
+
+ /**
+ * Gets quote by reserved order ID.
+ *
+ * @param string $reservedOrderId
+ * @return CartInterface
+ */
+ private function getQuote(string $reservedOrderId): CartInterface
+ {
+ $searchCriteria = $this->_objectManager->get(SearchCriteriaBuilder::class)
+ ->addFilter('reserved_order_id', $reservedOrderId)
+ ->create();
+
+ /** @var CartRepositoryInterface $quoteRepository */
+ $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
+ $items = $quoteRepository->getList($searchCriteria)
+ ->getItems();
+
+ return array_pop($items);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php
index fa5da2e0e50d1..f589a0f5a1c74 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php
@@ -7,10 +7,13 @@
namespace Magento\Sales\Controller\Adminhtml\Order\Creditmemo;
+use Magento\Framework\App\Request\Http as HttpRequest;
+use Magento\Sales\Api\Data\OrderItemInterface;
+use Magento\Sales\Model\Order;
use PHPUnit\Framework\Constraint\StringContains;
/**
- * Class tests creditmemo creation in backend.
+ * Provide tests for CreditMemo save controller.
*
* @magentoDbIsolation enabled
* @magentoAppArea adminhtml
@@ -24,6 +27,8 @@ class SaveTest extends AbstractCreditmemoControllerTest
protected $uri = 'backend/sales/order_creditmemo/save';
/**
+ * @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Sales/_files/invoice.php
* @return void
*/
public function testSendEmailOnCreditmemoSave(): void
@@ -54,6 +59,91 @@ public function testSendEmailOnCreditmemoSave(): void
$this->assertThat($message->getRawMessage(), $messageConstraint);
}
+ /**
+ * Test order will keep same(custom) status after partial refund, if state has not been changed.
+ *
+ * @magentoDataFixture Magento/Sales/_files/order_with_invoice_and_custom_status.php
+ */
+ public function testOrderStatusPartialRefund()
+ {
+ /** @var Order $existingOrder */
+ $existingOrder = $this->_objectManager->create(Order::class)->loadByIncrementId('100000001');
+ $items = $this->getOrderItems($existingOrder, 1);
+ $requestParams = [
+ 'creditmemo' => [
+ 'items' => $items,
+ 'do_offline' => '1',
+ 'comment_text' => '',
+ 'shipping_amount' => '0',
+ 'adjustment_positive' => '0',
+ 'adjustment_negative' => '0',
+ ],
+ 'order_id' => $existingOrder->getId(),
+ ];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setParams($requestParams);
+ $this->dispatch('backend/sales/order_creditmemo/save');
+
+ /** @var Order $updatedOrder */
+ $updatedOrder = $this->_objectManager->create(Order::class)
+ ->loadByIncrementId($existingOrder->getIncrementId());
+
+ $this->assertSame('custom_processing', $updatedOrder->getStatus());
+ $this->assertSame('processing', $updatedOrder->getState());
+ }
+
+ /**
+ * Test order will change custom status after total refund, when state has been changed.
+ *
+ * @magentoDataFixture Magento/Sales/_files/order_with_invoice_and_custom_status.php
+ */
+ public function testOrderStatusTotalRefund()
+ {
+ /** @var Order $existingOrder */
+ $existingOrder = $this->_objectManager->create(Order::class)->loadByIncrementId('100000001');
+ $requestParams = [
+ 'creditmemo' => [
+ 'items' => $this->getOrderItems($existingOrder),
+ 'do_offline' => '1',
+ 'comment_text' => '',
+ 'shipping_amount' => '0',
+ 'adjustment_positive' => '0',
+ 'adjustment_negative' => '0',
+ ],
+ 'order_id' => $existingOrder->getId(),
+ ];
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setParams($requestParams);
+ $this->dispatch('backend/sales/order_creditmemo/save');
+
+ /** @var Order $updatedOrder */
+ $updatedOrder = $this->_objectManager->create(Order::class)
+ ->loadByIncrementId($existingOrder->getIncrementId());
+
+ $this->assertSame('complete', $updatedOrder->getStatus());
+ $this->assertSame('complete', $updatedOrder->getState());
+ }
+
+ /**
+ * Gets all items of given Order in proper format.
+ *
+ * @param Order $order
+ * @param int $subQty
+ * @return array
+ */
+ private function getOrderItems(Order $order, int $subQty = 0)
+ {
+ $items = [];
+ /** @var OrderItemInterface $item */
+ foreach ($order->getAllItems() as $item) {
+ $items[$item->getItemId()] = [
+ 'qty' => $item->getQtyOrdered() - $subQty,
+ ];
+ }
+
+ return $items;
+ }
+
/**
* @inheritdoc
*/
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Helper/AdminTest.php b/dev/tests/integration/testsuite/Magento/Sales/Helper/AdminTest.php
new file mode 100644
index 0000000000000..5d598fa90678b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Helper/AdminTest.php
@@ -0,0 +1,97 @@
+helper = Bootstrap::getObjectManager()->create(Admin::class);
+ }
+
+ /**
+ * @param string $data
+ * @param string $expected
+ * @param null|array $allowedTags
+ * @return void
+ *
+ * @dataProvider escapeHtmlWithLinksDataProvider
+ */
+ public function testEscapeHtmlWithLinks(string $data, string $expected, $allowedTags = null): void
+ {
+ $actual = $this->helper->escapeHtmlWithLinks($data, $allowedTags);
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * @return array
+ */
+ public function escapeHtmlWithLinksDataProvider(): array
+ {
+ return [
+ [
+ 'some text in tags',
+ '<a>some text in tags</a>',
+ 'allowedTags' => null,
+ ],
+ [
+ 'Transaction ID: "XX123XX"',
+ 'Transaction ID: "XX123XX"',
+ 'allowedTags' => ['b', 'br', 'strong', 'i', 'u', 'a'],
+ ],
+ [
+ 'some text in tags',
+ 'some text in tags',
+ 'allowedTags' => ['a'],
+ ],
+ [
+ "",
+ 'alert(1)',
+ 'allowedTags' => ['a'],
+ ],
+ [
+ 'Foo',
+ 'Foo',
+ 'allowedTags' => ['a'],
+ ],
+ [
+ "Foo",
+ 'Foo',
+ 'allowedTags' => ['a'],
+ ],
+ [
+ "Foo",
+ 'Foo',
+ 'allowedTags' => ['a'],
+ ],
+ [
+ "Foo",
+ 'Foo',
+ 'allowedTags' => ['a'],
+ ],
+ [
+ "Foo",
+ 'Foo',
+ 'allowedTags' => ['a'],
+ ],
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php
index 1f4253f18487c..99122d72df4b7 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php
@@ -9,6 +9,7 @@
use Magento\Sales\Model\Order\Address as OrderAddress;
use Magento\Sales\Model\Order\Payment;
+// phpcs:ignore Magento2.Security.IncludeFile
require 'order.php';
/** @var Order $order */
/** @var Order\Payment $payment */
@@ -24,8 +25,7 @@
'subtotal' => 120.00,
'base_grand_total' => 120.00,
'store_id' => 1,
- 'website_id' => 1,
- 'payment' => $payment
+ 'website_id' => 1
],
[
'increment_id' => '100000003',
@@ -35,8 +35,7 @@
'base_grand_total' => 140.00,
'subtotal' => 140.00,
'store_id' => 0,
- 'website_id' => 0,
- 'payment' => $payment
+ 'website_id' => 0
],
[
'increment_id' => '100000004',
@@ -46,8 +45,7 @@
'base_grand_total' => 140.00,
'subtotal' => 140.00,
'store_id' => 1,
- 'website_id' => 1,
- 'payment' => $payment
+ 'website_id' => 1
],
];
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_invoice_and_custom_status.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_invoice_and_custom_status.php
new file mode 100644
index 0000000000000..46d3fb547cd09
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_invoice_and_custom_status.php
@@ -0,0 +1,24 @@
+create(Status::class);
+$data = [
+ 'status' => 'custom_processing',
+ 'label' => 'Custom Processing Status',
+];
+$orderStatus->setData($data)->setStatus('custom_processing');
+$orderStatus->save();
+$orderStatus->assignState('processing');
+
+$order->setStatus('custom_processing');
+$order->save();
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_invoice_and_custom_status_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_invoice_and_custom_status_rollback.php
new file mode 100644
index 0000000000000..274cb3c74395d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_invoice_and_custom_status_rollback.php
@@ -0,0 +1,17 @@
+create(Status::class);
+$orderStatus->load('custom_processing', 'status');
+$orderStatus->delete();
diff --git a/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php b/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php
index ebf302c16bd69..0e158821f1802 100644
--- a/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php
+++ b/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php
@@ -56,7 +56,7 @@ public function testHttpsPassSecureLoginPost()
$this->prepareRequest(true);
$this->dispatch('customer/account/loginPost/');
$redirectUrl = str_replace('http://', 'https://', $this->baseUrl) .
- 'index.php/customer/account/';
+ 'customer/account/';
$this->assertResponseRedirect($this->getResponse(), $redirectUrl);
$this->assertTrue($this->_objectManager->get(Session::class)->isLoggedIn());
$this->setFrontendCompletelySecureRollback();
diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php
index bb6d1687052e3..00de5544d8fb7 100644
--- a/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php
+++ b/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php
@@ -8,7 +8,6 @@
use Magento\Catalog\Model\ProductRepository;
use Magento\Framework\App\Bootstrap;
-use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\UrlInterface;
use Magento\Store\Api\StoreRepositoryInterface;
@@ -16,6 +15,7 @@
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * phpcs:disable Magento2.Security.Superglobal
*/
class StoreTest extends \PHPUnit\Framework\TestCase
{
@@ -201,7 +201,7 @@ public function testGetBaseUrlInPub()
*/
public function testGetBaseUrlForCustomEntryPoint($type, $useCustomEntryPoint, $useStoreCode, $expected)
{
- /* config operations require store to be loaded */
+ /* config operations require store to be loaded */
$this->model->load('default');
\Magento\TestFramework\Helper\Bootstrap::getObjectManager()
->get(\Magento\Framework\App\Config\MutableScopeConfigInterface::class)
@@ -213,6 +213,10 @@ public function testGetBaseUrlForCustomEntryPoint($type, $useCustomEntryPoint, $
// emulate custom entry point
$_SERVER['SCRIPT_FILENAME'] = 'custom_entry.php';
+ $request = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->get(\Magento\Framework\App\RequestInterface::class);
+ $request->setServer(new Parameters($_SERVER));
+
if ($useCustomEntryPoint) {
$property = new \ReflectionProperty($this->model, '_isCustomEntryPoint');
$property->setAccessible(true);
@@ -298,11 +302,11 @@ public function testGetCurrentUrl()
$url = $product->getUrlInStore();
$this->assertEquals(
- $secondStore->getBaseUrl().'catalog/product/view/id/1/s/simple-product/',
+ $secondStore->getBaseUrl() . 'catalog/product/view/id/1/s/simple-product/',
$url
);
$this->assertEquals(
- $secondStore->getBaseUrl().'?___from_store=default',
+ $secondStore->getBaseUrl() . '?___from_store=default',
$secondStore->getCurrentUrl()
);
$this->assertEquals(
@@ -332,25 +336,25 @@ public function testGetCurrentUrlWithUseStoreInUrlFalse()
$product->setStoreId($secondStore->getId());
$url = $product->getUrlInStore();
- /** @var \Magento\Catalog\Model\CategoryRepository $categoryRepository */
+ /** @var \Magento\Catalog\Model\CategoryRepository $categoryRepository */
$categoryRepository = $objectManager->get(\Magento\Catalog\Model\CategoryRepository::class);
$category = $categoryRepository->get(333, $secondStore->getStoreId());
$this->assertEquals(
- $secondStore->getBaseUrl().'catalog/category/view/s/category-1/id/333/',
+ $secondStore->getBaseUrl() . 'catalog/category/view/s/category-1/id/333/',
$category->getUrl()
);
$this->assertEquals(
- $secondStore->getBaseUrl().
+ $secondStore->getBaseUrl() .
'catalog/product/view/id/333/s/simple-product-three/?___store=fixture_second_store',
$url
);
$this->assertEquals(
- $secondStore->getBaseUrl().'?___store=fixture_second_store&___from_store=default',
+ $secondStore->getBaseUrl() . '?___store=fixture_second_store&___from_store=default',
$secondStore->getCurrentUrl()
);
$this->assertEquals(
- $secondStore->getBaseUrl().'?___store=fixture_second_store',
+ $secondStore->getBaseUrl() . '?___store=fixture_second_store',
$secondStore->getCurrentUrl(false)
);
}
@@ -405,7 +409,7 @@ public function testSaveValidation($badStoreData)
/**
* @return array
*/
- public static function saveValidationDataProvider()
+ public function saveValidationDataProvider()
{
return [
'empty store name' => [['name' => '']],
diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php
index fe4067cdc49f5..042bd03b1cd4c 100644
--- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php
+++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php
@@ -3,11 +3,16 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Ups\Model;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Quote\Model\Quote\Address\RateRequestFactory;
+/**
+ * Integration tests for Carrier model class
+ */
class CarrierTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -64,12 +69,12 @@ public function testGetShipConfirmUrlLive()
/**
* @magentoConfigFixture current_store carriers/ups/active 1
+ * @magentoConfigFixture current_store carriers/ups/type UPS
* @magentoConfigFixture current_store carriers/ups/allowed_methods 1DA,GND
* @magentoConfigFixture current_store carriers/ups/free_method GND
*/
public function testCollectFreeRates()
{
- $this->markTestSkipped('Test is blocked by MAGETWO-97467.');
$rateRequest = Bootstrap::getObjectManager()->get(RateRequestFactory::class)->create();
$rateRequest->setDestCountryId('US');
$rateRequest->setDestRegionId('CA');
diff --git a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/InvalidateTokenTest.php b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/InvalidateTokenTest.php
index 672cbd7a586ec..937a26fdf0a89 100644
--- a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/InvalidateTokenTest.php
+++ b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/InvalidateTokenTest.php
@@ -89,10 +89,6 @@ public function testInvalidateTokenNoTokens()
// invalidate token
$this->getRequest()->setParam('user_id', $adminUserId);
$this->dispatch('backend/admin/user/invalidateToken');
- $this->assertSessionMessages(
- $this->equalTo(['This user has no tokens.']),
- MessageInterface::TYPE_ERROR
- );
}
public function testInvalidateTokenNoUser()
@@ -110,9 +106,5 @@ public function testInvalidateTokenInvalidUser()
// invalidate token
$this->getRequest()->setParam('user_id', $adminUserId);
$this->dispatch('backend/admin/user/invalidateToken');
- $this->assertSessionMessages(
- $this->equalTo(['This user has no tokens.']),
- MessageInterface::TYPE_ERROR
- );
}
}
diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php
index ee56158a54509..707c3442d4056 100644
--- a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php
+++ b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php
@@ -54,6 +54,19 @@ private function isUiDataProvider(\ReflectionClass $class): bool
);
}
+ /**
+ * Is given class a Layout Processor?
+ *
+ * @param \ReflectionClass $class
+ * @return bool
+ */
+ private function isLayoutProcessor(\ReflectionClass $class): bool
+ {
+ return $class->isSubclassOf(
+ \Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class
+ );
+ }
+
/**
* Is given class an HTML UI Document?
*
@@ -159,6 +172,7 @@ private function doesUseRestrictedClasses(\ReflectionClass $class): bool
* @inheritdoc
*
* @param ClassNode|ASTClass $node
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function apply(AbstractNode $node)
{
@@ -176,6 +190,7 @@ public function apply(AbstractNode $node)
&& !$this->isUiDocument($class)
&& !$this->isControllerPlugin($class)
&& !$this->isBlockPlugin($class)
+ && !$this->isLayoutProcessor($class)
) {
$this->addViolation($node, [$node->getFullQualifiedName()]);
}
diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php
index 3f477f7ce5033..33df6e8ae54f1 100644
--- a/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php
+++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php
@@ -4,6 +4,7 @@
* See COPYING.txt for license details.
*/
declare(strict_types=1);
+
namespace Magento\Sniffs\Annotation;
use PHP_CodeSniffer\Files\File;
@@ -249,6 +250,60 @@ public function validateTagGroupingFormat(File $phpcsFile, int $commentStartPtr)
}
}
+ /**
+ * Validates tag aligning format
+ *
+ * @param File $phpcsFile
+ * @param int $commentStartPtr
+ */
+ public function validateTagAligningFormat(File $phpcsFile, int $commentStartPtr) : void
+ {
+ $tokens = $phpcsFile->getTokens();
+ $noAlignmentPositions = [];
+ $actualPositions = [];
+ $stackPtr = null;
+ foreach ($tokens[$commentStartPtr]['comment_tags'] as $tag) {
+ $content = $tokens[$tag]['content'];
+ if (preg_match('/^@/', $content) && ($tokens[$tag]['line'] === $tokens[$tag + 2]['line'])) {
+ $noAlignmentPositions[] = $tokens[$tag + 1]['column'] + 1;
+ $actualPositions[] = $tokens[$tag + 2]['column'];
+ $stackPtr = $stackPtr ?? $tag;
+ }
+ }
+
+ if (!$this->allTagsAligned($actualPositions)
+ && !$this->noneTagsAligned($actualPositions, $noAlignmentPositions)) {
+ $phpcsFile->addFixableError(
+ 'Tags visual alignment must be consistent',
+ $stackPtr,
+ 'MethodArguments'
+ );
+ }
+ }
+
+ /**
+ * Check whether all docblock params are aligned.
+ *
+ * @param array $actualPositions
+ * @return bool
+ */
+ private function allTagsAligned(array $actualPositions)
+ {
+ return count(array_unique($actualPositions)) === 1;
+ }
+
+ /**
+ * Check whether all docblock params are not aligned.
+ *
+ * @param array $actualPositions
+ * @param array $noAlignmentPositions
+ * @return bool
+ */
+ private function noneTagsAligned(array $actualPositions, array $noAlignmentPositions)
+ {
+ return $actualPositions === $noAlignmentPositions;
+ }
+
/**
* Validates extra newline before short description
*
diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodAnnotationStructureSniff.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodAnnotationStructureSniff.php
index 445671d245e03..f05ae64a170d7 100644
--- a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodAnnotationStructureSniff.php
+++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodAnnotationStructureSniff.php
@@ -75,6 +75,7 @@ public function process(File $phpcsFile, $stackPtr)
$emptyTypeTokens
);
$this->annotationFormatValidator->validateTagGroupingFormat($phpcsFile, $commentStartPtr);
+ $this->annotationFormatValidator->validateTagAligningFormat($phpcsFile, $commentStartPtr);
}
}
}
diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php
index 879334d8c553b..50efca9b1ed23 100644
--- a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php
+++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php
@@ -8,8 +8,8 @@
namespace Magento\Sniffs\Annotation;
-use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
+use PHP_CodeSniffer\Sniffs\Sniff;
/**
* Sniff to validate method arguments annotations
@@ -42,7 +42,7 @@ class MethodArgumentsSniff implements Sniff
/**
* @inheritdoc
*/
- public function register() : array
+ public function register(): array
{
return [
T_FUNCTION
@@ -55,7 +55,7 @@ public function register() : array
* @param string $type
* @return bool
*/
- private function isTokenBeforeClosingCommentTagValid(string $type) : bool
+ private function isTokenBeforeClosingCommentTagValid(string $type): bool
{
return in_array($type, $this->validTokensBeforeClosingCommentTag);
}
@@ -68,7 +68,7 @@ private function isTokenBeforeClosingCommentTagValid(string $type) : bool
* @param int $stackPtr
* @return bool
*/
- private function validateCommentBlockExists(File $phpcsFile, int $previousCommentClosePtr, int $stackPtr) : bool
+ private function validateCommentBlockExists(File $phpcsFile, int $previousCommentClosePtr, int $stackPtr): bool
{
$tokens = $phpcsFile->getTokens();
for ($tempPtr = $previousCommentClosePtr + 1; $tempPtr < $stackPtr; $tempPtr++) {
@@ -85,7 +85,7 @@ private function validateCommentBlockExists(File $phpcsFile, int $previousCommen
* @param string $type
* @return bool
*/
- private function isInvalidType(string $type) : bool
+ private function isInvalidType(string $type): bool
{
return in_array(strtolower($type), $this->invalidTypes);
}
@@ -98,7 +98,7 @@ private function isInvalidType(string $type) : bool
* @param int $closedParenthesisPtr
* @return array
*/
- private function getMethodArguments(File $phpcsFile, int $openParenthesisPtr, int $closedParenthesisPtr) : array
+ private function getMethodArguments(File $phpcsFile, int $openParenthesisPtr, int $closedParenthesisPtr): array
{
$tokens = $phpcsFile->getTokens();
$methodArguments = [];
@@ -121,10 +121,11 @@ private function getMethodArguments(File $phpcsFile, int $openParenthesisPtr, in
* @param array $paramDefinitions
* @return array
*/
- private function getMethodParameters(array $paramDefinitions) : array
+ private function getMethodParameters(array $paramDefinitions): array
{
$paramName = [];
- for ($i = 0; $i < count($paramDefinitions); $i++) {
+ $paramCount = count($paramDefinitions);
+ for ($i = 0; $i < $paramCount; $i++) {
if (isset($paramDefinitions[$i]['paramName'])) {
$paramName[] = $paramDefinitions[$i]['paramName'];
}
@@ -143,7 +144,7 @@ private function validateInheritdocAnnotationWithoutBracesExists(
File $phpcsFile,
int $previousCommentOpenPtr,
int $previousCommentClosePtr
- ) : bool {
+ ): bool {
return $this->validateInheritdocAnnotationExists(
$phpcsFile,
$previousCommentOpenPtr,
@@ -163,7 +164,7 @@ private function validateInheritdocAnnotationWithBracesExists(
File $phpcsFile,
int $previousCommentOpenPtr,
int $previousCommentClosePtr
- ) : bool {
+ ): bool {
return $this->validateInheritdocAnnotationExists(
$phpcsFile,
$previousCommentOpenPtr,
@@ -186,7 +187,7 @@ private function validateInheritdocAnnotationExists(
int $previousCommentOpenPtr,
int $previousCommentClosePtr,
string $inheritdocAnnotation
- ) : bool {
+ ): bool {
$tokens = $phpcsFile->getTokens();
for ($ptr = $previousCommentOpenPtr; $ptr < $previousCommentClosePtr; $ptr++) {
if (strtolower($tokens[$ptr]['content']) === $inheritdocAnnotation) {
@@ -213,7 +214,7 @@ private function validateParameterAnnotationForArgumentExists(
int $previousCommentOpenPtr,
int $previousCommentClosePtr,
int $stackPtr
- ) : void {
+ ): void {
if ($argumentsCount > 0 && $parametersCount === 0) {
$inheritdocAnnotationWithoutBracesExists = $this->validateInheritdocAnnotationWithoutBracesExists(
$phpcsFile,
@@ -256,7 +257,7 @@ private function validateCommentBlockDoesnotHaveExtraParameterAnnotation(
int $argumentsCount,
int $parametersCount,
int $stackPtr
- ) : void {
+ ): void {
if ($argumentsCount < $parametersCount && $argumentsCount > 0) {
$phpcsFile->addFixableError(
'Extra @param found in method annotation',
@@ -287,10 +288,10 @@ private function validateArgumentNameInParameterAnnotationExists(
File $phpcsFile,
array $methodArguments,
array $paramDefinitions
- ) : void {
+ ): void {
$parameterNames = $this->getMethodParameters($paramDefinitions);
if (!in_array($methodArguments[$ptr], $parameterNames)) {
- $error = $methodArguments[$ptr]. ' parameter is missing in method annotation';
+ $error = $methodArguments[$ptr] . ' parameter is missing in method annotation';
$phpcsFile->addFixableError($error, $stackPtr, 'MethodArguments');
}
}
@@ -310,7 +311,7 @@ private function validateParameterPresentInMethodSignature(
array $methodArguments,
File $phpcsFile,
array $paramPointers
- ) : void {
+ ): void {
if (!in_array($paramDefinitionsArguments, $methodArguments)) {
$phpcsFile->addFixableError(
$paramDefinitionsArguments . ' parameter is missing in method arguments signature',
@@ -333,7 +334,7 @@ private function validateParameterOrderIsCorrect(
array $methodArguments,
File $phpcsFile,
array $paramPointers
- ) : void {
+ ): void {
$parameterNames = $this->getMethodParameters($paramDefinitions);
$paramDefinitionsCount = count($paramDefinitions);
for ($ptr = 0; $ptr < $paramDefinitionsCount; $ptr++) {
@@ -342,7 +343,7 @@ private function validateParameterOrderIsCorrect(
) {
if ($methodArguments[$ptr] != $parameterNames[$ptr]) {
$phpcsFile->addFixableError(
- $methodArguments[$ptr].' parameter is not in order',
+ $methodArguments[$ptr] . ' parameter is not in order',
$paramPointers[$ptr],
'MethodArguments'
);
@@ -366,15 +367,16 @@ private function validateDuplicateAnnotationDoesnotExists(
array $paramPointers,
File $phpcsFile,
array $methodArguments
- ) : void {
+ ): void {
$argumentsCount = count($methodArguments);
$parametersCount = count($paramPointers);
if ($argumentsCount <= $parametersCount && $argumentsCount > 0) {
$duplicateParameters = [];
- for ($i = 0; $i < sizeof($paramDefinitions); $i++) {
+ $paramCount = count($paramDefinitions);
+ for ($i = 0; $i < $paramCount; $i++) {
if (isset($paramDefinitions[$i]['paramName'])) {
$parameterContent = $paramDefinitions[$i]['paramName'];
- for ($j = $i + 1; $j < count($paramDefinitions); $j++) {
+ for ($j = $i + 1; $j < $paramCount; $j++) {
if (isset($paramDefinitions[$j]['paramName'])
&& $parameterContent === $paramDefinitions[$j]['paramName']
) {
@@ -408,7 +410,7 @@ private function validateParameterAnnotationFormatIsCorrect(
array $methodArguments,
array $paramDefinitions,
array $paramPointers
- ) : void {
+ ): void {
switch (count($paramDefinitions)) {
case 0:
$phpcsFile->addFixableError(
@@ -429,7 +431,7 @@ private function validateParameterAnnotationFormatIsCorrect(
case 2:
if ($this->isInvalidType($paramDefinitions[0])) {
$phpcsFile->addFixableError(
- $paramDefinitions[0].' is not a valid PHP type',
+ $paramDefinitions[0] . ' is not a valid PHP type',
$paramPointers[$ptr],
'MethodArguments'
);
@@ -451,7 +453,7 @@ private function validateParameterAnnotationFormatIsCorrect(
);
if ($this->isInvalidType($paramDefinitions[0])) {
$phpcsFile->addFixableError(
- $paramDefinitions[0].' is not a valid PHP type',
+ $paramDefinitions[0] . ' is not a valid PHP type',
$paramPointers[$ptr],
'MethodArguments'
);
@@ -480,7 +482,7 @@ private function validateMethodParameterAnnotations(
array $methodArguments,
int $previousCommentOpenPtr,
int $previousCommentClosePtr
- ) : void {
+ ): void {
$argumentCount = count($methodArguments);
$paramCount = count($paramPointers);
$this->validateParameterAnnotationForArgumentExists(
@@ -510,8 +512,14 @@ private function validateMethodParameterAnnotations(
$phpcsFile,
$paramPointers
);
- for ($ptr = 0; $ptr < count($methodArguments); $ptr++) {
- $tokens = $phpcsFile->getTokens();
+ $this->validateFormattingConsistency(
+ $paramDefinitions,
+ $methodArguments,
+ $phpcsFile,
+ $paramPointers
+ );
+ $tokens = $phpcsFile->getTokens();
+ for ($ptr = 0; $ptr < $argumentCount; $ptr++) {
if (isset($paramPointers[$ptr])) {
$this->validateArgumentNameInParameterAnnotationExists(
$stackPtr,
@@ -520,7 +528,7 @@ private function validateMethodParameterAnnotations(
$methodArguments,
$paramDefinitions
);
- $paramContent = $tokens[$paramPointers[$ptr]+2]['content'];
+ $paramContent = $tokens[$paramPointers[$ptr] + 2]['content'];
$paramContentExplode = explode(' ', $paramContent);
$this->validateParameterAnnotationFormatIsCorrect(
$ptr,
@@ -540,36 +548,40 @@ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$numTokens = count($tokens);
- $previousCommentOpenPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr-1, 0);
- $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr-1, 0);
+ $previousCommentOpenPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1, 0);
+ $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr - 1, 0);
if (!$this->validateCommentBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr)) {
$phpcsFile->addError('Comment block is missing', $stackPtr, 'MethodArguments');
return;
}
- $openParenthesisPtr = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr+1, $numTokens);
- $closedParenthesisPtr = $phpcsFile->findNext(T_CLOSE_PARENTHESIS, $stackPtr+1, $numTokens);
+ $openParenthesisPtr = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr + 1, $numTokens);
+ $closedParenthesisPtr = $phpcsFile->findNext(T_CLOSE_PARENTHESIS, $stackPtr + 1, $numTokens);
$methodArguments = $this->getMethodArguments($phpcsFile, $openParenthesisPtr, $closedParenthesisPtr);
$paramPointers = $paramDefinitions = [];
for ($tempPtr = $previousCommentOpenPtr; $tempPtr < $previousCommentClosePtr; $tempPtr++) {
if (strtolower($tokens[$tempPtr]['content']) === '@param') {
$paramPointers[] = $tempPtr;
- $paramAnnotationParts = explode(' ', $tokens[$tempPtr+2]['content']);
+ $content = preg_replace('/\s+/', ' ', $tokens[$tempPtr + 2]['content'], 2);
+ $paramAnnotationParts = explode(' ', $content, 3);
if (count($paramAnnotationParts) === 1) {
if ((preg_match('/^\$.*/', $paramAnnotationParts[0]))) {
$paramDefinitions[] = [
'type' => null,
- 'paramName' => rtrim(ltrim($tokens[$tempPtr+2]['content'], '&'), ',')
+ 'paramName' => rtrim(ltrim($tokens[$tempPtr + 2]['content'], '&'), ','),
+ 'comment' => null
];
} else {
$paramDefinitions[] = [
- 'type' => $tokens[$tempPtr+2]['content'],
- 'paramName' => null
+ 'type' => $tokens[$tempPtr + 2]['content'],
+ 'paramName' => null,
+ 'comment' => null
];
}
} else {
$paramDefinitions[] = [
'type' => $paramAnnotationParts[0],
- 'paramName' => rtrim(ltrim($paramAnnotationParts[1], '&'), ',')
+ 'paramName' => rtrim(ltrim($paramAnnotationParts[1], '&'), ','),
+ 'comment' => $paramAnnotationParts[2] ?? null,
];
}
}
@@ -584,4 +596,81 @@ public function process(File $phpcsFile, $stackPtr)
$previousCommentClosePtr
);
}
+
+ /**
+ * Validates function params format consistency.
+ *
+ * @param array $paramDefinitions
+ * @param array $methodArguments
+ * @param File $phpcsFile
+ * @param array $paramPointers
+ *
+ * @see https://devdocs.magento.com/guides/v2.3/coding-standards/docblock-standard-general.html#format-consistency
+ */
+ private function validateFormattingConsistency(
+ array $paramDefinitions,
+ array $methodArguments,
+ File $phpcsFile,
+ array $paramPointers
+ ): void {
+ $argumentPositions = [];
+ $commentPositions = [];
+ $tokens = $phpcsFile->getTokens();
+ $argumentCount = count($methodArguments);
+ for ($ptr = 0; $ptr < $argumentCount; $ptr++) {
+ if (isset($paramPointers[$ptr])) {
+ $paramContent = $tokens[$paramPointers[$ptr] + 2]['content'];
+ $paramDefinition = $paramDefinitions[$ptr];
+ $argumentPositions[] = strpos($paramContent, $paramDefinition['paramName']);
+ $commentPositions[] = $paramDefinition['comment']
+ ? strpos($paramContent, $paramDefinition['comment']) : null;
+ }
+ }
+ if (!$this->allParamsAligned($argumentPositions, $commentPositions)
+ && !$this->noneParamsAligned($argumentPositions, $commentPositions, $paramDefinitions)) {
+ $phpcsFile->addFixableError(
+ 'Visual alignment must be consistent',
+ $paramPointers[0],
+ 'MethodArguments'
+ );
+ }
+ }
+
+ /**
+ * Check all params are aligned.
+ *
+ * @param array $argumentPositions
+ * @param array $commentPositions
+ * @return bool
+ */
+ private function allParamsAligned(array $argumentPositions, array $commentPositions): bool
+ {
+ return count(array_unique($argumentPositions)) === 1
+ && count(array_unique(array_filter($commentPositions))) <= 1;
+ }
+
+ /**
+ * Check none of params are aligned.
+ *
+ * @param array $argumentPositions
+ * @param array $commentPositions
+ * @param array $paramDefinitions
+ * @return bool
+ */
+ private function noneParamsAligned(array $argumentPositions, array $commentPositions, array $paramDefinitions): bool
+ {
+ $flag = true;
+ foreach ($argumentPositions as $index => $argumentPosition) {
+ $commentPosition = $commentPositions[$index];
+ $type = $paramDefinitions[$index]['type'];
+ $paramName = $paramDefinitions[$index]['paramName'];
+ if (($argumentPosition !== strlen($type) + 1) ||
+ (isset($commentPosition) && ($commentPosition !== $argumentPosition + strlen($paramName) + 1))) {
+ $flag = false;
+ break;
+ }
+ }
+
+ return $flag;
+ }
}
diff --git a/lib/internal/Magento/Framework/App/Action/HttpGetActionInterface.php b/lib/internal/Magento/Framework/App/Action/HttpGetActionInterface.php
index 308b77aa8dbcf..c3d3d2d6fd5ec 100644
--- a/lib/internal/Magento/Framework/App/Action/HttpGetActionInterface.php
+++ b/lib/internal/Magento/Framework/App/Action/HttpGetActionInterface.php
@@ -8,12 +8,10 @@
namespace Magento\Framework\App\Action;
-use Magento\Framework\App\ActionInterface;
-
/**
* Marker for actions processing GET requests.
*/
-interface HttpGetActionInterface extends ActionInterface
+interface HttpGetActionInterface extends HttpHeadActionInterface
{
}
diff --git a/lib/internal/Magento/Framework/App/Action/HttpHeadActionInterface.php b/lib/internal/Magento/Framework/App/Action/HttpHeadActionInterface.php
index d2f9b70913c1f..389bd8089967b 100644
--- a/lib/internal/Magento/Framework/App/Action/HttpHeadActionInterface.php
+++ b/lib/internal/Magento/Framework/App/Action/HttpHeadActionInterface.php
@@ -12,6 +12,8 @@
/**
* Marker for actions processing HEAD requests.
+ *
+ * @deprecated Both GET and HEAD requests map to HttpGetActionInterface
*/
interface HttpHeadActionInterface extends ActionInterface
{
diff --git a/lib/internal/Magento/Framework/App/DocRootLocator.php b/lib/internal/Magento/Framework/App/DocRootLocator.php
index 6fb35c42f1330..d73baf8e4e742 100644
--- a/lib/internal/Magento/Framework/App/DocRootLocator.php
+++ b/lib/internal/Magento/Framework/App/DocRootLocator.php
@@ -3,10 +3,12 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Framework\App;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\ReadFactory;
/**
@@ -20,18 +22,26 @@ class DocRootLocator
private $request;
/**
+ * @deprecated
* @var ReadFactory
*/
private $readFactory;
+ /**
+ * @var Filesystem
+ */
+ private $filesystem;
+
/**
* @param RequestInterface $request
* @param ReadFactory $readFactory
+ * @param Filesystem|null $filesystem
*/
- public function __construct(RequestInterface $request, ReadFactory $readFactory)
+ public function __construct(RequestInterface $request, ReadFactory $readFactory, Filesystem $filesystem = null)
{
$this->request = $request;
$this->readFactory = $readFactory;
+ $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class);
}
/**
@@ -42,7 +52,8 @@ public function __construct(RequestInterface $request, ReadFactory $readFactory)
public function isPub()
{
$rootBasePath = $this->request->getServer('DOCUMENT_ROOT');
- $readDirectory = $this->readFactory->create(DirectoryList::ROOT);
- return (substr($rootBasePath, -strlen('/pub')) === '/pub') && !$readDirectory->isExist($rootBasePath . 'setup');
+ $readDirectory = $this->filesystem->getDirectoryRead(DirectoryList::ROOT);
+
+ return (substr($rootBasePath, -\strlen('/pub')) === '/pub') && ! $readDirectory->isExist('setup');
}
}
diff --git a/lib/internal/Magento/Framework/App/Http.php b/lib/internal/Magento/Framework/App/Http.php
index 23024a44c2def..ca3976da1df52 100644
--- a/lib/internal/Magento/Framework/App/Http.php
+++ b/lib/internal/Magento/Framework/App/Http.php
@@ -3,17 +3,18 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Framework\App;
use Magento\Framework\App\Filesystem\DirectoryList;
-use Magento\Framework\Debug;
-use Magento\Framework\ObjectManager\ConfigLoaderInterface;
use Magento\Framework\App\Request\Http as RequestHttp;
use Magento\Framework\App\Response\Http as ResponseHttp;
use Magento\Framework\App\Response\HttpInterface;
use Magento\Framework\Controller\ResultInterface;
+use Magento\Framework\Debug;
use Magento\Framework\Event;
use Magento\Framework\Filesystem;
+use Magento\Framework\ObjectManager\ConfigLoaderInterface;
/**
* HTTP web application. Called from webroot index.php to serve web requests.
@@ -143,12 +144,31 @@ public function launch()
} else {
throw new \InvalidArgumentException('Invalid return type');
}
+ if ($this->_request->isHead() && $this->_response->getHttpResponseCode() == 200) {
+ $this->handleHeadRequest();
+ }
// This event gives possibility to launch something before sending output (allow cookie setting)
$eventParams = ['request' => $this->_request, 'response' => $this->_response];
$this->_eventManager->dispatch('controller_front_send_response_before', $eventParams);
return $this->_response;
}
+ /**
+ * Handle HEAD requests by adding the Content-Length header and removing the body from the response.
+ *
+ * @return void
+ */
+ private function handleHeadRequest()
+ {
+ // It is possible that some PHP installations have overloaded strlen to use mb_strlen instead.
+ // This means strlen might return the actual number of characters in a non-ascii string instead
+ // of the number of bytes. Use mb_strlen explicitly with a single byte character encoding to ensure
+ // that the content length is calculated in bytes.
+ $contentLength = mb_strlen($this->_response->getContent(), '8bit');
+ $this->_response->clearBody();
+ $this->_response->setHeader('Content-Length', $contentLength);
+ }
+
/**
* @inheritdoc
*/
@@ -248,7 +268,7 @@ private function redirectToSetup(Bootstrap $bootstrap, \Exception $exception)
. "because the Magento setup directory cannot be accessed. \n"
. 'You can install Magento using either the command line or you must restore access '
. 'to the following directory: ' . $setupInfo->getDir($projectRoot) . "\n";
-
+ // phpcs:ignore Magento2.Exceptions.DirectThrow
throw new \Exception($newMessage, 0, $exception);
}
}
@@ -257,13 +277,14 @@ private function redirectToSetup(Bootstrap $bootstrap, \Exception $exception)
* Handler for bootstrap errors
*
* @param Bootstrap $bootstrap
- * @param \Exception &$exception
+ * @param \Exception $exception
* @return bool
*/
private function handleBootstrapErrors(Bootstrap $bootstrap, \Exception &$exception)
{
$bootstrapCode = $bootstrap->getErrorCode();
if (Bootstrap::ERR_MAINTENANCE == $bootstrapCode) {
+ // phpcs:ignore Magento2.Security.IncludeFile
require $this->_filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/503.php');
return true;
}
@@ -304,6 +325,7 @@ private function handleInitException(\Exception $exception)
{
if ($exception instanceof \Magento\Framework\Exception\State\InitException) {
$this->getLogger()->critical($exception);
+ // phpcs:ignore Magento2.Security.IncludeFile
require $this->_filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/404.php');
return true;
}
@@ -335,6 +357,7 @@ private function handleGenericReport(Bootstrap $bootstrap, \Exception $exception
if (isset($params['SCRIPT_NAME'])) {
$reportData['script_name'] = $params['SCRIPT_NAME'];
}
+ // phpcs:ignore Magento2.Security.IncludeFile
require $this->_filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/report.php');
return true;
}
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php
index 23afbbc73d2b9..ef4152ba2e49e 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php
@@ -8,6 +8,9 @@
use Magento\Framework\App\DocRootLocator;
+/**
+ * Test for Magento\Framework\App\DocRootLocator class.
+ */
class DocRootLocatorTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -21,11 +24,15 @@ public function testIsPub($path, $isExist, $result)
{
$request = $this->createMock(\Magento\Framework\App\Request\Http::class);
$request->expects($this->once())->method('getServer')->willReturn($path);
+
+ $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class);
+
$reader = $this->createMock(\Magento\Framework\Filesystem\Directory\Read::class);
+ $filesystem = $this->createMock(\Magento\Framework\Filesystem::class);
+ $filesystem->expects($this->once())->method('getDirectoryRead')->willReturn($reader);
$reader->expects($this->any())->method('isExist')->willReturn($isExist);
- $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class);
- $readFactory->expects($this->once())->method('create')->willReturn($reader);
- $model = new DocRootLocator($request, $readFactory);
+
+ $model = new DocRootLocator($request, $readFactory, $filesystem);
$this->assertSame($result, $model->isPub());
}
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php
index a299e04e152cc..dbb315e88a526 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php
@@ -92,7 +92,7 @@ protected function setUp()
'pathInfoProcessor' => $pathInfoProcessorMock,
'objectManager' => $objectManagerMock
])
- ->setMethods(['getFrontName'])
+ ->setMethods(['getFrontName', 'isHead'])
->getMock();
$this->areaListMock = $this->getMockBuilder(\Magento\Framework\App\AreaList::class)
->disableOriginalConstructor()
@@ -135,12 +135,17 @@ private function setUpLaunch()
{
$frontName = 'frontName';
$areaCode = 'areaCode';
- $this->requestMock->expects($this->once())->method('getFrontName')->will($this->returnValue($frontName));
+ $this->requestMock->expects($this->once())
+ ->method('getFrontName')
+ ->willReturn($frontName);
$this->areaListMock->expects($this->once())
->method('getCodeByFrontName')
- ->with($frontName)->will($this->returnValue($areaCode));
+ ->with($frontName)
+ ->willReturn($areaCode);
$this->configLoaderMock->expects($this->once())
- ->method('load')->with($areaCode)->will($this->returnValue([]));
+ ->method('load')
+ ->with($areaCode)
+ ->willReturn([]);
$this->objectManagerMock->expects($this->once())->method('configure')->with([]);
$this->objectManagerMock->expects($this->once())
->method('get')
@@ -149,12 +154,15 @@ private function setUpLaunch()
$this->frontControllerMock->expects($this->once())
->method('dispatch')
->with($this->requestMock)
- ->will($this->returnValue($this->responseMock));
+ ->willReturn($this->responseMock);
}
public function testLaunchSuccess()
{
$this->setUpLaunch();
+ $this->requestMock->expects($this->once())
+ ->method('isHead')
+ ->willReturn(false);
$this->eventManagerMock->expects($this->once())
->method('dispatch')
->with(
@@ -171,33 +179,101 @@ public function testLaunchSuccess()
public function testLaunchException()
{
$this->setUpLaunch();
- $this->frontControllerMock->expects($this->once())->method('dispatch')->with($this->requestMock)->will(
- $this->returnCallback(
- function () {
- throw new \Exception('Message');
- }
- )
- );
+ $this->frontControllerMock->expects($this->once())
+ ->method('dispatch')
+ ->with($this->requestMock)
+ ->willThrowException(
+ new \Exception('Message')
+ );
$this->http->launch();
}
+ /**
+ * Test that HEAD requests lead to an empty body and a Content-Length header matching the original body size.
+ * @dataProvider dataProviderForTestLaunchHeadRequest
+ * @param string $body
+ * @param int $expectedLength
+ */
+ public function testLaunchHeadRequest($body, $expectedLength)
+ {
+ $this->setUpLaunch();
+ $this->requestMock->expects($this->once())
+ ->method('isHead')
+ ->willReturn(true);
+ $this->responseMock->expects($this->once())
+ ->method('getHttpResponseCode')
+ ->willReturn(200);
+ $this->responseMock->expects($this->once())
+ ->method('getContent')
+ ->willReturn($body);
+ $this->responseMock->expects($this->once())
+ ->method('clearBody')
+ ->willReturn($this->responseMock);
+ $this->responseMock->expects($this->once())
+ ->method('setHeader')
+ ->with('Content-Length', $expectedLength)
+ ->willReturn($this->responseMock);
+ $this->eventManagerMock->expects($this->once())
+ ->method('dispatch')
+ ->with(
+ 'controller_front_send_response_before',
+ ['request' => $this->requestMock, 'response' => $this->responseMock]
+ );
+ $this->assertSame($this->responseMock, $this->http->launch());
+ }
+
+ /**
+ * Different test content for responseMock with their expected lengths in bytes.
+ * @return array
+ */
+ public function dataProviderForTestLaunchHeadRequest(): array
+ {
+ return [
+ [
+ "Test", // Ascii text
+ 43 // Expected Content-Length
+ ],
+ [
+ "部落格", // Multi-byte characters
+ 48 // Expected Content-Length
+ ],
+ [
+ "\0", // Null byte
+ 40 // Expected Content-Length
+ ],
+ [
+ "خرید", // LTR text
+ 47 // Expected Content-Length
+ ]
+ ];
+ }
+
public function testHandleDeveloperModeNotInstalled()
{
$dir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\ReadInterface::class);
- $dir->expects($this->once())->method('getAbsolutePath')->willReturn(__DIR__);
+ $dir->expects($this->once())
+ ->method('getAbsolutePath')
+ ->willReturn(__DIR__);
$this->filesystemMock->expects($this->once())
->method('getDirectoryRead')
->with(DirectoryList::ROOT)
->willReturn($dir);
- $this->responseMock->expects($this->once())->method('setRedirect')->with('/_files/');
- $this->responseMock->expects($this->once())->method('sendHeaders');
+ $this->responseMock->expects($this->once())
+ ->method('setRedirect')
+ ->with('/_files/');
+ $this->responseMock->expects($this->once())
+ ->method('sendHeaders');
$bootstrap = $this->getBootstrapNotInstalled();
- $bootstrap->expects($this->once())->method('getParams')->willReturn([
- 'SCRIPT_NAME' => '/index.php',
- 'DOCUMENT_ROOT' => __DIR__,
- 'SCRIPT_FILENAME' => __DIR__ . '/index.php',
- SetupInfo::PARAM_NOT_INSTALLED_URL_PATH => '_files',
- ]);
+ $bootstrap->expects($this->once())
+ ->method('getParams')
+ ->willReturn(
+ [
+ 'SCRIPT_NAME' => '/index.php',
+ 'DOCUMENT_ROOT' => __DIR__,
+ 'SCRIPT_FILENAME' => __DIR__ . '/index.php',
+ SetupInfo::PARAM_NOT_INSTALLED_URL_PATH => '_files',
+ ]
+ );
$this->assertTrue($this->http->catchException($bootstrap, new \Exception('Test Message')));
}
@@ -206,24 +282,37 @@ public function testHandleDeveloperMode()
$this->filesystemMock->expects($this->once())
->method('getDirectoryRead')
->will($this->throwException(new \Exception('strange error')));
- $this->responseMock->expects($this->once())->method('setHttpResponseCode')->with(500);
- $this->responseMock->expects($this->once())->method('setHeader')->with('Content-Type', 'text/plain');
+ $this->responseMock->expects($this->once())
+ ->method('setHttpResponseCode')
+ ->with(500);
+ $this->responseMock->expects($this->once())
+ ->method('setHeader')
+ ->with('Content-Type', 'text/plain');
$constraint = new \PHPUnit\Framework\Constraint\StringStartsWith('1 exception(s):');
- $this->responseMock->expects($this->once())->method('setBody')->with($constraint);
- $this->responseMock->expects($this->once())->method('sendResponse');
+ $this->responseMock->expects($this->once())
+ ->method('setBody')
+ ->with($constraint);
+ $this->responseMock->expects($this->once())
+ ->method('sendResponse');
$bootstrap = $this->getBootstrapNotInstalled();
- $bootstrap->expects($this->once())->method('getParams')->willReturn(
- ['DOCUMENT_ROOT' => 'something', 'SCRIPT_FILENAME' => 'something/else']
- );
+ $bootstrap->expects($this->once())
+ ->method('getParams')
+ ->willReturn(
+ ['DOCUMENT_ROOT' => 'something', 'SCRIPT_FILENAME' => 'something/else']
+ );
$this->assertTrue($this->http->catchException($bootstrap, new \Exception('Test')));
}
public function testCatchExceptionSessionException()
{
- $this->responseMock->expects($this->once())->method('setRedirect');
- $this->responseMock->expects($this->once())->method('sendHeaders');
+ $this->responseMock->expects($this->once())
+ ->method('setRedirect');
+ $this->responseMock->expects($this->once())
+ ->method('sendHeaders');
$bootstrap = $this->createMock(\Magento\Framework\App\Bootstrap::class);
- $bootstrap->expects($this->once())->method('isDeveloperMode')->willReturn(false);
+ $bootstrap->expects($this->once())
+ ->method('isDeveloperMode')
+ ->willReturn(false);
$this->assertTrue($this->http->catchException(
$bootstrap,
new \Magento\Framework\Exception\SessionException(new \Magento\Framework\Phrase('Test'))
@@ -238,8 +327,12 @@ public function testCatchExceptionSessionException()
private function getBootstrapNotInstalled()
{
$bootstrap = $this->createMock(\Magento\Framework\App\Bootstrap::class);
- $bootstrap->expects($this->once())->method('isDeveloperMode')->willReturn(true);
- $bootstrap->expects($this->once())->method('getErrorCode')->willReturn(Bootstrap::ERR_IS_INSTALLED);
+ $bootstrap->expects($this->once())
+ ->method('isDeveloperMode')
+ ->willReturn(true);
+ $bootstrap->expects($this->once())
+ ->method('getErrorCode')
+ ->willReturn(Bootstrap::ERR_IS_INSTALLED);
return $bootstrap;
}
}
diff --git a/lib/internal/Magento/Framework/Code/Generator.php b/lib/internal/Magento/Framework/Code/Generator.php
index 4dec7d1a28146..b46c8c681bb52 100644
--- a/lib/internal/Magento/Framework/Code/Generator.php
+++ b/lib/internal/Magento/Framework/Code/Generator.php
@@ -8,11 +8,15 @@
use Magento\Framework\Code\Generator\DefinedClasses;
use Magento\Framework\Code\Generator\EntityAbstract;
use Magento\Framework\Code\Generator\Io;
+use Magento\Framework\ObjectManager\ConfigInterface;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Magento\Framework\Filesystem\Driver\File;
use Psr\Log\LoggerInterface;
+/**
+ * Class code generator.
+ */
class Generator
{
const GENERATION_SUCCESS = 'success';
@@ -232,7 +236,21 @@ protected function shouldSkipGeneration($resultEntityType, $sourceClassName, $re
{
if (!$resultEntityType || !$sourceClassName) {
return self::GENERATION_ERROR;
- } elseif ($this->definedClasses->isClassLoadableFromDisk($resultClass)) {
+ }
+
+ /** @var ConfigInterface $omConfig */
+ $omConfig = $this->objectManager->get(ConfigInterface::class);
+ $virtualTypes = $omConfig->getVirtualTypes();
+
+ /**
+ * Do not try to autogenerate virtual types
+ * For example virtual types with names overlapping autogenerated suffixes
+ */
+ if (isset($virtualTypes[$resultClass])) {
+ return self::GENERATION_SKIP;
+ }
+
+ if ($this->definedClasses->isClassLoadableFromDisk($resultClass)) {
$generatedFileName = $this->_ioObject->generateResultFileName($resultClass);
/**
* Must handle two edge cases: a competing process has generated the class and written it to disc already,
@@ -244,9 +262,12 @@ protected function shouldSkipGeneration($resultEntityType, $sourceClassName, $re
$this->_ioObject->includeFile($generatedFileName);
}
return self::GENERATION_SKIP;
- } elseif (!isset($this->_generatedEntities[$resultEntityType])) {
+ }
+
+ if (!isset($this->_generatedEntities[$resultEntityType])) {
throw new \InvalidArgumentException('Unknown generation entity.');
}
+
return false;
}
}
diff --git a/lib/internal/Magento/Framework/Code/Test/Unit/GeneratorTest.php b/lib/internal/Magento/Framework/Code/Test/Unit/GeneratorTest.php
index 9cc93f7620b1f..2753561683385 100644
--- a/lib/internal/Magento/Framework/Code/Test/Unit/GeneratorTest.php
+++ b/lib/internal/Magento/Framework/Code/Test/Unit/GeneratorTest.php
@@ -3,11 +3,15 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Framework\Code\Test\Unit;
use Magento\Framework\Code\Generator;
use Magento\Framework\Code\Generator\DefinedClasses;
use Magento\Framework\Code\Generator\Io;
+use Magento\Framework\ObjectManager\ConfigInterface;
+use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Magento\Framework\ObjectManager\Code\Generator\Factory;
use Magento\Framework\ObjectManager\Code\Generator\Proxy;
@@ -17,13 +21,19 @@
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Code\Generator\EntityAbstract;
use Magento\GeneratedClass\Factory as GeneratedClassFactory;
+use RuntimeException;
+/**
+ * Tests for code generator.
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class GeneratorTest extends TestCase
{
/**
* Class name parameter value
*/
- const SOURCE_CLASS = 'testClassName';
+ private const SOURCE_CLASS = 'testClassName';
/**
* Expected generated entities
@@ -58,6 +68,19 @@ class GeneratorTest extends TestCase
*/
private $loggerMock;
+ /**
+ * @var ObjectManagerInterface|MockObject
+ */
+ private $objectManagerMock;
+
+ /**
+ * @var ConfigInterface|MockObject
+ */
+ private $objectManagerConfigMock;
+
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
$this->definedClassesMock = $this->createMock(DefinedClasses::class);
@@ -65,6 +88,12 @@ protected function setUp()
->disableOriginalConstructor()
->getMock();
$this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class);
+ $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->objectManagerConfigMock = $this->getMockBuilder(ConfigInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
$this->model = new Generator(
$this->ioObjectMock,
@@ -78,7 +107,7 @@ protected function setUp()
);
}
- public function testGetGeneratedEntities()
+ public function testGetGeneratedEntities(): void
{
$this->model = new Generator(
$this->ioObjectMock,
@@ -91,22 +120,58 @@ public function testGetGeneratedEntities()
/**
* @param string $className
* @param string $entityType
- * @expectedException \RuntimeException
+ * @expectedException RuntimeException
* @dataProvider generateValidClassDataProvider
*/
- public function testGenerateClass($className, $entityType)
+ public function testGenerateClass($className, $entityType): void
{
- $objectManagerMock = $this->createMock(ObjectManagerInterface::class);
$fullClassName = $className . $entityType;
+
$entityGeneratorMock = $this->getMockBuilder(EntityAbstract::class)
->disableOriginalConstructor()
->getMock();
- $objectManagerMock->expects($this->once())->method('create')->willReturn($entityGeneratorMock);
- $this->model->setObjectManager($objectManagerMock);
- $this->model->generateClass($fullClassName);
+ $this->objectManagerMock
+ ->expects($this->once())
+ ->method('create')
+ ->willReturn($entityGeneratorMock);
+
+ $this->objectManagerConfigMock
+ ->expects($this->once())
+ ->method('getVirtualTypes')
+ ->willReturn([]);
+ $this->objectManagerMock
+ ->expects($this->once())
+ ->method('get')
+ ->with(ConfigInterface::class)
+ ->willReturn($this->objectManagerConfigMock);
+ $this->model->setObjectManager($this->objectManagerMock);
+
+ $this->assertSame(
+ Generator::GENERATION_SUCCESS,
+ $this->model->generateClass($fullClassName)
+ );
}
- public function testGenerateClassWithWrongName()
+ public function testShouldNotGenerateVirtualType(): void
+ {
+ $this->objectManagerConfigMock
+ ->expects($this->once())
+ ->method('getVirtualTypes')
+ ->willReturn([GeneratedClassFactory::class => GeneratedClassFactory::class]);
+ $this->objectManagerMock
+ ->expects($this->once())
+ ->method('get')
+ ->with(ConfigInterface::class)
+ ->willReturn($this->objectManagerConfigMock);
+ $this->model->setObjectManager($this->objectManagerMock);
+
+ $this->assertSame(
+ Generator::GENERATION_SKIP,
+ $this->model->generateClass(GeneratedClassFactory::class)
+ );
+ }
+
+ public function testGenerateClassWithWrongName(): void
{
$this->assertEquals(
Generator::GENERATION_ERROR,
@@ -115,25 +180,42 @@ public function testGenerateClassWithWrongName()
}
/**
- * @expectedException \RuntimeException
+ * @expectedException RuntimeException
*/
- public function testGenerateClassWhenClassIsNotGenerationSuccess()
+ public function testGenerateClassWhenClassIsNotGenerationSuccess(): void
{
$expectedEntities = array_values($this->expectedEntities);
$resultClassName = self::SOURCE_CLASS . ucfirst(array_shift($expectedEntities));
- $objectManagerMock = $this->createMock(ObjectManagerInterface::class);
+
$entityGeneratorMock = $this->getMockBuilder(EntityAbstract::class)
->disableOriginalConstructor()
->getMock();
- $objectManagerMock->expects($this->once())->method('create')->willReturn($entityGeneratorMock);
- $this->model->setObjectManager($objectManagerMock);
- $this->model->generateClass($resultClassName);
+ $this->objectManagerMock
+ ->expects($this->once())
+ ->method('create')
+ ->willReturn($entityGeneratorMock);
+
+ $this->objectManagerConfigMock
+ ->expects($this->once())
+ ->method('getVirtualTypes')
+ ->willReturn([]);
+ $this->objectManagerMock
+ ->expects($this->once())
+ ->method('get')
+ ->with(ConfigInterface::class)
+ ->willReturn($this->objectManagerConfigMock);
+ $this->model->setObjectManager($this->objectManagerMock);
+
+ $this->assertSame(
+ Generator::GENERATION_SUCCESS,
+ $this->model->generateClass($resultClassName)
+ );
}
/**
* @inheritdoc
*/
- public function testGenerateClassWithErrors()
+ public function testGenerateClassWithErrors(): void
{
$expectedEntities = array_values($this->expectedEntities);
$resultClassName = self::SOURCE_CLASS . ucfirst(array_shift($expectedEntities));
@@ -148,17 +230,15 @@ public function testGenerateClassWithErrors()
. 'directory permission is set to write --- the requested class did not generate properly, then '
. 'you must add the generated class object to the signature of the related construct method, only.';
$FinalErrorMessage = implode(PHP_EOL, $errorMessages) . "\n" . $mainErrorMessage;
- $this->expectException(\RuntimeException::class);
+ $this->expectException(RuntimeException::class);
$this->expectExceptionMessage($FinalErrorMessage);
- /** @var ObjectManagerInterface|Mock $objectManagerMock */
- $objectManagerMock = $this->createMock(ObjectManagerInterface::class);
/** @var EntityAbstract|Mock $entityGeneratorMock */
$entityGeneratorMock = $this->getMockBuilder(EntityAbstract::class)
->disableOriginalConstructor()
->getMock();
- $objectManagerMock->expects($this->once())
+ $this->objectManagerMock->expects($this->once())
->method('create')
->willReturn($entityGeneratorMock);
$entityGeneratorMock->expects($this->once())
@@ -177,26 +257,62 @@ public function testGenerateClassWithErrors()
$this->loggerMock->expects($this->once())
->method('critical')
->with($FinalErrorMessage);
- $this->model->setObjectManager($objectManagerMock);
- $this->model->generateClass($resultClassName);
+
+ $this->objectManagerConfigMock
+ ->expects($this->once())
+ ->method('getVirtualTypes')
+ ->willReturn([]);
+ $this->objectManagerMock
+ ->expects($this->once())
+ ->method('get')
+ ->with(ConfigInterface::class)
+ ->willReturn($this->objectManagerConfigMock);
+ $this->model->setObjectManager($this->objectManagerMock);
+
+ $this->assertSame(
+ Generator::GENERATION_SUCCESS,
+ $this->model->generateClass($resultClassName)
+ );
}
/**
* @dataProvider trueFalseDataProvider
+ * @param $fileExists
*/
- public function testGenerateClassWithExistName($fileExists)
+ public function testGenerateClassWithExistName($fileExists): void
{
$this->definedClassesMock->expects($this->any())
->method('isClassLoadableFromDisk')
->willReturn(true);
$resultClassFileName = '/Magento/Path/To/Class.php';
- $this->ioObjectMock->expects($this->once())->method('generateResultFileName')->willReturn($resultClassFileName);
- $this->ioObjectMock->expects($this->once())->method('fileExists')->willReturn($fileExists);
+
+ $this->objectManagerConfigMock
+ ->expects($this->once())
+ ->method('getVirtualTypes')
+ ->willReturn([]);
+ $this->objectManagerMock
+ ->expects($this->once())
+ ->method('get')
+ ->with(ConfigInterface::class)
+ ->willReturn($this->objectManagerConfigMock);
+ $this->model->setObjectManager($this->objectManagerMock);
+
+ $this->ioObjectMock
+ ->expects($this->once())
+ ->method('generateResultFileName')
+ ->willReturn($resultClassFileName);
+ $this->ioObjectMock
+ ->expects($this->once())
+ ->method('fileExists')
+ ->willReturn($fileExists);
+
$includeFileInvokeCount = $fileExists ? 1 : 0;
- $this->ioObjectMock->expects($this->exactly($includeFileInvokeCount))->method('includeFile');
+ $this->ioObjectMock
+ ->expects($this->exactly($includeFileInvokeCount))
+ ->method('includeFile');
- $this->assertEquals(
+ $this->assertSame(
Generator::GENERATION_SKIP,
$this->model->generateClass(GeneratedClassFactory::class)
);
@@ -205,7 +321,7 @@ public function testGenerateClassWithExistName($fileExists)
/**
* @return array
*/
- public function trueFalseDataProvider()
+ public function trueFalseDataProvider(): array
{
return [[true], [false]];
}
@@ -215,7 +331,7 @@ public function trueFalseDataProvider()
*
* @return array
*/
- public function generateValidClassDataProvider()
+ public function generateValidClassDataProvider(): array
{
$data = [];
foreach ($this->expectedEntities as $generatedEntity) {
diff --git a/lib/internal/Magento/Framework/Config/Dom.php b/lib/internal/Magento/Framework/Config/Dom.php
index f4721660d8da6..5c97c996634dd 100644
--- a/lib/internal/Magento/Framework/Config/Dom.php
+++ b/lib/internal/Magento/Framework/Config/Dom.php
@@ -190,9 +190,20 @@ protected function _mergeNode(\DOMElement $node, $parentPath)
/* override node value */
if ($this->_isTextNode($node)) {
/* skip the case when the matched node has children, otherwise they get overridden */
- if (!$matchedNode->hasChildNodes() || $this->_isTextNode($matchedNode)) {
+ if (!$matchedNode->hasChildNodes()
+ || $this->_isTextNode($matchedNode)
+ || $this->isCdataNode($matchedNode)
+ ) {
$matchedNode->nodeValue = $node->childNodes->item(0)->nodeValue;
}
+ } elseif ($this->isCdataNode($node) && $this->_isTextNode($matchedNode)) {
+ /* Replace text node with CDATA section */
+ if ($this->findCdataSection($node)) {
+ $matchedNode->nodeValue = $this->findCdataSection($node)->nodeValue;
+ }
+ } elseif ($this->isCdataNode($node) && $this->isCdataNode($matchedNode)) {
+ /* Replace CDATA with new one */
+ $this->replaceCdataNode($matchedNode, $node);
} else {
/* recursive merge for all child nodes */
foreach ($node->childNodes as $childNode) {
@@ -220,6 +231,56 @@ protected function _isTextNode($node)
return $node->childNodes->length == 1 && $node->childNodes->item(0) instanceof \DOMText;
}
+ /**
+ * Check if the node content is CDATA (probably surrounded with text nodes) or just text node
+ *
+ * @param \DOMNode $node
+ * @return bool
+ */
+ private function isCdataNode($node)
+ {
+ // If every child node of current is NOT \DOMElement
+ // It is arbitrary combination of text nodes and CDATA sections.
+ foreach ($node->childNodes as $childNode) {
+ if ($childNode instanceof \DOMElement) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Finds CDATA section from given node children
+ *
+ * @param \DOMNode $node
+ * @return \DOMCdataSection|null
+ */
+ private function findCdataSection($node)
+ {
+ foreach ($node->childNodes as $childNode) {
+ if ($childNode instanceof \DOMCdataSection) {
+ return $childNode;
+ }
+ }
+ }
+
+ /**
+ * Replaces CDATA section in $oldNode with $newNode's
+ *
+ * @param \DOMNode $oldNode
+ * @param \DOMNode $newNode
+ */
+ private function replaceCdataNode($oldNode, $newNode)
+ {
+ $oldCdata = $this->findCdataSection($oldNode);
+ $newCdata = $this->findCdataSection($newNode);
+
+ if ($oldCdata && $newCdata) {
+ $oldCdata->nodeValue = $newCdata->nodeValue;
+ }
+ }
+
/**
* Merges attributes of the merge node to the base node
*
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php b/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php
index 0508b5e4fb359..73968ac1ed897 100644
--- a/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Framework\Config\Test\Unit;
+/**
+ * Test for \Magento\Framework\Config\Dom class.
+ */
class DomTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -62,6 +65,37 @@ public function mergeDataProvider()
['override_node.xml', 'override_node_new.xml', [], null, 'override_node_merged.xml'],
['override_node_new.xml', 'override_node.xml', [], null, 'override_node_merged.xml'],
['text_node.xml', 'text_node_new.xml', [], null, 'text_node_merged.xml'],
+ 'text node replaced with cdata' => [
+ 'text_node_cdata.xml',
+ 'text_node_cdata_new.xml',
+ [],
+ null,
+ 'text_node_cdata_merged.xml'
+ ],
+ 'cdata' => ['cdata.xml', 'cdata_new.xml', [], null, 'cdata_merged.xml'],
+ 'cdata with html' => ['cdata_html.xml', 'cdata_html_new.xml', [], null, 'cdata_html_merged.xml'],
+ 'cdata replaced with text node' => [
+ 'cdata_text.xml',
+ 'cdata_text_new.xml',
+ [],
+ null,
+ 'cdata_text_merged.xml'
+ ],
+ 'big cdata' => ['big_cdata.xml', 'big_cdata_new.xml', [], null, 'big_cdata_merged.xml'],
+ 'big cdata with attribute' => [
+ 'big_cdata_attribute.xml',
+ 'big_cdata_attribute_new.xml',
+ [],
+ null,
+ 'big_cdata_attribute_merged.xml'
+ ],
+ 'big cdata replaced with text' => [
+ 'big_cdata_text.xml',
+ 'big_cdata_text_new.xml',
+ [],
+ null,
+ 'big_cdata_text_merged.xml'
+ ],
[
'recursive.xml',
'recursive_new.xml',
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata.xml
new file mode 100644
index 0000000000000..69eb0035958e6
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute.xml
new file mode 100644
index 0000000000000..12a9389e3d238
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute_merged.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute_merged.xml
new file mode 100644
index 0000000000000..6e95d843e34ba
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute_merged.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute_new.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute_new.xml
new file mode 100644
index 0000000000000..b905781a9fe50
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_attribute_new.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_merged.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_merged.xml
new file mode 100644
index 0000000000000..b905781a9fe50
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_merged.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_new.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_new.xml
new file mode 100644
index 0000000000000..b905781a9fe50
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_new.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text.xml
new file mode 100644
index 0000000000000..69eb0035958e6
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text_merged.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text_merged.xml
new file mode 100644
index 0000000000000..3e37e67ffcf35
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text_merged.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Some Other Phrase
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text_new.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text_new.xml
new file mode 100644
index 0000000000000..3e37e67ffcf35
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/big_cdata_text_new.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Some Other Phrase
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata.xml
new file mode 100644
index 0000000000000..f65a21e122394
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html.xml
new file mode 100644
index 0000000000000..15294f46445ec
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Phrase]]>
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html_merged.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html_merged.xml
new file mode 100644
index 0000000000000..709d921f737e4
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html_merged.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Other
Phrase]]>
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html_new.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html_new.xml
new file mode 100644
index 0000000000000..709d921f737e4
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_html_new.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Other
Phrase]]>
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_merged.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_merged.xml
new file mode 100644
index 0000000000000..e6d2d809d7f7f
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_merged.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_new.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_new.xml
new file mode 100644
index 0000000000000..e6d2d809d7f7f
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_new.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text.xml
new file mode 100644
index 0000000000000..f65a21e122394
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text_merged.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text_merged.xml
new file mode 100644
index 0000000000000..3e37e67ffcf35
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text_merged.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Some Other Phrase
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text_new.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text_new.xml
new file mode 100644
index 0000000000000..3e37e67ffcf35
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/cdata_text_new.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Some Other Phrase
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata.xml
new file mode 100644
index 0000000000000..6807872aa3d3a
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ Some Phrase
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata_merged.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata_merged.xml
new file mode 100644
index 0000000000000..b905781a9fe50
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata_merged.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata_new.xml b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata_new.xml
new file mode 100644
index 0000000000000..b905781a9fe50
--- /dev/null
+++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/dom/text_node_cdata_new.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/DB/Helper/Mysql/Fulltext.php b/lib/internal/Magento/Framework/DB/Helper/Mysql/Fulltext.php
index 5c50faf71a854..5660098157ace 100644
--- a/lib/internal/Magento/Framework/DB/Helper/Mysql/Fulltext.php
+++ b/lib/internal/Magento/Framework/DB/Helper/Mysql/Fulltext.php
@@ -14,13 +14,6 @@
*/
class Fulltext
{
- /**
- * Characters that have special meaning in fulltext match syntax
- *
- * @var string
- */
- const SPECIAL_CHARACTERS = '-+<>*()~';
-
/**
* FULLTEXT search in MySQL search mode "natural language"
*/
@@ -106,15 +99,4 @@ public function match($select, $columns, $expression, $isCondition = true, $mode
return $select;
}
-
- /**
- * Remove special characters from fulltext query expression
- *
- * @param string $expression
- * @return string
- */
- public function removeSpecialCharacters(string $expression): string
- {
- return str_replace(str_split(static::SPECIAL_CHARACTERS), '', $expression);
- }
}
diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php
index 9c789e81913c4..128d3d8e9fd3d 100644
--- a/lib/internal/Magento/Framework/Data/Collection.php
+++ b/lib/internal/Magento/Framework/Data/Collection.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Framework\Data;
use Magento\Framework\Data\Collection\EntityFactoryInterface;
@@ -391,7 +392,7 @@ public function getItemByColumnValue($column, $value)
/**
* Adding item to item array
*
- * @param \Magento\Framework\DataObject $item
+ * @param \Magento\Framework\DataObject $item
* @return $this
* @throws \Exception
*/
@@ -401,6 +402,7 @@ public function addItem(\Magento\Framework\DataObject $item)
if ($itemId !== null) {
if (isset($this->_items[$itemId])) {
+ //phpcs:ignore Magento2.Exceptions.DirectThrow
throw new \Exception(
'Item (' . get_class($item) . ') with the same ID "' . $item->getId() . '" already exists.'
);
@@ -452,7 +454,7 @@ public function getAllIds()
/**
* Remove item from collection by item key
*
- * @param mixed $key
+ * @param mixed $key
* @return $this
*/
public function removeItemByKey($key)
@@ -483,6 +485,7 @@ public function clear()
{
$this->_setIsLoaded(false);
$this->_items = [];
+ $this->_totalRecords = null;
return $this;
}
@@ -539,8 +542,8 @@ public function each($objMethod, $args = [])
/**
* Setting data for all collection items
*
- * @param mixed $key
- * @param mixed $value
+ * @param mixed $key
+ * @param mixed $value
* @return $this
*/
public function setDataToAll($key, $value = null)
@@ -560,7 +563,7 @@ public function setDataToAll($key, $value = null)
/**
* Set current page
*
- * @param int $page
+ * @param int $page
* @return $this
*/
public function setCurPage($page)
@@ -572,7 +575,7 @@ public function setCurPage($page)
/**
* Set collection page size
*
- * @param int $size
+ * @param int $size
* @return $this
*/
public function setPageSize($size)
@@ -584,8 +587,8 @@ public function setPageSize($size)
/**
* Set select order
*
- * @param string $field
- * @param string $direction
+ * @param string $field
+ * @param string $direction
* @return $this
*/
public function setOrder($field, $direction = self::SORT_ORDER_DESC)
@@ -597,7 +600,7 @@ public function setOrder($field, $direction = self::SORT_ORDER_DESC)
/**
* Set collection item class name
*
- * @param string $className
+ * @param string $className
* @return $this
* @throws \InvalidArgumentException
*/
diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Textarea.php b/lib/internal/Magento/Framework/Data/Form/Element/Textarea.php
index 7cd3fb1f7fb99..1970ebeb9544e 100644
--- a/lib/internal/Magento/Framework/Data/Form/Element/Textarea.php
+++ b/lib/internal/Magento/Framework/Data/Form/Element/Textarea.php
@@ -4,15 +4,15 @@
* See COPYING.txt for license details.
*/
-/**
- * Form textarea element
- *
- * @author Magento Core Team
- */
namespace Magento\Framework\Data\Form\Element;
use Magento\Framework\Escaper;
+/**
+ * Form textarea element.
+ *
+ * @author Magento Core Team
+ */
class Textarea extends AbstractElement
{
/**
@@ -64,6 +64,7 @@ public function getHtmlAttributes()
'rows',
'cols',
'readonly',
+ 'maxlength',
'disabled',
'onkeyup',
'tabindex',
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/TextareaTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/TextareaTest.php
index e99df6c4c6e6f..eec85ca35775d 100644
--- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/TextareaTest.php
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/TextareaTest.php
@@ -4,11 +4,11 @@
* See COPYING.txt for license details.
*/
-/**
- * Tests for \Magento\Framework\Data\Form\Element\Textarea
- */
namespace Magento\Framework\Data\Test\Unit\Form\Element;
+/**
+ * Tests for \Magento\Framework\Data\Form\Element\Textarea class.
+ */
class TextareaTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -76,6 +76,7 @@ public function testGetHtmlAttributes()
'rows',
'cols',
'readonly',
+ 'maxlength',
'disabled',
'onkeyup',
'tabindex',
diff --git a/lib/internal/Magento/Framework/Encryption/Crypt.php b/lib/internal/Magento/Framework/Encryption/Crypt.php
index d52db40b395ab..930cfa7a44f68 100644
--- a/lib/internal/Magento/Framework/Encryption/Crypt.php
+++ b/lib/internal/Magento/Framework/Encryption/Crypt.php
@@ -41,13 +41,13 @@ class Crypt
/**
* Constructor
*
- * @param string $key Secret encryption key.
- * It's unsafe to store encryption key in memory, so no getter for key exists.
- * @param string $cipher Cipher algorithm (one of the MCRYPT_ciphername constants)
- * @param string $mode Mode of cipher algorithm (MCRYPT_MODE_modeabbr constants)
- * @param string|bool $initVector Initial vector to fill algorithm blocks.
- * TRUE generates a random initial vector.
- * FALSE fills initial vector with zero bytes to not use it.
+ * @param string $key Secret encryption key.
+ * It's unsafe to store encryption key in memory, so no getter for key exists.
+ * @param string $cipher Cipher algorithm (one of the MCRYPT_ciphername constants)
+ * @param string $mode Mode of cipher algorithm (MCRYPT_MODE_modeabbr constants)
+ * @param string|bool $initVector Initial vector to fill algorithm blocks.
+ * TRUE generates a random initial vector.
+ * FALSE fills initial vector with zero bytes to not use it.
* @throws \Exception
*/
public function __construct(
@@ -66,7 +66,7 @@ public function __construct(
$allowedCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$initVector = '';
for ($i = 0; $i < $initVectorSize; $i++) {
- $initVector .= $allowedCharacters[rand(0, strlen($allowedCharacters) - 1)];
+ $initVector .= $allowedCharacters[random_int(0, strlen($allowedCharacters) - 1)];
}
// @codingStandardsIgnoreStart
@mcrypt_generic_deinit($handle);
diff --git a/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php b/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php
index d945791282a2d..023c4cc4ddba6 100644
--- a/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php
+++ b/lib/internal/Magento/Framework/File/Test/Unit/Transfer/Adapter/HttpTest.php
@@ -3,24 +3,37 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
namespace Magento\Framework\File\Test\Unit\Transfer\Adapter;
-use \Magento\Framework\File\Transfer\Adapter\Http;
+use Magento\Framework\File\Transfer\Adapter\Http;
+use Magento\Framework\File\Mime;
+use Magento\Framework\HTTP\PhpEnvironment\Response;
+use Magento\Framework\App\Request\Http as RequestHttp;
+use PHPUnit\Framework\MockObject\MockObject;
+/**
+ * Tests http transfer adapter.
+ */
class HttpTest extends \PHPUnit\Framework\TestCase
{
/**
- * @var \Magento\Framework\HTTP\PhpEnvironment\Response|\PHPUnit_Framework_MockObject_MockObject
+ * @var RequestHttp|MockObject
+ */
+ private $request;
+
+ /**
+ * @var Response|MockObject
*/
private $response;
/**
- * @var Http|\PHPUnit_Framework_MockObject_MockObject
+ * @var Http|MockObject
*/
private $object;
/**
- * @var \Magento\Framework\File\Mime|\PHPUnit_Framework_MockObject_MockObject
+ * @var Mime|MockObject
*/
private $mime;
@@ -30,11 +43,15 @@ class HttpTest extends \PHPUnit\Framework\TestCase
protected function setUp()
{
$this->response = $this->createPartialMock(
- \Magento\Framework\HTTP\PhpEnvironment\Response::class,
+ Response::class,
['setHeader', 'sendHeaders', 'setHeaders']
);
- $this->mime = $this->createMock(\Magento\Framework\File\Mime::class);
- $this->object = new Http($this->response, $this->mime);
+ $this->mime = $this->createMock(Mime::class);
+ $this->request = $this->createPartialMock(
+ RequestHttp::class,
+ ['isHead']
+ );
+ $this->object = new Http($this->response, $this->mime, $this->request);
}
/**
@@ -56,7 +73,10 @@ public function testSend(): void
$this->mime->expects($this->once())
->method('getMimeType')
->with($file)
- ->will($this->returnValue($contentType));
+ ->willReturn($contentType);
+ $this->request->expects($this->once())
+ ->method('isHead')
+ ->willReturn(false);
$this->expectOutputString(file_get_contents($file));
$this->object->send($file);
@@ -82,7 +102,10 @@ public function testSendWithOptions(): void
$this->mime->expects($this->once())
->method('getMimeType')
->with($file)
- ->will($this->returnValue($contentType));
+ ->willReturn($contentType);
+ $this->request->expects($this->once())
+ ->method('isHead')
+ ->willReturn(false);
$this->expectOutputString(file_get_contents($file));
$this->object->send(['filepath' => $file, 'headers' => $headers]);
@@ -106,4 +129,32 @@ public function testSendNoFileExistException(): void
{
$this->object->send('nonexistent.file');
}
+
+ /**
+ * @return void
+ */
+ public function testSendHeadRequest(): void
+ {
+ $file = __DIR__ . '/../../_files/javascript.js';
+ $contentType = 'content/type';
+
+ $this->response->expects($this->at(0))
+ ->method('setHeader')
+ ->with('Content-length', filesize($file));
+ $this->response->expects($this->at(1))
+ ->method('setHeader')
+ ->with('Content-Type', $contentType);
+ $this->response->expects($this->once())
+ ->method('sendHeaders');
+ $this->mime->expects($this->once())
+ ->method('getMimeType')
+ ->with($file)
+ ->willReturn($contentType);
+ $this->request->expects($this->once())
+ ->method('isHead')
+ ->willReturn(true);
+
+ $this->object->send($file);
+ $this->assertEquals(false, $this->hasOutput());
+ }
}
diff --git a/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php b/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php
index aa527866eff55..cd42c8d04b477 100644
--- a/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php
+++ b/lib/internal/Magento/Framework/File/Transfer/Adapter/Http.php
@@ -6,28 +6,46 @@
namespace Magento\Framework\File\Transfer\Adapter;
+use Magento\Framework\HTTP\PhpEnvironment\Response;
+use Magento\Framework\File\Mime;
+use Magento\Framework\App\Request\Http as HttpRequest;
+use Magento\Framework\App\ObjectManager;
+use Zend\Http\Headers;
+
+/**
+ * File adapter to send the file to the client.
+ */
class Http
{
/**
- * @var \Magento\Framework\HTTP\PhpEnvironment\Response
+ * @var Response
*/
private $response;
/**
- * @var \Magento\Framework\File\Mime
+ * @var Mime
*/
private $mime;
/**
- * @param \Magento\Framework\App\Response\Http $response
- * @param \Magento\Framework\File\Mime $mime
+ * @var HttpRequest
+ */
+ private $request;
+
+ /**
+ * @param Response $response
+ * @param Mime $mime
+ * @param HttpRequest|null $request
*/
public function __construct(
- \Magento\Framework\HTTP\PhpEnvironment\Response $response,
- \Magento\Framework\File\Mime $mime
+ Response $response,
+ Mime $mime,
+ HttpRequest $request = null
) {
$this->response = $response;
$this->mime = $mime;
+ $objectManager = ObjectManager::getInstance();
+ $this->request = $request ?: $objectManager->get(HttpRequest::class);
}
/**
@@ -46,18 +64,17 @@ public function send($options = null)
throw new \InvalidArgumentException("File '{$filepath}' does not exists.");
}
- $mimeType = $this->mime->getMimeType($filepath);
- if (is_array($options) && isset($options['headers']) && $options['headers'] instanceof \Zend\Http\Headers) {
- $this->response->setHeaders($options['headers']);
- }
- $this->response->setHeader('Content-length', filesize($filepath));
- $this->response->setHeader('Content-Type', $mimeType);
+ $this->prepareResponse($options, $filepath);
- $this->response->sendHeaders();
+ if ($this->request->isHead()) {
+ // Do not send the body on HEAD requests.
+ return;
+ }
$handle = fopen($filepath, 'r');
if ($handle) {
while (($buffer = fgets($handle, 4096)) !== false) {
+ // phpcs:ignore Magento2.Security.LanguageConstruct.DirectOutput
echo $buffer;
}
if (!feof($handle)) {
@@ -88,4 +105,22 @@ private function getFilePath($options): string
return $filePath;
}
+
+ /**
+ * Set and send all necessary headers.
+ *
+ * @param array $options
+ * @param string $filepath
+ */
+ private function prepareResponse($options, string $filepath): void
+ {
+ $mimeType = $this->mime->getMimeType($filepath);
+ if (is_array($options) && isset($options['headers']) && $options['headers'] instanceof Headers) {
+ $this->response->setHeaders($options['headers']);
+ }
+ $this->response->setHeader('Content-length', filesize($filepath));
+ $this->response->setHeader('Content-Type', $mimeType);
+
+ $this->response->sendHeaders();
+ }
}
diff --git a/lib/internal/Magento/Framework/Setup/Patch/DependentPatchInterface.php b/lib/internal/Magento/Framework/Setup/Patch/DependentPatchInterface.php
index abda94a0e6f8e..7573441fa9466 100644
--- a/lib/internal/Magento/Framework/Setup/Patch/DependentPatchInterface.php
+++ b/lib/internal/Magento/Framework/Setup/Patch/DependentPatchInterface.php
@@ -6,7 +6,7 @@
namespace Magento\Framework\Setup\Patch;
/**
- * Each patch can have dependecies, that should be applied before such patch
+ * Each patch can have dependencies, that should be applied before such patch
*
* / Patch2 --- Patch3
* /
@@ -20,7 +20,7 @@ interface DependentPatchInterface
/**
* Get array of patches that have to be executed prior to this.
*
- * example of implementation:
+ * Example of implementation:
*
* [
* \Vendor_Name\Module_Name\Setup\Patch\Patch1::class,
diff --git a/lib/internal/Magento/Framework/Setup/Test/Unit/Patch/PatchApplierTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/Patch/PatchApplierTest.php
index f89bdc9e137dd..cb40845bcc488 100644
--- a/lib/internal/Magento/Framework/Setup/Test/Unit/Patch/PatchApplierTest.php
+++ b/lib/internal/Magento/Framework/Setup/Test/Unit/Patch/PatchApplierTest.php
@@ -91,7 +91,7 @@ class PatchApplierTest extends \PHPUnit\Framework\TestCase
/**
* @var PatchBackwardCompatability |\PHPUnit_Framework_MockObject_MockObject
*/
- private $patchBacwardCompatability;
+ private $patchBackwardCompatability;
protected function setUp()
{
@@ -109,7 +109,7 @@ protected function setUp()
$this->moduleDataSetupMock->expects($this->any())->method('getConnection')->willReturn($this->connectionMock);
$objectManager = new ObjectManager($this);
- $this->patchBacwardCompatability = $objectManager->getObject(
+ $this->patchBackwardCompatability = $objectManager->getObject(
PatchBackwardCompatability::class,
[
'moduleResource' => $this->moduleResourceMock
@@ -128,7 +128,7 @@ protected function setUp()
'objectManager' => $this->objectManagerMock,
'schemaSetup' => $this->schemaSetupMock,
'moduleDataSetup' => $this->moduleDataSetupMock,
- 'patchBackwardCompatability' => $this->patchBacwardCompatability
+ 'patchBackwardCompatability' => $this->patchBackwardCompatability
]
);
require_once __DIR__ . '/../_files/data_patch_classes.php';
diff --git a/lib/internal/Magento/Framework/Test/Unit/Data/CollectionTest.php b/lib/internal/Magento/Framework/Test/Unit/Data/CollectionTest.php
index 75f8b1cd0633f..913d5a577e62a 100644
--- a/lib/internal/Magento/Framework/Test/Unit/Data/CollectionTest.php
+++ b/lib/internal/Magento/Framework/Test/Unit/Data/CollectionTest.php
@@ -57,6 +57,27 @@ public function testWalk()
);
}
+ /**
+ * Ensure that getSize works correctly with clear
+ *
+ */
+ public function testClearTotalRecords()
+ {
+ $objOne = new \Magento\Framework\DataObject(['id' => 1, 'name' => 'one']);
+ $objTwo = new \Magento\Framework\DataObject(['id' => 2, 'name' => 'two']);
+ $objThree = new \Magento\Framework\DataObject(['id' => 3, 'name' => 'three']);
+
+ /** @noinspection PhpUnhandledExceptionInspection */
+ $this->collection->addItem($objOne);
+ /** @noinspection PhpUnhandledExceptionInspection */
+ $this->collection->addItem($objTwo);
+ /** @noinspection PhpUnhandledExceptionInspection */
+ $this->collection->addItem($objThree);
+ $this->assertEquals(3, $this->collection->getSize());
+ $this->collection->clear();
+ $this->assertEquals(0, $this->collection->getSize());
+ }
+
/**
* Callback function.
*
diff --git a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php
index 1e8eb74bf5414..315c3ee204864 100644
--- a/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php
+++ b/lib/internal/Magento/Framework/View/Asset/MergeStrategy/Direct.php
@@ -6,6 +6,8 @@
namespace Magento\Framework\View\Asset\MergeStrategy;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Math\Random;
use Magento\Framework\View\Asset;
/**
@@ -30,38 +32,46 @@ class Direct implements \Magento\Framework\View\Asset\MergeStrategyInterface
*/
private $cssUrlResolver;
+ /**
+ * @var Random
+ */
+ private $mathRandom;
+
/**
* @param \Magento\Framework\Filesystem $filesystem
* @param \Magento\Framework\View\Url\CssResolver $cssUrlResolver
+ * @param Random|null $mathRandom
*/
public function __construct(
\Magento\Framework\Filesystem $filesystem,
- \Magento\Framework\View\Url\CssResolver $cssUrlResolver
+ \Magento\Framework\View\Url\CssResolver $cssUrlResolver,
+ Random $mathRandom = null
) {
$this->filesystem = $filesystem;
$this->cssUrlResolver = $cssUrlResolver;
+ $this->mathRandom = $mathRandom ?: ObjectManager::getInstance()->get(Random::class);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function merge(array $assetsToMerge, Asset\LocalInterface $resultAsset)
{
$mergedContent = $this->composeMergedContent($assetsToMerge, $resultAsset);
$filePath = $resultAsset->getPath();
+ $tmpFilePath = $filePath . $this->mathRandom->getUniqueHash('_');
$staticDir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW);
$tmpDir = $this->filesystem->getDirectoryWrite(DirectoryList::TMP);
- $tmpDir->writeFile($filePath, $mergedContent);
- $tmpDir->renameFile($filePath, $filePath, $staticDir);
+ $tmpDir->writeFile($tmpFilePath, $mergedContent);
+ $tmpDir->renameFile($tmpFilePath, $filePath, $staticDir);
}
/**
* Merge files together and modify content if needed
*
- * @param \Magento\Framework\View\Asset\MergeableInterface[] $assetsToMerge
- * @param \Magento\Framework\View\Asset\LocalInterface $resultAsset
- * @return string
- * @throws \Magento\Framework\Exception\LocalizedException
+ * @param array $assetsToMerge
+ * @param Asset\LocalInterface $resultAsset
+ * @return array|string
*/
private function composeMergedContent(array $assetsToMerge, Asset\LocalInterface $resultAsset)
{
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php
index 823ad5c8c8be7..c23f13745c79f 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/MergeStrategy/DirectTest.php
@@ -5,13 +5,19 @@
*/
namespace Magento\Framework\View\Test\Unit\Asset\MergeStrategy;
-use Magento\Framework\Filesystem\Directory\WriteInterface;
-use \Magento\Framework\View\Asset\MergeStrategy\Direct;
-
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Filesystem\Directory\WriteInterface;
+use Magento\Framework\View\Asset\MergeStrategy\Direct;
+/**
+ * Test for Magento\Framework\View\Asset\MergeStrategy\Direct.
+ */
class DirectTest extends \PHPUnit\Framework\TestCase
{
+ /**
+ * @var \Magento\Framework\Math\Random|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $mathRandomMock;
/**
* @var \Magento\Framework\View\Asset\MergeStrategy\Direct
*/
@@ -50,33 +56,47 @@ protected function setUp()
[DirectoryList::TMP, \Magento\Framework\Filesystem\DriverPool::FILE, $this->tmpDir],
]);
$this->resultAsset = $this->createMock(\Magento\Framework\View\Asset\File::class);
- $this->object = new Direct($filesystem, $this->cssUrlResolver);
+ $this->mathRandomMock = $this->getMockBuilder(\Magento\Framework\Math\Random::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->object = new Direct($filesystem, $this->cssUrlResolver, $this->mathRandomMock);
}
public function testMergeNoAssets()
{
+ $uniqId = '_b3bf82fa6e140594420fa90982a8e877';
$this->resultAsset->expects($this->once())->method('getPath')->will($this->returnValue('foo/result'));
$this->staticDir->expects($this->never())->method('writeFile');
- $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', '');
- $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir);
+ $this->mathRandomMock->expects($this->once())
+ ->method('getUniqueHash')
+ ->willReturn($uniqId);
+ $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result' . $uniqId, '');
+ $this->tmpDir->expects($this->once())->method('renameFile')
+ ->with('foo/result' . $uniqId, 'foo/result', $this->staticDir);
$this->object->merge([], $this->resultAsset);
}
public function testMergeGeneric()
{
+ $uniqId = '_be50ccf992fd81818c1a2645d1a29e92';
$this->resultAsset->expects($this->once())->method('getPath')->will($this->returnValue('foo/result'));
$assets = $this->prepareAssetsToMerge([' one', 'two']); // note leading space intentionally
$this->staticDir->expects($this->never())->method('writeFile');
- $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', 'onetwo');
- $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir);
+ $this->mathRandomMock->expects($this->once())
+ ->method('getUniqueHash')
+ ->willReturn($uniqId);
+ $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result' . $uniqId, 'onetwo');
+ $this->tmpDir->expects($this->once())->method('renameFile')
+ ->with('foo/result' . $uniqId, 'foo/result', $this->staticDir);
$this->object->merge($assets, $this->resultAsset);
}
public function testMergeCss()
{
+ $uniqId = '_f929c374767e00712449660ea673f2f5';
$this->resultAsset->expects($this->exactly(3))
->method('getPath')
- ->will($this->returnValue('foo/result'));
+ ->willReturn('foo/result');
$this->resultAsset->expects($this->any())->method('getContentType')->will($this->returnValue('css'));
$assets = $this->prepareAssetsToMerge(['one', 'two']);
$this->cssUrlResolver->expects($this->exactly(2))
@@ -85,10 +105,14 @@ public function testMergeCss()
$this->cssUrlResolver->expects($this->once())
->method('aggregateImportDirectives')
->with('12')
- ->will($this->returnValue('1020'));
+ ->willReturn('1020');
+ $this->mathRandomMock->expects($this->once())
+ ->method('getUniqueHash')
+ ->willReturn($uniqId);
$this->staticDir->expects($this->never())->method('writeFile');
- $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result', '1020');
- $this->tmpDir->expects($this->once())->method('renameFile')->with('foo/result', 'foo/result', $this->staticDir);
+ $this->tmpDir->expects($this->once())->method('writeFile')->with('foo/result' . $uniqId, '1020');
+ $this->tmpDir->expects($this->once())->method('renameFile')
+ ->with('foo/result' . $uniqId, 'foo/result', $this->staticDir);
$this->object->merge($assets, $this->resultAsset);
}
diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js
index 093ee707d3f98..b38e70d915c9d 100644
--- a/lib/web/fotorama/fotorama.js
+++ b/lib/web/fotorama/fotorama.js
@@ -1399,12 +1399,13 @@ fotoramaVersion = '4.6.4';
touchFLAG,
targetIsSelectFLAG,
targetIsLinkFlag,
+ isDisabledSwipe,
tolerance,
moved;
function onStart(e) {
$target = $(e.target);
- tail.checked = targetIsSelectFLAG = targetIsLinkFlag = moved = false;
+ tail.checked = targetIsSelectFLAG = targetIsLinkFlag = isDisabledSwipe = moved = false;
if (touchEnabledFLAG
|| tail.flow
@@ -1415,6 +1416,7 @@ fotoramaVersion = '4.6.4';
touchFLAG = e.type === 'touchstart';
targetIsLinkFlag = $target.is('a, a *', el);
+ isDisabledSwipe = $target.hasClass('disableSwipe');
controlTouch = tail.control;
tolerance = (tail.noMove || tail.noSwipe || controlTouch) ? 16 : !tail.snap ? 4 : 0;
@@ -1428,7 +1430,7 @@ fotoramaVersion = '4.6.4';
touchEnabledFLAG = tail.flow = true;
- if (!touchFLAG || tail.go) stopEvent(e);
+ if (!isDisabledSwipe && (!touchFLAG || tail.go)) stopEvent(e);
}
function onMove(e) {
@@ -1441,6 +1443,12 @@ fotoramaVersion = '4.6.4';
return;
}
+ isDisabledSwipe = $(e.target).hasClass('disableSwipe');
+
+ if (isDisabledSwipe) {
+ return;
+ }
+
extendEvent(e);
var xDiff = Math.abs(e._x - startEvent._x), // opt _x → _pageX
diff --git a/lib/web/fotorama/fotorama.min.js b/lib/web/fotorama/fotorama.min.js
deleted file mode 100644
index 0a0cbd9db7e74..0000000000000
--- a/lib/web/fotorama/fotorama.min.js
+++ /dev/null
@@ -1 +0,0 @@
-fotoramaVersion="4.6.4";(function(window,document,location,$,undefined){"use strict";var _fotoramaClass="fotorama",_fullscreenClass="fotorama__fullscreen",wrapClass=_fotoramaClass+"__wrap",wrapCss2Class=wrapClass+"--css2",wrapCss3Class=wrapClass+"--css3",wrapVideoClass=wrapClass+"--video",wrapFadeClass=wrapClass+"--fade",wrapSlideClass=wrapClass+"--slide",wrapNoControlsClass=wrapClass+"--no-controls",wrapNoShadowsClass=wrapClass+"--no-shadows",wrapPanYClass=wrapClass+"--pan-y",wrapRtlClass=wrapClass+"--rtl",wrapOnlyActiveClass=wrapClass+"--only-active",wrapNoCaptionsClass=wrapClass+"--no-captions",wrapToggleArrowsClass=wrapClass+"--toggle-arrows",stageClass=_fotoramaClass+"__stage",stageFrameClass=stageClass+"__frame",stageFrameVideoClass=stageFrameClass+"--video",stageShaftClass=stageClass+"__shaft",grabClass=_fotoramaClass+"__grab",pointerClass=_fotoramaClass+"__pointer",arrClass=_fotoramaClass+"__arr",arrDisabledClass=arrClass+"--disabled",arrPrevClass=arrClass+"--prev",arrNextClass=arrClass+"--next",navClass=_fotoramaClass+"__nav",navWrapClass=navClass+"-wrap",navShaftClass=navClass+"__shaft",navShaftVerticalClass=navWrapClass+"--vertical",navShaftListClass=navWrapClass+"--list",navShafthorizontalClass=navWrapClass+"--horizontal",navDotsClass=navClass+"--dots",navThumbsClass=navClass+"--thumbs",navFrameClass=navClass+"__frame",fadeClass=_fotoramaClass+"__fade",fadeFrontClass=fadeClass+"-front",fadeRearClass=fadeClass+"-rear",shadowClass=_fotoramaClass+"__shadow",shadowsClass=shadowClass+"s",shadowsLeftClass=shadowsClass+"--left",shadowsRightClass=shadowsClass+"--right",shadowsTopClass=shadowsClass+"--top",shadowsBottomClass=shadowsClass+"--bottom",activeClass=_fotoramaClass+"__active",selectClass=_fotoramaClass+"__select",hiddenClass=_fotoramaClass+"--hidden",fullscreenClass=_fotoramaClass+"--fullscreen",fullscreenIconClass=_fotoramaClass+"__fullscreen-icon",errorClass=_fotoramaClass+"__error",loadingClass=_fotoramaClass+"__loading",loadedClass=_fotoramaClass+"__loaded",loadedFullClass=loadedClass+"--full",loadedImgClass=loadedClass+"--img",grabbingClass=_fotoramaClass+"__grabbing",imgClass=_fotoramaClass+"__img",imgFullClass=imgClass+"--full",thumbClass=_fotoramaClass+"__thumb",thumbArrLeft=thumbClass+"__arr--left",thumbArrRight=thumbClass+"__arr--right",thumbBorderClass=thumbClass+"-border",htmlClass=_fotoramaClass+"__html",videoContainerClass=_fotoramaClass+"-video-container",videoClass=_fotoramaClass+"__video",videoPlayClass=videoClass+"-play",videoCloseClass=videoClass+"-close",horizontalImageClass=_fotoramaClass+"_horizontal_ratio",verticalImageClass=_fotoramaClass+"_vertical_ratio",fotoramaSpinnerClass=_fotoramaClass+"__spinner",spinnerShowClass=fotoramaSpinnerClass+"--show";var JQUERY_VERSION=$&&$.fn.jquery.split(".");if(!JQUERY_VERSION||JQUERY_VERSION[0]<1||JQUERY_VERSION[0]==1&&JQUERY_VERSION[1]<8){throw"Fotorama requires jQuery 1.8 or later and will not run without it."}var _={};var Modernizr=function(window,document,undefined){var version="2.8.3",Modernizr={},docElement=document.documentElement,mod="modernizr",modElem=document.createElement(mod),mStyle=modElem.style,inputElem,toString={}.toString,prefixes=" -webkit- -moz- -o- -ms- ".split(" "),omPrefixes="Webkit Moz O ms",cssomPrefixes=omPrefixes.split(" "),domPrefixes=omPrefixes.toLowerCase().split(" "),tests={},inputs={},attrs={},classes=[],slice=classes.slice,featureName,injectElementWithStyles=function(rule,callback,nodes,testnames){var style,ret,node,docOverflow,div=document.createElement("div"),body=document.body,fakeBody=body||document.createElement("body");if(parseInt(nodes,10)){while(nodes--){node=document.createElement("div");node.id=testnames?testnames[nodes]:mod+(nodes+1);div.appendChild(node)}}style=["",'"].join("");div.id=mod;(body?div:fakeBody).innerHTML+=style;fakeBody.appendChild(div);if(!body){fakeBody.style.background="";fakeBody.style.overflow="hidden";docOverflow=docElement.style.overflow;docElement.style.overflow="hidden";docElement.appendChild(fakeBody)}ret=callback(div,rule);if(!body){fakeBody.parentNode.removeChild(fakeBody);docElement.style.overflow=docOverflow}else{div.parentNode.removeChild(div)}return!!ret},_hasOwnProperty={}.hasOwnProperty,hasOwnProp;if(!is(_hasOwnProperty,"undefined")&&!is(_hasOwnProperty.call,"undefined")){hasOwnProp=function(object,property){return _hasOwnProperty.call(object,property)}}else{hasOwnProp=function(object,property){return property in object&&is(object.constructor.prototype[property],"undefined")}}if(!Function.prototype.bind){Function.prototype.bind=function bind(that){var target=this;if(typeof target!="function"){throw new TypeError}var args=slice.call(arguments,1),bound=function(){if(this instanceof bound){var F=function(){};F.prototype=target.prototype;var self=new F;var result=target.apply(self,args.concat(slice.call(arguments)));if(Object(result)===result){return result}return self}else{return target.apply(that,args.concat(slice.call(arguments)))}};return bound}}function setCss(str){mStyle.cssText=str}function setCssAll(str1,str2){return setCss(prefixes.join(str1+";")+(str2||""))}function is(obj,type){return typeof obj===type}function contains(str,substr){return!!~(""+str).indexOf(substr)}function testProps(props,prefixed){for(var i in props){var prop=props[i];if(!contains(prop,"-")&&mStyle[prop]!==undefined){return prefixed=="pfx"?prop:true}}return false}function testDOMProps(props,obj,elem){for(var i in props){var item=obj[props[i]];if(item!==undefined){if(elem===false)return props[i];if(is(item,"function")){return item.bind(elem||obj)}return item}}return false}function testPropsAll(prop,prefixed,elem){var ucProp=prop.charAt(0).toUpperCase()+prop.slice(1),props=(prop+" "+cssomPrefixes.join(ucProp+" ")+ucProp).split(" ");if(is(prefixed,"string")||is(prefixed,"undefined")){return testProps(props,prefixed)}else{props=(prop+" "+domPrefixes.join(ucProp+" ")+ucProp).split(" ");return testDOMProps(props,prefixed,elem)}}tests["touch"]=function(){var bool;if("ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch){bool=true}else{injectElementWithStyles(["@media (",prefixes.join("touch-enabled),("),mod,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(node){bool=node.offsetTop===9})}return bool};tests["csstransforms3d"]=function(){var ret=!!testPropsAll("perspective");if(ret&&"webkitPerspective"in docElement.style){injectElementWithStyles("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(node,rule){ret=node.offsetLeft===9&&node.offsetHeight===3})}return ret};tests["csstransitions"]=function(){return testPropsAll("transition")};for(var feature in tests){if(hasOwnProp(tests,feature)){featureName=feature.toLowerCase();Modernizr[featureName]=tests[feature]();classes.push((Modernizr[featureName]?"":"no-")+featureName)}}Modernizr.addTest=function(feature,test){if(typeof feature=="object"){for(var key in feature){if(hasOwnProp(feature,key)){Modernizr.addTest(key,feature[key])}}}else{feature=feature.toLowerCase();if(Modernizr[feature]!==undefined){return Modernizr}test=typeof test=="function"?test():test;if(typeof enableClasses!=="undefined"&&enableClasses){docElement.className+=" "+(test?"":"no-")+feature}Modernizr[feature]=test}return Modernizr};setCss("");modElem=inputElem=null;Modernizr._version=version;Modernizr._prefixes=prefixes;Modernizr._domPrefixes=domPrefixes;Modernizr._cssomPrefixes=cssomPrefixes;Modernizr.testProp=function(prop){return testProps([prop])};Modernizr.testAllProps=testPropsAll;Modernizr.testStyles=injectElementWithStyles;Modernizr.prefixed=function(prop,obj,elem){if(!obj){return testPropsAll(prop,"pfx")}else{return testPropsAll(prop,obj,elem)}};return Modernizr}(window,document);var fullScreenApi={ok:false,is:function(){return false},request:function(){},cancel:function(){},event:"",prefix:""},browserPrefixes="webkit moz o ms khtml".split(" ");if(typeof document.cancelFullScreen!="undefined"){fullScreenApi.ok=true}else{for(var i=0,il=browserPrefixes.length;i=max?"bottom":"top bottom":pos<=min?"left":pos>=max?"right":"left right"}function smartClick($el,fn,_options){_options=_options||{};$el.each(function(){var $this=$(this),thisData=$this.data(),startEvent;if(thisData.clickOn)return;thisData.clickOn=true;$.extend(touch($this,{onStart:function(e){startEvent=e;(_options.onStart||noop).call(this,e)},onMove:_options.onMove||noop,onTouchEnd:_options.onTouchEnd||noop,onEnd:function(result){if(result.moved)return;fn.call(this,startEvent)}}),{noMove:true})})}function div(classes,child){return''+(child||"")+"
"}function cls(className){return"."+className}function createVideoFrame(videoItem){var frame='';return frame}function shuffle(array){var l=array.length;while(l){var i=Math.floor(Math.random()*l--);var t=array[l];array[l]=array[i];array[i]=t}return array}function clone(array){return Object.prototype.toString.call(array)=="[object Array]"&&$.map(array,function(frame){return $.extend({},frame)})}function lockScroll($el,left,top){$el.scrollLeft(left||0).scrollTop(top||0)}function optionsToLowerCase(options){if(options){var opts={};$.each(options,function(key,value){opts[key.toLowerCase()]=value});return opts}}function getRatio(_ratio){if(!_ratio)return;var ratio=+_ratio;if(!isNaN(ratio)){return ratio}else{ratio=_ratio.split("/");return+ratio[0]/+ratio[1]||undefined}}function addEvent(el,e,fn,bool){if(!e)return;el.addEventListener?el.addEventListener(e,fn,!!bool):el.attachEvent("on"+e,fn)}function validateRestrictions(position,restriction){if(position>restriction.max){position=restriction.max}else{if(position=wrapSize-offsetNav){if(dir==="horizontal"){position=-$guessNavFrame.position().left}else{position=-$guessNavFrame.position().top}}else{if((size+opt.margin)*guessIndex<=Math.abs(offsetNav)){if(dir==="horizontal"){position=-$guessNavFrame.position().left+wrapSize-(size+opt.margin)}else{position=-$guessNavFrame.position().top+wrapSize-(size+opt.margin)}}else{position=offsetNav}}position=validateRestrictions(position,navShaftTouchTail);return position||0}function elIsDisabled(el){return!!el.getAttribute("disabled")}function disableAttr(FLAG,disable){if(disable){return{disabled:FLAG}}else{return{tabindex:FLAG*-1+"",disabled:FLAG}}}function addEnterUp(el,fn){addEvent(el,"keyup",function(e){elIsDisabled(el)||e.keyCode==13&&fn.call(el,e)})}function addFocus(el,fn){addEvent(el,"focus",el.onfocusin=function(e){fn.call(el,e)},true)}function stopEvent(e,stopPropagation){e.preventDefault?e.preventDefault():e.returnValue=false;stopPropagation&&e.stopPropagation&&e.stopPropagation()}function stubEvent($el,eventType){var isIOS=/ip(ad|hone|od)/i.test(window.navigator.userAgent);if(isIOS&&eventType==="touchend"){$el.on("touchend",function(e){$DOCUMENT.trigger("mouseup",e)})}$el.on(eventType,function(e){stopEvent(e,true);return false})}function getDirectionSign(forward){return forward?">":"<"}var UTIL=function(){function setRatioClass($el,wh,ht){var rateImg=wh/ht;if(rateImg<=1){$el.parent().removeClass(horizontalImageClass);$el.parent().addClass(verticalImageClass)}else{$el.parent().removeClass(verticalImageClass);$el.parent().addClass(horizontalImageClass)}}function setThumbAttr($frame,value,searchAttr){var attr=searchAttr;if(!$frame.attr(attr)&&$frame.attr(attr)!==undefined){$frame.attr(attr,value)}if($frame.find("["+attr+"]").length){$frame.find("["+attr+"]").each(function(){$(this).attr(attr,value)})}}function isExpectedCaption(frameItem,isExpected,undefined){var expected=false,frameExpected;frameItem.showCaption===undefined||frameItem.showCaption===true?frameExpected=true:frameExpected=false;if(!isExpected){return false}if(frameItem.caption&&frameExpected){expected=true}return expected}return{setRatio:setRatioClass,setThumbAttr:setThumbAttr,isExpectedCaption:isExpectedCaption}}(UTIL||{},jQuery);function slide($el,options){var elData=$el.data(),elPos=Math.round(options.pos),onEndFn=function(){if(elData&&elData.sliding){elData.sliding=false}(options.onEnd||noop)()};if(typeof options.overPos!=="undefined"&&options.overPos!==options.pos){elPos=options.overPos}var translate=$.extend(getTranslate(elPos,options.direction),options.width&&{width:options.width},options.height&&{height:options.height});if(elData&&elData.sliding){elData.sliding=true}if(CSS3){$el.css($.extend(getDuration(options.time),translate));if(options.time>10){afterTransition($el,"transform",onEndFn,options.time)}else{onEndFn()}}else{$el.stop().animate(translate,options.time,BEZIER,onEndFn)}}function fade($el1,$el2,$frames,options,fadeStack,chain){var chainedFLAG=typeof chain!=="undefined";if(!chainedFLAG){fadeStack.push(arguments);Array.prototype.push.call(arguments,fadeStack.length);if(fadeStack.length>1)return}$el1=$el1||$($el1);$el2=$el2||$($el2);var _$el1=$el1[0],_$el2=$el2[0],crossfadeFLAG=options.method==="crossfade",onEndFn=function(){if(!onEndFn.done){onEndFn.done=true;var args=(chainedFLAG||fadeStack.shift())&&fadeStack.shift();args&&fade.apply(this,args);(options.onEnd||noop)(!!args)}},time=options.time/(chain||1);$frames.removeClass(fadeRearClass+" "+fadeFrontClass);$el1.stop().addClass(fadeRearClass);$el2.stop().addClass(fadeFrontClass);crossfadeFLAG&&_$el2&&$el1.fadeTo(0,0);$el1.fadeTo(crossfadeFLAG?time:0,1,crossfadeFLAG&&onEndFn);$el2.fadeTo(time,0,onEndFn);_$el1&&crossfadeFLAG||_$el2||onEndFn()}var lastEvent,moveEventType,preventEvent,preventEventTimeout,dragDomEl;function extendEvent(e){var touch=(e.touches||[])[0]||e;e._x=touch.pageX||touch.originalEvent.pageX;e._y=touch.clientY||touch.originalEvent.clientY;e._now=$.now()}function touch($el,options){var el=$el[0],tail={},touchEnabledFLAG,startEvent,$target,controlTouch,touchFLAG,targetIsSelectFLAG,targetIsLinkFlag,tolerance,moved;function onStart(e){$target=$(e.target);tail.checked=targetIsSelectFLAG=targetIsLinkFlag=moved=false;if(touchEnabledFLAG||tail.flow||e.touches&&e.touches.length>1||e.which>1||lastEvent&&lastEvent.type!==e.type&&preventEvent||(targetIsSelectFLAG=options.select&&$target.is(options.select,el)))return targetIsSelectFLAG;touchFLAG=e.type==="touchstart";targetIsLinkFlag=$target.is("a, a *",el);controlTouch=tail.control;tolerance=tail.noMove||tail.noSwipe||controlTouch?16:!tail.snap?4:0;extendEvent(e);startEvent=lastEvent=e;moveEventType=e.type.replace(/down|start/,"move").replace(/Down/,"Move");(options.onStart||noop).call(el,e,{control:controlTouch,$target:$target});touchEnabledFLAG=tail.flow=true;if(!touchFLAG||tail.go)stopEvent(e)}function onMove(e){if(e.touches&&e.touches.length>1||MS_POINTER&&!e.isPrimary||moveEventType!==e.type||!touchEnabledFLAG){touchEnabledFLAG&&onEnd();(options.onTouchEnd||noop)();return}extendEvent(e);var xDiff=Math.abs(e._x-startEvent._x),yDiff=Math.abs(e._y-startEvent._y),xyDiff=xDiff-yDiff,xWin=(tail.go||tail.x||xyDiff>=0)&&!tail.noSwipe,yWin=xyDiff<0;if(touchFLAG&&!tail.checked){if(touchEnabledFLAG=xWin){stopEvent(e)}}else{stopEvent(e);if(movedEnough(xDiff,yDiff)){(options.onMove||noop).call(el,e,{touch:touchFLAG})}}if(!moved&&movedEnough(xDiff,yDiff)&&Math.sqrt(Math.pow(xDiff,2)+Math.pow(yDiff,2))>tolerance){moved=true}tail.checked=tail.checked||xWin||yWin}function movedEnough(xDiff,yDiff){return xDiff>yDiff&&xDiff>1.5}function onEnd(e){(options.onTouchEnd||noop)();var _touchEnabledFLAG=touchEnabledFLAG;tail.control=touchEnabledFLAG=false;if(_touchEnabledFLAG){tail.flow=false}if(!_touchEnabledFLAG||targetIsLinkFlag&&!tail.checked)return;e&&stopEvent(e);preventEvent=true;clearTimeout(preventEventTimeout);preventEventTimeout=setTimeout(function(){preventEvent=false},1e3);(options.onEnd||noop).call(el,{moved:moved,$target:$target,control:controlTouch,touch:touchFLAG,startEvent:startEvent,aborted:!e||e.type==="MSPointerCancel"})}function onOtherStart(){if(tail.flow)return;tail.flow=true}function onOtherEnd(){if(!tail.flow)return;tail.flow=false}if(MS_POINTER){addEvent(el,"MSPointerDown",onStart);addEvent(document,"MSPointerMove",onMove);addEvent(document,"MSPointerCancel",onEnd);addEvent(document,"MSPointerUp",onEnd)}else{addEvent(el,"touchstart",onStart);addEvent(el,"touchmove",onMove);addEvent(el,"touchend",onEnd);addEvent(document,"touchstart",onOtherStart);addEvent(document,"touchend",onOtherEnd);addEvent(document,"touchcancel",onOtherEnd);$WINDOW.on("scroll",onOtherEnd);$el.on("mousedown pointerdown",onStart);$DOCUMENT.on("mousemove pointermove",onMove).on("mouseup pointerup",onEnd)}if(Modernizr.touch){dragDomEl="a"}else{dragDomEl="div"}$el.on("click",dragDomEl,function(e){tail.checked&&stopEvent(e)});return tail}function moveOnTouch($el,options){var el=$el[0],elData=$el.data(),tail={},startCoo,coo,startElPos,moveElPos,edge,moveTrack,startTime,endTime,min,max,snap,dir,slowFLAG,controlFLAG,moved,tracked;function startTracking(e,noStop){tracked=true;startCoo=coo=dir==="vertical"?e._y:e._x;startTime=e._now;moveTrack=[[startTime,startCoo]];startElPos=moveElPos=tail.noMove||noStop?0:stop($el,(options.getPos||noop)());(options.onStart||noop).call(el,e)}function onStart(e,result){min=tail.min;max=tail.max;snap=tail.snap,dir=tail.direction||"horizontal",$el.navdir=dir;slowFLAG=e.altKey;tracked=moved=false;controlFLAG=result.control;if(!controlFLAG&&!elData.sliding){startTracking(e)}}function onMove(e,result){if(!tail.noSwipe){if(!tracked){startTracking(e)}coo=dir==="vertical"?e._y:e._x;moveTrack.push([e._now,coo]);moveElPos=startElPos-(startCoo-coo);edge=findShadowEdge(moveElPos,min,max,dir);if(moveElPos<=min){moveElPos=edgeResistance(moveElPos,min)}else if(moveElPos>=max){moveElPos=edgeResistance(moveElPos,max)}if(!tail.noMove){$el.css(getTranslate(moveElPos,dir));if(!moved){moved=true;result.touch||MS_POINTER||$el.addClass(grabbingClass)}(options.onMove||noop).call(el,e,{pos:moveElPos,edge:edge})}}}function onEnd(result){if(tail.noSwipe&&result.moved)return;if(!tracked){startTracking(result.startEvent,true)}result.touch||MS_POINTER||$el.removeClass(grabbingClass);endTime=$.now();var _backTimeIdeal=endTime-TOUCH_TIMEOUT,_backTime,_timeDiff,_timeDiffLast,backTime=null,backCoo,virtualPos,limitPos,newPos,overPos,time=TRANSITION_DURATION,speed,friction=options.friction;for(var _i=moveTrack.length-1;_i>=0;_i--){_backTime=moveTrack[_i][0];_timeDiff=Math.abs(_backTime-_backTimeIdeal);if(backTime===null||_timeDiff<_timeDiffLast){backTime=_backTime;backCoo=moveTrack[_i][1]}else if(backTime===_backTimeIdeal||_timeDiff>_timeDiffLast){break}_timeDiffLast=_timeDiff}newPos=minMaxLimit(moveElPos,min,max);var cooDiff=backCoo-coo,forwardFLAG=cooDiff>=0,timeDiff=endTime-backTime,longTouchFLAG=timeDiff>TOUCH_TIMEOUT,swipeFLAG=!longTouchFLAG&&moveElPos!==startElPos&&newPos===moveElPos;if(snap){newPos=minMaxLimit(Math[swipeFLAG?forwardFLAG?"floor":"ceil":"round"](moveElPos/snap)*snap,min,max);min=max=newPos}if(swipeFLAG&&(snap||newPos===moveElPos)){speed=-(cooDiff/timeDiff);time*=minMaxLimit(Math.abs(speed),options.timeLow,options.timeHigh);virtualPos=Math.round(moveElPos+speed*time/friction);if(!snap){newPos=virtualPos}if(!forwardFLAG&&virtualPos>max||forwardFLAG&&virtualPos"),$anchor=$(div(hiddenClass)),$wrap=$fotorama.find(cls(wrapClass)),$stage=$wrap.find(cls(stageClass)),stage=$stage[0],$stageShaft=$fotorama.find(cls(stageShaftClass)),$stageFrame=$(),$arrPrev=$fotorama.find(cls(arrPrevClass)),$arrNext=$fotorama.find(cls(arrNextClass)),$arrs=$fotorama.find(cls(arrClass)),$navWrap=$fotorama.find(cls(navWrapClass)),$nav=$navWrap.find(cls(navClass)),$navShaft=$nav.find(cls(navShaftClass)),$navFrame,$navDotFrame=$(),$navThumbFrame=$(),stageShaftData=$stageShaft.data(),navShaftData=$navShaft.data(),$thumbBorder=$fotorama.find(cls(thumbBorderClass)),$thumbArrLeft=$fotorama.find(cls(thumbArrLeft)),$thumbArrRight=$fotorama.find(cls(thumbArrRight)),$fullscreenIcon=$fotorama.find(cls(fullscreenIconClass)),fullscreenIcon=$fullscreenIcon[0],$videoPlay=$(div(videoPlayClass)),$videoClose=$fotorama.find(cls(videoCloseClass)),videoClose=$videoClose[0],$spinner=$fotorama.find(cls(fotoramaSpinnerClass)),$videoPlaying,activeIndex=false,activeFrame,activeIndexes,repositionIndex,dirtyIndex,lastActiveIndex,prevIndex,nextIndex,nextAutoplayIndex,startIndex,o_loop,o_nav,o_navThumbs,o_navTop,o_allowFullScreen,o_nativeFullScreen,o_fade,o_thumbSide,o_thumbSide2,o_transitionDuration,o_transition,o_shadows,o_rtl,o_keyboard,lastOptions={},measures={},measuresSetFLAG,stageShaftTouchTail={},stageWheelTail={},navShaftTouchTail={},navWheelTail={},scrollTop,scrollLeft,showedFLAG,pausedAutoplayFLAG,stoppedAutoplayFLAG,toDeactivate={},toDetach={},measuresStash,touchedFLAG,hoverFLAG,navFrameKey,stageLeft=0,fadeStack=[];$wrap[STAGE_FRAME_KEY]=$('');$wrap[NAV_THUMB_FRAME_KEY]=$($.Fotorama.jst.thumb());$wrap[NAV_DOT_FRAME_KEY]=$($.Fotorama.jst.dots());toDeactivate[STAGE_FRAME_KEY]=[];toDeactivate[NAV_THUMB_FRAME_KEY]=[];toDeactivate[NAV_DOT_FRAME_KEY]=[];toDetach[STAGE_FRAME_KEY]={};$wrap.addClass(CSS3?wrapCss3Class:wrapCss2Class);fotoramaData.fotorama=this;function checkForVideo(){$.each(data,function(i,dataFrame){if(!dataFrame.i){dataFrame.i=dataFrameCount++;var video=findVideoId(dataFrame.video,true);if(video){var thumbs={};dataFrame.video=video;if(!dataFrame.img&&!dataFrame.thumb){thumbs=getVideoThumbs(dataFrame,data,that)}else{dataFrame.thumbsReady=true}updateData(data,{img:thumbs.img,thumb:thumbs.thumb},dataFrame.i,that)}}})}function allowKey(key){return o_keyboard[key]}function setStagePosition(){if($stage!==undefined){if(opts.navdir=="vertical"){var padding=opts.thumbwidth+opts.thumbmargin;$stage.css("left",padding);$arrNext.css("right",padding);$fullscreenIcon.css("right",padding);$wrap.css("width",$wrap.css("width")+padding);$stageShaft.css("max-width",$wrap.width()-padding)}else{$stage.css("left","");$arrNext.css("right","");$fullscreenIcon.css("right","");$wrap.css("width",$wrap.css("width")+padding);$stageShaft.css("max-width","")}}}function bindGlobalEvents(FLAG){var keydownCommon="keydown."+_fotoramaClass,localStamp=_fotoramaClass+stamp,keydownLocal="keydown."+localStamp,keyupLocal="keyup."+localStamp,resizeLocal="resize."+localStamp+" "+"orientationchange."+localStamp,showParams;if(FLAG){$DOCUMENT.on(keydownLocal,function(e){var catched,index;if($videoPlaying&&e.keyCode===27){catched=true;unloadVideo($videoPlaying,true,true)}else if(that.fullScreen||opts.keyboard&&!that.index){if(e.keyCode===27){catched=true;that.cancelFullScreen()}else if(e.shiftKey&&e.keyCode===32&&allowKey("space")||!e.altKey&&!e.metaKey&&e.keyCode===37&&allowKey("left")||e.keyCode===38&&allowKey("up")&&$(":focus").attr("data-gallery-role")){that.longPress.progress();index="<"}else if(e.keyCode===32&&allowKey("space")||!e.altKey&&!e.metaKey&&e.keyCode===39&&allowKey("right")||e.keyCode===40&&allowKey("down")&&$(":focus").attr("data-gallery-role")){that.longPress.progress();index=">"}else if(e.keyCode===36&&allowKey("home")){that.longPress.progress();index="<<"}else if(e.keyCode===35&&allowKey("end")){that.longPress.progress();index=">>"}}(catched||index)&&stopEvent(e);showParams={index:index,slow:e.altKey,user:true};index&&(that.longPress.inProgress?that.showWhileLongPress(showParams):that.show(showParams))});if(FLAG){$DOCUMENT.on(keyupLocal,function(e){if(that.longPress.inProgress){that.showEndLongPress({user:true})}that.longPress.reset()})}if(!that.index){$DOCUMENT.off(keydownCommon).on(keydownCommon,"textarea, input, select",function(e){!$BODY.hasClass(_fullscreenClass)&&e.stopPropagation()})}$WINDOW.on(resizeLocal,that.resize)}else{$DOCUMENT.off(keydownLocal);$WINDOW.off(resizeLocal)}}function appendElements(FLAG){if(FLAG===appendElements.f)return;if(FLAG){$fotorama.addClass(_fotoramaClass+" "+stampClass).before($anchor).before($style);addInstance(that)}else{$anchor.detach();$style.detach();$fotorama.html(fotoramaData.urtext).removeClass(stampClass);hideInstance(that)}bindGlobalEvents(FLAG);appendElements.f=FLAG}function setData(){data=that.data=data||clone(opts.data)||getDataFromHtml($fotorama);size=that.size=data.length;ready.ok&&opts.shuffle&&shuffle(data);checkForVideo();activeIndex=limitIndex(activeIndex);size&&appendElements(true)}function stageNoMove(){var _noMove=size<2||$videoPlaying;stageShaftTouchTail.noMove=_noMove||o_fade;stageShaftTouchTail.noSwipe=_noMove||!opts.swipe;!o_transition&&$stageShaft.toggleClass(grabClass,!opts.click&&!stageShaftTouchTail.noMove&&!stageShaftTouchTail.noSwipe);MS_POINTER&&$wrap.toggleClass(wrapPanYClass,!stageShaftTouchTail.noSwipe)}function setAutoplayInterval(interval){if(interval===true)interval="";opts.autoplay=Math.max(+interval||AUTOPLAY_INTERVAL,o_transitionDuration*1.5)}function updateThumbArrow(opt){if(opt.navarrows&&opt.nav==="thumbs"){$thumbArrLeft.show();$thumbArrRight.show()}else{$thumbArrLeft.hide();$thumbArrRight.hide()}}function getThumbsInSlide($el,opts){return Math.floor($wrap.width()/(opts.thumbwidth+opts.thumbmargin))}function setOptions(){if(!opts.nav||opts.nav==="dots"){opts.navdir="horizontal"}that.options=opts=optionsToLowerCase(opts);thumbsPerSlide=getThumbsInSlide($wrap,opts);o_fade=opts.transition==="crossfade"||opts.transition==="dissolve";o_loop=opts.loop&&(size>2||o_fade&&(!o_transition||o_transition!=="slide"));o_transitionDuration=+opts.transitionduration||TRANSITION_DURATION;o_rtl=opts.direction==="rtl";o_keyboard=$.extend({},opts.keyboard&&KEYBOARD_OPTIONS,opts.keyboard);updateThumbArrow(opts);var classes={add:[],remove:[]};function addOrRemoveClass(FLAG,value){classes[FLAG?"add":"remove"].push(value)}if(size>1){o_nav=opts.nav;o_navTop=opts.navposition==="top";classes.remove.push(selectClass);$arrs.toggle(!!opts.arrows)}else{o_nav=false;$arrs.hide()}arrsUpdate();stageWheelUpdate();thumbArrUpdate();if(opts.autoplay)setAutoplayInterval(opts.autoplay);o_thumbSide=numberFromMeasure(opts.thumbwidth)||THUMB_SIZE;o_thumbSide2=numberFromMeasure(opts.thumbheight)||THUMB_SIZE;stageWheelTail.ok=navWheelTail.ok=opts.trackpad&&!SLOW;stageNoMove();extendMeasures(opts,[measures]);o_navThumbs=o_nav==="thumbs";if($navWrap.filter(":hidden")&&!!o_nav){$navWrap.show()}if(o_navThumbs){frameDraw(size,"navThumb");$navFrame=$navThumbFrame;navFrameKey=NAV_THUMB_FRAME_KEY;setStyle($style,$.Fotorama.jst.style({w:o_thumbSide,h:o_thumbSide2,b:opts.thumbborderwidth,m:opts.thumbmargin,s:stamp,q:!COMPAT}));$nav.addClass(navThumbsClass).removeClass(navDotsClass)}else if(o_nav==="dots"){frameDraw(size,"navDot");$navFrame=$navDotFrame;navFrameKey=NAV_DOT_FRAME_KEY;$nav.addClass(navDotsClass).removeClass(navThumbsClass)}else{$navWrap.hide();o_nav=false;$nav.removeClass(navThumbsClass+" "+navDotsClass)}if(o_nav){if(o_navTop){$navWrap.insertBefore($stage)}else{$navWrap.insertAfter($stage)}frameAppend.nav=false;frameAppend($navFrame,$navShaft,"nav")}o_allowFullScreen=opts.allowfullscreen;if(o_allowFullScreen){$fullscreenIcon.prependTo($stage);o_nativeFullScreen=FULLSCREEN&&o_allowFullScreen==="native";stubEvent($fullscreenIcon,"touchend")}else{$fullscreenIcon.detach();o_nativeFullScreen=false}addOrRemoveClass(o_fade,wrapFadeClass);addOrRemoveClass(!o_fade,wrapSlideClass);addOrRemoveClass(!opts.captions,wrapNoCaptionsClass);addOrRemoveClass(o_rtl,wrapRtlClass);addOrRemoveClass(opts.arrows,wrapToggleArrowsClass);o_shadows=opts.shadows&&!SLOW;addOrRemoveClass(!o_shadows,wrapNoShadowsClass);$wrap.addClass(classes.add.join(" ")).removeClass(classes.remove.join(" "));lastOptions=$.extend({},opts);setStagePosition()}function normalizeIndex(index){return index<0?(size+index%size)%size:index>=size?index%size:index}function limitIndex(index){return minMaxLimit(index,0,size-1)}function edgeIndex(index){return o_loop?normalizeIndex(index):limitIndex(index)}function getPrevIndex(index){return index>0||o_loop?index-1:false}function getNextIndex(index){return index1&&data[index]===dataFrame&&!dataFrame.html&&!dataFrame.deleted&&!dataFrame.video&&!fullFLAG){dataFrame.deleted=true;that.splice(index,1)}}}function loaded(){$.Fotorama.measures[src]=imgData.measures=$.Fotorama.measures[src]||{width:img.width,height:img.height,ratio:img.width/img.height};setMeasures(imgData.measures.width,imgData.measures.height,imgData.measures.ratio,index);$img.off("load error").addClass(""+(fullFLAG?imgFullClass:imgClass)).attr("aria-hidden","false").prependTo($frame);if($frame.hasClass(stageFrameClass)&&!$frame.hasClass(videoContainerClass)){$frame.attr("href",$img.attr("src"))}fit($img,($.isFunction(specialMeasures)?specialMeasures():specialMeasures)||measures);$.Fotorama.cache[src]=frameData.state="loaded";setTimeout(function(){$frame.trigger("f:load").removeClass(loadingClass+" "+errorClass).addClass(loadedClass+" "+(fullFLAG?loadedFullClass:loadedImgClass));if(type==="stage"){triggerTriggerEvent("load")}else if(dataFrame.thumbratio===AUTO||!dataFrame.thumbratio&&opts.thumbratio===AUTO){dataFrame.thumbratio=imgData.measures.ratio;reset()}},0)}if(!src){error();return}function waitAndLoad(){var _i=10;waitFor(function(){return!touchedFLAG||!_i--&&!SLOW},function(){loaded()})}if(!$.Fotorama.cache[src]){$.Fotorama.cache[src]="*";$img.on("load",waitAndLoad).on("error",error)}else{(function justWait(){if($.Fotorama.cache[src]==="error"){error()}else if($.Fotorama.cache[src]==="loaded"){setTimeout(waitAndLoad,0)}else{setTimeout(justWait,100)}})()}frameData.state="";img.src=src;if(frameData.data.caption){img.alt=frameData.data.caption||""}if(frameData.data.full){$(img).data("original",frameData.data.full)}if(UTIL.isExpectedCaption(dataFrame,opts.showcaption)){$(img).attr("aria-labelledby",dataFrame.labelledby)}})}function updateFotoramaState(){var $frame=activeFrame[STAGE_FRAME_KEY];if($frame&&!$frame.data().state){$spinner.addClass(spinnerShowClass);$frame.on("f:load f:error",function(){$frame.off("f:load f:error");$spinner.removeClass(spinnerShowClass)})}}function addNavFrameEvents(frame){addEnterUp(frame,onNavFrameClick);addFocus(frame,function(){setTimeout(function(){lockScroll($nav)},0);slideNavShaft({time:o_transitionDuration,guessIndex:$(this).data().eq,minMax:navShaftTouchTail})})}function frameDraw(indexes,type){eachIndex(indexes,type,function(i,index,dataFrame,$frame,key,frameData){if($frame)return;$frame=dataFrame[key]=$wrap[key].clone();frameData=$frame.data();frameData.data=dataFrame;var frame=$frame[0],labelledbyValue="labelledby"+$.now();if(type==="stage"){if(dataFrame.html){$('').append(dataFrame._html?$(dataFrame.html).removeAttr("id").html(dataFrame._html):dataFrame.html).appendTo($frame)}if(dataFrame.id){labelledbyValue=dataFrame.id||labelledbyValue}dataFrame.labelledby=labelledbyValue;if(UTIL.isExpectedCaption(dataFrame,opts.showcaption)){$($.Fotorama.jst.frameCaption({caption:dataFrame.caption,labelledby:labelledbyValue})).appendTo($frame)}dataFrame.video&&$frame.addClass(stageFrameVideoClass).append($videoPlay.clone());addFocus(frame,function(){setTimeout(function(){lockScroll($stage)},0);clickToShow({index:frameData.eq,user:true})});$stageFrame=$stageFrame.add($frame)}else if(type==="navDot"){addNavFrameEvents(frame);$navDotFrame=$navDotFrame.add($frame)}else if(type==="navThumb"){addNavFrameEvents(frame);frameData.$wrap=$frame.children(":first");$navThumbFrame=$navThumbFrame.add($frame);if(dataFrame.video){frameData.$wrap.append($videoPlay.clone())}}})}function callFit($img,measuresToFit){return $img&&$img.length&&fit($img,measuresToFit)}function stageFramePosition(indexes){eachIndex(indexes,"stage",function(i,index,dataFrame,$frame,key,frameData){if(!$frame)return;var normalizedIndex=normalizeIndex(index);frameData.eq=normalizedIndex;toDetach[STAGE_FRAME_KEY][normalizedIndex]=$frame.css($.extend({left:o_fade?0:getPosByIndex(index,measures.w,opts.margin,repositionIndex)},o_fade&&getDuration(0)));if(isDetached($frame[0])){$frame.appendTo($stageShaft);unloadVideo(dataFrame.$video)}callFit(frameData.$img,measures);callFit(frameData.$full,measures);if($frame.hasClass(stageFrameClass)&&!($frame.attr("aria-hidden")==="false"&&$frame.hasClass(activeClass))){$frame.attr("aria-hidden","true")}})}function thumbsDraw(pos,loadFLAG){var leftLimit,rightLimit,exceedLimit;if(o_nav!=="thumbs"||isNaN(pos))return;leftLimit=-pos;rightLimit=-pos+measures.nw;if(opts.navdir==="vertical"){pos=pos-opts.thumbheight;rightLimit=-pos+measures.h}$navThumbFrame.each(function(){var $this=$(this),thisData=$this.data(),eq=thisData.eq,getSpecialMeasures=function(){return{h:o_thumbSide2,w:thisData.w}},specialMeasures=getSpecialMeasures(),exceedLimit=opts.navdir==="vertical"?thisData.t>rightLimit:thisData.l>rightLimit;specialMeasures.w=thisData.w;if(thisData.l+thisData.wmeasures.w/3}function disableDirrection(i){return!o_loop&&(!(activeIndex+i)||!(activeIndex-size+i))&&!$videoPlaying}function arrsUpdate(){var disablePrev=disableDirrection(0),disableNext=disableDirrection(1);$arrPrev.toggleClass(arrDisabledClass,disablePrev).attr(disableAttr(disablePrev,false));$arrNext.toggleClass(arrDisabledClass,disableNext).attr(disableAttr(disableNext,false))}function thumbArrUpdate(){var isLeftDisable=false,isRightDisable=false;if(opts.navtype==="thumbs"&&!opts.loop){activeIndex==0?isLeftDisable=true:isLeftDisable=false;activeIndex==opts.data.length-1?isRightDisable=true:isRightDisable=false}if(opts.navtype==="slides"){var pos=readPosition($navShaft,opts.navdir);pos>=navShaftTouchTail.max?isLeftDisable=true:isLeftDisable=false;pos<=navShaftTouchTail.min?isRightDisable=true:isRightDisable=false}$thumbArrLeft.toggleClass(arrDisabledClass,isLeftDisable).attr(disableAttr(isLeftDisable,true));$thumbArrRight.toggleClass(arrDisabledClass,isRightDisable).attr(disableAttr(isRightDisable,true))}function stageWheelUpdate(){if(stageWheelTail.ok){stageWheelTail.prevent={"<":disableDirrection(0),">":disableDirrection(1)}}}function getNavFrameBounds($navFrame){var navFrameData=$navFrame.data(),left,top,width,height;if(o_navThumbs){left=navFrameData.l;top=navFrameData.t;width=navFrameData.w;height=navFrameData.h}else{left=$navFrame.position().left;width=$navFrame.width()}var horizontalBounds={c:left+width/2,min:-left+opts.thumbmargin*10,max:-left+measures.w-width-opts.thumbmargin*10};var verticalBounds={c:top+height/2,min:-top+opts.thumbmargin*10,max:-top+measures.h-height-opts.thumbmargin*10};return opts.navdir==="vertical"?verticalBounds:horizontalBounds}function slideThumbBorder(time){var navFrameData=activeFrame[navFrameKey].data();slide($thumbBorder,{time:time*1.2,pos:opts.navdir==="vertical"?navFrameData.t:navFrameData.l,width:navFrameData.w,height:navFrameData.h,direction:opts.navdir})}function slideNavShaft(options){var $guessNavFrame=data[options.guessIndex][navFrameKey],typeOfAnimation=opts.navtype;var overflowFLAG,time,minMax,boundTop,boundLeft,l,pos,x;if($guessNavFrame){if(typeOfAnimation==="thumbs"){overflowFLAG=navShaftTouchTail.min!==navShaftTouchTail.max;minMax=options.minMax||overflowFLAG&&getNavFrameBounds(activeFrame[navFrameKey]);boundTop=overflowFLAG&&(options.keep&&slideNavShaft.t?slideNavShaft.l:minMaxLimit((options.coo||measures.nw/2)-getNavFrameBounds($guessNavFrame).c,minMax.min,minMax.max));boundLeft=overflowFLAG&&(options.keep&&slideNavShaft.l?slideNavShaft.l:minMaxLimit((options.coo||measures.nw/2)-getNavFrameBounds($guessNavFrame).c,minMax.min,minMax.max));l=opts.navdir==="vertical"?boundTop:boundLeft;pos=overflowFLAG&&minMaxLimit(l,navShaftTouchTail.min,navShaftTouchTail.max)||0;time=options.time*1.1;slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:function(){thumbsDraw(pos,true);thumbArrUpdate()}});setShadow($nav,findShadowEdge(pos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir));slideNavShaft.l=l}else{x=readPosition($navShaft,opts.navdir);time=options.time*1.11;pos=validateSlidePos(opts,navShaftTouchTail,options.guessIndex,x,$guessNavFrame,$navWrap,opts.navdir);slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:function(){thumbsDraw(pos,true);thumbArrUpdate()}});setShadow($nav,findShadowEdge(pos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir))}}}function navUpdate(){deactivateFrames(navFrameKey);toDeactivate[navFrameKey].push(activeFrame[navFrameKey].addClass(activeClass).attr("data-active",true))}function deactivateFrames(key){var _toDeactivate=toDeactivate[key];while(_toDeactivate.length){_toDeactivate.shift().removeClass(activeClass).attr("data-active",false)}}function detachFrames(key){var _toDetach=toDetach[key];$.each(activeIndexes,function(i,index){delete _toDetach[normalizeIndex(index)]});$.each(_toDetach,function(index,$frame){delete _toDetach[index];$frame.detach()})}function stageShaftReposition(skipOnEnd){repositionIndex=dirtyIndex=activeIndex;var $frame=activeFrame[STAGE_FRAME_KEY];if($frame){deactivateFrames(STAGE_FRAME_KEY);toDeactivate[STAGE_FRAME_KEY].push($frame.addClass(activeClass).attr("data-active",true));if($frame.hasClass(stageFrameClass)){$frame.attr("aria-hidden","false")}skipOnEnd||that.showStage.onEnd(true);stop($stageShaft,0,true);detachFrames(STAGE_FRAME_KEY);stageFramePosition(activeIndexes);setStageShaftMinmaxAndSnap();setNavShaftMinMax();addEnterUp($stageShaft[0],function(){if(!$fotorama.hasClass(fullscreenClass)){that.requestFullScreen();$fullscreenIcon.focus()}})}}function extendMeasures(options,measuresArray){if(!options)return;$.each(measuresArray,function(i,measures){if(!measures)return;$.extend(measures,{width:options.width||measures.width,height:options.height,minwidth:options.minwidth,maxwidth:options.maxwidth,minheight:options.minheight,maxheight:options.maxheight,ratio:getRatio(options.ratio)})})}function triggerEvent(event,extra){$fotorama.trigger(_fotoramaClass+":"+event,[that,extra])}function onTouchStart(){clearTimeout(onTouchEnd.t);touchedFLAG=1;if(opts.stopautoplayontouch){that.stopAutoplay()}else{pausedAutoplayFLAG=true}}function onTouchEnd(){if(!touchedFLAG)return;if(!opts.stopautoplayontouch){releaseAutoplay();changeAutoplay()}onTouchEnd.t=setTimeout(function(){touchedFLAG=0},TRANSITION_DURATION+TOUCH_TIMEOUT)}function releaseAutoplay(){pausedAutoplayFLAG=!!($videoPlaying||stoppedAutoplayFLAG)}function changeAutoplay(){clearTimeout(changeAutoplay.t);waitFor.stop(changeAutoplay.w);if(!opts.autoplay||pausedAutoplayFLAG){if(that.autoplay){that.autoplay=false;triggerEvent("stopautoplay")}return}if(!that.autoplay){that.autoplay=true;triggerEvent("startautoplay")}var _activeIndex=activeIndex;var frameData=activeFrame[STAGE_FRAME_KEY].data();changeAutoplay.w=waitFor(function(){return frameData.state||_activeIndex!==activeIndex},function(){changeAutoplay.t=setTimeout(function(){if(pausedAutoplayFLAG||_activeIndex!==activeIndex)return;var _nextAutoplayIndex=nextAutoplayIndex,nextFrameData=data[_nextAutoplayIndex][STAGE_FRAME_KEY].data();changeAutoplay.w=waitFor(function(){return nextFrameData.state||_nextAutoplayIndex!==nextAutoplayIndex},function(){if(pausedAutoplayFLAG||_nextAutoplayIndex!==nextAutoplayIndex)return;that.show(o_loop?getDirectionSign(!o_rtl):nextAutoplayIndex)})},opts.autoplay)})}that.startAutoplay=function(interval){if(that.autoplay)return this;pausedAutoplayFLAG=stoppedAutoplayFLAG=false;setAutoplayInterval(interval||opts.autoplay);changeAutoplay();return this};that.stopAutoplay=function(){if(that.autoplay){pausedAutoplayFLAG=stoppedAutoplayFLAG=true;changeAutoplay()}return this};that.showSlide=function(slideDir){var currentPosition=readPosition($navShaft,opts.navdir),pos,time=500*1.1,size=opts.navdir==="horizontal"?opts.thumbwidth:opts.thumbheight,onEnd=function(){thumbArrUpdate()};if(slideDir==="next"){pos=currentPosition-(size+opts.margin)*thumbsPerSlide}if(slideDir==="prev"){pos=currentPosition+(size+opts.margin)*thumbsPerSlide}pos=validateRestrictions(pos,navShaftTouchTail);thumbsDraw(pos,true);slide($navShaft,{time:time,pos:pos,direction:opts.navdir,onEnd:onEnd})};that.showWhileLongPress=function(options){if(that.longPress.singlePressInProgress){return}var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options)/50;var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showNav(silent,options,time);return this};that.showEndLongPress=function(options){if(that.longPress.singlePressInProgress){return}var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options)/50;var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showStage(silent,options,time);showedFLAG=typeof lastActiveIndex!=="undefined"&&lastActiveIndex!==activeIndex;lastActiveIndex=activeIndex;return this};function calcActiveIndex(options){var index;if(typeof options!=="object"){index=options;options={}}else{index=options.index}index=index===">"?dirtyIndex+1:index==="<"?dirtyIndex-1:index==="<<"?0:index===">>"?size-1:index;index=isNaN(index)?undefined:index;index=typeof index==="undefined"?activeIndex||0:index;return index}function calcGlobalIndexes(index){that.activeIndex=activeIndex=edgeIndex(index);prevIndex=getPrevIndex(activeIndex);nextIndex=getNextIndex(activeIndex);nextAutoplayIndex=normalizeIndex(activeIndex+(o_rtl?-1:1));activeIndexes=[activeIndex,prevIndex,nextIndex];dirtyIndex=o_loop?index:activeIndex}function calcTime(options){var diffIndex=Math.abs(lastActiveIndex-dirtyIndex),time=getNumber(options.time,function(){return Math.min(o_transitionDuration*(1+(diffIndex-1)/12),o_transitionDuration*2)});if(options.slow){time*=10}return time}that.showStage=function(silent,options,time){unloadVideo($videoPlaying,activeFrame.i!==data[normalizeIndex(repositionIndex)].i);frameDraw(activeIndexes,"stage");stageFramePosition(SLOW?[dirtyIndex]:[dirtyIndex,getPrevIndex(dirtyIndex),getNextIndex(dirtyIndex)]);updateTouchTails("go",true);silent||triggerEvent("show",{user:options.user,time:time});pausedAutoplayFLAG=true;var overPos=options.overPos;var onEnd=that.showStage.onEnd=function(skipReposition){if(onEnd.ok)return;onEnd.ok=true;skipReposition||stageShaftReposition(true);if(!silent){triggerEvent("showend",{user:options.user})}if(!skipReposition&&o_transition&&o_transition!==opts.transition){that.setOptions({transition:o_transition});o_transition=false;return}updateFotoramaState();loadImg(activeIndexes,"stage");updateTouchTails("go",false);stageWheelUpdate();stageCursor();releaseAutoplay();changeAutoplay();if(that.fullScreen){activeFrame[STAGE_FRAME_KEY].find("."+imgFullClass).attr("aria-hidden",false);activeFrame[STAGE_FRAME_KEY].find("."+imgClass).attr("aria-hidden",true)}else{activeFrame[STAGE_FRAME_KEY].find("."+imgFullClass).attr("aria-hidden",true);activeFrame[STAGE_FRAME_KEY].find("."+imgClass).attr("aria-hidden",false)}};if(!o_fade){slide($stageShaft,{pos:-getPosByIndex(dirtyIndex,measures.w,opts.margin,repositionIndex),overPos:overPos,time:time,onEnd:onEnd})}else{var $activeFrame=activeFrame[STAGE_FRAME_KEY],$prevActiveFrame=data[lastActiveIndex]&&activeIndex!==lastActiveIndex?data[lastActiveIndex][STAGE_FRAME_KEY]:null;fade($activeFrame,$prevActiveFrame,$stageFrame,{time:time,method:opts.transition,onEnd:onEnd},fadeStack)}arrsUpdate()};that.showNav=function(silent,options,time){thumbArrUpdate();if(o_nav){navUpdate();var guessIndex=limitIndex(activeIndex+minMaxLimit(dirtyIndex-lastActiveIndex,-1,1));slideNavShaft({time:time,coo:guessIndex!==activeIndex&&options.coo,guessIndex:typeof options.coo!=="undefined"?guessIndex:activeIndex,keep:silent});if(o_navThumbs)slideThumbBorder(time)}};that.show=function(options){that.longPress.singlePressInProgress=true;var index=calcActiveIndex(options);calcGlobalIndexes(index);var time=calcTime(options);var _activeFrame=activeFrame;that.activeFrame=activeFrame=data[activeIndex];var silent=_activeFrame===activeFrame&&!options.user;that.showStage(silent,options,time);that.showNav(silent,options,time);showedFLAG=typeof lastActiveIndex!=="undefined"&&lastActiveIndex!==activeIndex;lastActiveIndex=activeIndex;that.longPress.singlePressInProgress=false;return this};that.requestFullScreen=function(){if(o_allowFullScreen&&!that.fullScreen){var isVideo=$((that.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container");if(isVideo){return}scrollTop=$WINDOW.scrollTop();scrollLeft=$WINDOW.scrollLeft();lockScroll($WINDOW);updateTouchTails("x",true);measuresStash=$.extend({},measures);$fotorama.addClass(fullscreenClass).appendTo($BODY.addClass(_fullscreenClass));$HTML.addClass(_fullscreenClass);unloadVideo($videoPlaying,true,true);that.fullScreen=true;if(o_nativeFullScreen){fullScreenApi.request(fotorama)}that.resize();loadImg(activeIndexes,"stage");updateFotoramaState();triggerEvent("fullscreenenter");if(!("ontouchstart"in window)){$fullscreenIcon.focus()}}return this};function cancelFullScreen(){if(that.fullScreen){that.fullScreen=false;if(FULLSCREEN){fullScreenApi.cancel(fotorama)}$BODY.removeClass(_fullscreenClass);$HTML.removeClass(_fullscreenClass);$fotorama.removeClass(fullscreenClass).insertAfter($anchor);measures=$.extend({},measuresStash);unloadVideo($videoPlaying,true,true);updateTouchTails("x",false);that.resize();loadImg(activeIndexes,"stage");lockScroll($WINDOW,scrollLeft,scrollTop);triggerEvent("fullscreenexit")}}that.cancelFullScreen=function(){if(o_nativeFullScreen&&fullScreenApi.is()){fullScreenApi.cancel(document)}else{cancelFullScreen()}return this};that.toggleFullScreen=function(){return that[(that.fullScreen?"cancel":"request")+"FullScreen"]()};that.resize=function(options){if(!data)return this;var time=arguments[1]||0,setFLAG=arguments[2];thumbsPerSlide=getThumbsInSlide($wrap,opts);extendMeasures(!that.fullScreen?optionsToLowerCase(options):{width:$(window).width(),maxwidth:null,minwidth:null,height:$(window).height(),maxheight:null,minheight:null},[measures,setFLAG||that.fullScreen||opts]);var width=measures.width,height=measures.height,ratio=measures.ratio,windowHeight=$WINDOW.height()-(o_nav?$nav.height():0);if(measureIsValid(width)){$wrap.css({width:""});$wrap.css({height:""});$stage.css({width:""});$stage.css({height:""});$stageShaft.css({width:""});$stageShaft.css({height:""});$nav.css({width:""});$nav.css({height:""});$wrap.css({minWidth:measures.minwidth||0,maxWidth:measures.maxwidth||MAX_WIDTH});if(o_nav==="dots"){$navWrap.hide()}width=measures.W=measures.w=$wrap.width();measures.nw=o_nav&&numberFromWhatever(opts.navwidth,width)||width;$stageShaft.css({width:measures.w,marginLeft:(measures.W-measures.w)/2});height=numberFromWhatever(height,windowHeight);height=height||ratio&&width/ratio;if(height){width=Math.round(width);height=measures.h=Math.round(minMaxLimit(height,numberFromWhatever(measures.minheight,windowHeight),numberFromWhatever(measures.maxheight,windowHeight)));$stage.css({width:width,height:height});if(opts.navdir==="vertical"&&!that.fullscreen){$nav.width(opts.thumbwidth+opts.thumbmargin*2)}if(opts.navdir==="horizontal"&&!that.fullscreen){$nav.height(opts.thumbheight+opts.thumbmargin*2)}if(o_nav==="dots"){$nav.width(width).height("auto");$navWrap.show()}if(opts.navdir==="vertical"&&that.fullScreen){$stage.css("height",$WINDOW.height())}if(opts.navdir==="horizontal"&&that.fullScreen){$stage.css("height",$WINDOW.height()-$nav.height())}if(o_nav){switch(opts.navdir){case"vertical":$navWrap.removeClass(navShafthorizontalClass);$navWrap.removeClass(navShaftListClass);$navWrap.addClass(navShaftVerticalClass);$nav.stop().animate({height:measures.h,width:opts.thumbwidth},time);break;case"list":$navWrap.removeClass(navShaftVerticalClass);$navWrap.removeClass(navShafthorizontalClass);$navWrap.addClass(navShaftListClass);break;default:$navWrap.removeClass(navShaftVerticalClass);$navWrap.removeClass(navShaftListClass);$navWrap.addClass(navShafthorizontalClass);$nav.stop().animate({width:measures.nw},time);break}stageShaftReposition();slideNavShaft({guessIndex:activeIndex,time:time,keep:true});if(o_navThumbs&&frameAppend.nav)slideThumbBorder(time)}measuresSetFLAG=setFLAG||true;ready.ok=true;ready()}}stageLeft=$stage.offset().left;setStagePosition();return this};that.setOptions=function(options){$.extend(opts,options);reset();return this};that.shuffle=function(){data&&shuffle(data)&&reset();return this};function setShadow($el,edge){if(o_shadows){$el.removeClass(shadowsLeftClass+" "+shadowsRightClass);$el.removeClass(shadowsTopClass+" "+shadowsBottomClass);edge&&!$videoPlaying&&$el.addClass(edge.replace(/^|\s/g," "+shadowsClass+"--"))}}that.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){if(!this.inProgress){this.count++;this.inProgress=this.count>this.threshold}},end:function(){if(this.inProgress){this.isEnded=true}},reset:function(){this.count=0;this.inProgress=false;this.isEnded=false}};that.destroy=function(){that.cancelFullScreen();that.stopAutoplay();data=that.data=null;appendElements();activeIndexes=[];detachFrames(STAGE_FRAME_KEY);reset.ok=false;return this};that.playVideo=function(){var dataFrame=activeFrame,video=dataFrame.video,_activeIndex=activeIndex;if(typeof video==="object"&&dataFrame.videoReady){o_nativeFullScreen&&that.fullScreen&&that.cancelFullScreen();waitFor(function(){return!fullScreenApi.is()||_activeIndex!==activeIndex},function(){if(_activeIndex===activeIndex){dataFrame.$video=dataFrame.$video||$(div(videoClass)).append(createVideoFrame(video));dataFrame.$video.appendTo(dataFrame[STAGE_FRAME_KEY]);$wrap.addClass(wrapVideoClass);$videoPlaying=dataFrame.$video;stageNoMove();$arrs.blur();$fullscreenIcon.blur();triggerEvent("loadvideo")}})}return this};that.stopVideo=function(){unloadVideo($videoPlaying,true,true);return this};that.spliceByIndex=function(index,newImgObj){newImgObj.i=index+1;newImgObj.img&&$.ajax({url:newImgObj.img,type:"HEAD",success:function(){data.splice(index,1,newImgObj);reset()}})};function unloadVideo($video,unloadActiveFLAG,releaseAutoplayFLAG){if(unloadActiveFLAG){$wrap.removeClass(wrapVideoClass);$videoPlaying=false;stageNoMove()}if($video&&$video!==$videoPlaying){$video.remove();triggerEvent("unloadvideo")}if(releaseAutoplayFLAG){releaseAutoplay();changeAutoplay()}}function toggleControlsClass(FLAG){$wrap.toggleClass(wrapNoControlsClass,FLAG)}function stageCursor(e){if(stageShaftTouchTail.flow)return;var x=e?e.pageX:stageCursor.x,pointerFLAG=x&&!disableDirrection(getDirection(x))&&opts.click;if(stageCursor.p!==pointerFLAG&&$stage.toggleClass(pointerClass,pointerFLAG)){stageCursor.p=pointerFLAG;stageCursor.x=x}}$stage.on("mousemove",stageCursor);function clickToShow(showOptions){clearTimeout(clickToShow.t);if(opts.clicktransition&&opts.clicktransition!==opts.transition){setTimeout(function(){var _o_transition=opts.transition;that.setOptions({transition:opts.clicktransition});o_transition=_o_transition;clickToShow.t=setTimeout(function(){that.show(showOptions)},10)},0)}else{that.show(showOptions)}}function onStageTap(e,toggleControlsFLAG){var target=e.target,$target=$(target);if($target.hasClass(videoPlayClass)){that.playVideo()}else if(target===fullscreenIcon){that.toggleFullScreen()}else if($videoPlaying){target===videoClose&&unloadVideo($videoPlaying,true,true)}else if(!$fotorama.hasClass(fullscreenClass)){that.requestFullScreen()}}function updateTouchTails(key,value){stageShaftTouchTail[key]=navShaftTouchTail[key]=value}stageShaftTouchTail=moveOnTouch($stageShaft,{onStart:onTouchStart,onMove:function(e,result){setShadow($stage,result.edge)},onTouchEnd:onTouchEnd,onEnd:function(result){var toggleControlsFLAG;setShadow($stage);toggleControlsFLAG=(MS_POINTER&&!hoverFLAG||result.touch)&&opts.arrows;if((result.moved||toggleControlsFLAG&&result.pos!==result.newPos&&!result.control)&&result.$target[0]!==$fullscreenIcon[0]){var index=getIndexByPos(result.newPos,measures.w,opts.margin,repositionIndex);that.show({index:index,time:o_fade?o_transitionDuration:result.time,overPos:result.overPos,user:true})}else if(!result.aborted&&!result.control){onStageTap(result.startEvent,toggleControlsFLAG)}},timeLow:1,timeHigh:1,friction:2,select:"."+selectClass+", ."+selectClass+" *",$wrap:$stage,direction:"horizontal"});navShaftTouchTail=moveOnTouch($navShaft,{onStart:onTouchStart,onMove:function(e,result){setShadow($nav,result.edge)},onTouchEnd:onTouchEnd,onEnd:function(result){function onEnd(){slideNavShaft.l=result.newPos;releaseAutoplay();changeAutoplay();thumbsDraw(result.newPos,true);thumbArrUpdate()}if(!result.moved){var target=result.$target.closest("."+navFrameClass,$navShaft)[0];target&&onNavFrameClick.call(target,result.startEvent)}else if(result.pos!==result.newPos){pausedAutoplayFLAG=true;slide($navShaft,{time:result.time,pos:result.newPos,overPos:result.overPos,direction:opts.navdir,onEnd:onEnd});thumbsDraw(result.newPos);o_shadows&&setShadow($nav,findShadowEdge(result.newPos,navShaftTouchTail.min,navShaftTouchTail.max,result.dir))}else{onEnd()}},timeLow:.5,timeHigh:2,friction:5,$wrap:$nav,direction:opts.navdir});stageWheelTail=wheel($stage,{shift:true,onEnd:function(e,direction){onTouchStart();onTouchEnd();that.show({index:direction,slow:e.altKey})}});navWheelTail=wheel($nav,{onEnd:function(e,direction){onTouchStart();onTouchEnd();var newPos=stop($navShaft)+direction*.25;$navShaft.css(getTranslate(minMaxLimit(newPos,navShaftTouchTail.min,navShaftTouchTail.max),opts.navdir));o_shadows&&setShadow($nav,findShadowEdge(newPos,navShaftTouchTail.min,navShaftTouchTail.max,opts.navdir));navWheelTail.prevent={"<":newPos>=navShaftTouchTail.max,">":newPos<=navShaftTouchTail.min};clearTimeout(navWheelTail.t);navWheelTail.t=setTimeout(function(){slideNavShaft.l=newPos;thumbsDraw(newPos,true)},TOUCH_TIMEOUT);thumbsDraw(newPos)}});$wrap.hover(function(){setTimeout(function(){if(touchedFLAG)return;toggleControlsClass(!(hoverFLAG=true))},0)},function(){if(!hoverFLAG)return;toggleControlsClass(!(hoverFLAG=false))});function onNavFrameClick(e){var index=$(this).data().eq;if(opts.navtype==="thumbs"){clickToShow({index:index,slow:e.altKey,user:true,coo:e._x-$nav.offset().left})}else{clickToShow({index:index,slow:e.altKey,user:true})}}function onArrClick(e){clickToShow({index:$arrs.index(this)?">":"<",slow:e.altKey,user:true})}smartClick($arrs,function(e){stopEvent(e);onArrClick.call(this,e)},{onStart:function(){onTouchStart();stageShaftTouchTail.control=true},onTouchEnd:onTouchEnd});smartClick($thumbArrLeft,function(e){stopEvent(e);if(opts.navtype==="thumbs"){that.show("<")}else{that.showSlide("prev")}});smartClick($thumbArrRight,function(e){stopEvent(e);if(opts.navtype==="thumbs"){that.show(">")}else{that.showSlide("next")}});function addFocusOnControls(el){addFocus(el,function(){setTimeout(function(){lockScroll($stage)},0);toggleControlsClass(false)})}$arrs.each(function(){addEnterUp(this,function(e){onArrClick.call(this,e)});addFocusOnControls(this)});addEnterUp(fullscreenIcon,function(){if($fotorama.hasClass(fullscreenClass)){that.cancelFullScreen();$stageShaft.focus()}else{that.requestFullScreen();$fullscreenIcon.focus()}});addFocusOnControls(fullscreenIcon);function reset(){setData();setOptions();if(!reset.i){reset.i=true;var _startindex=opts.startindex;activeIndex=repositionIndex=dirtyIndex=lastActiveIndex=startIndex=edgeIndex(_startindex)||0}if(size){if(changeToRtl())return;if($videoPlaying){unloadVideo($videoPlaying,true)}activeIndexes=[];detachFrames(STAGE_FRAME_KEY);reset.ok=true;that.show({index:activeIndex,time:0});that.resize()}else{that.destroy()}}function changeToRtl(){if(!changeToRtl.f===o_rtl){changeToRtl.f=o_rtl;activeIndex=size-1-activeIndex;that.reverse();return true}}$.each("load push pop shift unshift reverse sort splice".split(" "),function(i,method){that[method]=function(){data=data||[];if(method!=="load"){Array.prototype[method].apply(data,arguments)}else if(arguments[0]&&typeof arguments[0]==="object"&&arguments[0].length){data=clone(arguments[0])}reset();return that}});function ready(){if(ready.ok){ready.ok=false;triggerEvent("ready")}}reset()};$.fn.fotorama=function(opts){return this.each(function(){var that=this,$fotorama=$(this),fotoramaData=$fotorama.data(),fotorama=fotoramaData.fotorama;if(!fotorama){waitFor(function(){return!isHidden(that)},function(){fotoramaData.urtext=$fotorama.html();new $.Fotorama($fotorama,$.extend({},OPTIONS,window.fotoramaDefaults,opts,fotoramaData))})}else{fotorama.setOptions(opts,true)}})};$.Fotorama.instances=[];function calculateIndexes(){$.each($.Fotorama.instances,function(index,instance){instance.index=index})}function addInstance(instance){$.Fotorama.instances.push(instance);calculateIndexes()}function hideInstance(instance){$.Fotorama.instances.splice(instance.index,1);calculateIndexes()}$.Fotorama.cache={};$.Fotorama.measures={};$=$||{};$.Fotorama=$.Fotorama||{};$.Fotorama.jst=$.Fotorama.jst||{};$.Fotorama.jst.dots=function(v){var __t,__p="",__e=_.escape;__p+='';return __p};$.Fotorama.jst.frameCaption=function(v){var __t,__p="",__e=_.escape;__p+='\r\n
'+((__t=v.caption)==null?"":__t)+"
\r\n
\r\n";return __p};$.Fotorama.jst.style=function(v){var __t,__p="",__e=_.escape;__p+=".fotorama"+((__t=v.s)==null?"":__t)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+((__t=v.m)==null?"":__t)+"px;\r\nheight:"+((__t=v.h)==null?"":__t)+"px}\r\n.fotorama"+((__t=v.s)==null?"":__t)+" .fotorama__thumb-border{\r\nheight:"+((__t=v.h)==null?"":__t)+"px;\r\nborder-width:"+((__t=v.b)==null?"":__t)+"px;\r\nmargin-top:"+((__t=v.m)==null?"":__t)+"px}";return __p};$.Fotorama.jst.thumb=function(v){var __t,__p="",__e=_.escape;__p+='';return __p}})(window,document,location,typeof jQuery!=="undefined"&&jQuery);
diff --git a/lib/web/mage/adminhtml/wysiwyg/widget.js b/lib/web/mage/adminhtml/wysiwyg/widget.js
index 68206fdec6201..aa38e2e1875f6 100644
--- a/lib/web/mage/adminhtml/wysiwyg/widget.js
+++ b/lib/web/mage/adminhtml/wysiwyg/widget.js
@@ -456,7 +456,7 @@ define([
parameters: params,
onComplete: function (transport) {
try {
- editor = wysiwyg.activeEditor();
+ editor = wysiwyg.get(this.widgetTargetId);
widgetTools.onAjaxSuccess(transport);
widgetTools.dialogWindow.modal('closeModal');
@@ -469,7 +469,7 @@ define([
editor.selection.select(activeNode);
editor.selection.setContent(transport.responseText);
} else if (this.bMark) {
- wysiwyg.activeEditor().selection.moveToBookmark(this.bMark);
+ editor.selection.moveToBookmark(this.bMark);
}
}
@@ -513,7 +513,7 @@ define([
* @return {null|wysiwyg.Editor|*}
*/
getWysiwyg: function () {
- return wysiwyg.activeEditor();
+ return wysiwyg.get(this.widgetTargetId);
},
/**
diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js
index a57191fd4aff4..73c5ef4d4f02f 100644
--- a/lib/web/mage/validation.js
+++ b/lib/web/mage/validation.js
@@ -1958,7 +1958,7 @@
}
if (firstActive.length) {
- $('html, body').animate({
+ $('html, body').stop().animate({
scrollTop: firstActive.offset().top
});
firstActive.focus();
diff --git a/pub/health_check.php b/pub/health_check.php
index c9a4965876bb7..fc6d73daa2079 100644
--- a/pub/health_check.php
+++ b/pub/health_check.php
@@ -4,11 +4,17 @@
* See COPYING.txt for license details.
*/
+/**
+ * phpcs:disable PSR1.Files.SideEffects
+ * phpcs:disable Squiz.Functions.GlobalFunction
+ */
use Magento\Framework\Config\ConfigOptionsListConstants;
+// phpcs:ignore Magento2.Functions.DiscouragedFunction
register_shutdown_function("fatalErrorHandler");
try {
+ // phpcs:ignore Magento2.Security.IncludeFile
require __DIR__ . '/../app/bootstrap.php';
/** @var \Magento\Framework\App\ObjectManagerFactory $objectManagerFactory */
$objectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, []);
@@ -20,6 +26,7 @@
$logger = $objectManager->get(\Psr\Log\LoggerInterface::class);
} catch (\Exception $e) {
http_response_code(500);
+ // phpcs:ignore Magento2.Security.LanguageConstruct
exit(1);
}
@@ -35,6 +42,7 @@
} catch (\Exception $e) {
http_response_code(500);
$logger->error("MySQL connection failed: " . $e->getMessage());
+ // phpcs:ignore Magento2.Security.LanguageConstruct
exit(1);
}
}
@@ -47,6 +55,7 @@
!isset($cacheConfig[ConfigOptionsListConstants::CONFIG_PATH_BACKEND_OPTIONS])) {
http_response_code(500);
$logger->error("Cache configuration is invalid");
+ // phpcs:ignore Magento2.Security.LanguageConstruct
exit(1);
}
$cacheBackendClass = $cacheConfig[ConfigOptionsListConstants::CONFIG_PATH_BACKEND];
@@ -57,6 +66,7 @@
} catch (\Exception $e) {
http_response_code(500);
$logger->error("Cache storage is not accessible");
+ // phpcs:ignore Magento2.Security.LanguageConstruct
exit(1);
}
}
@@ -70,7 +80,7 @@
function fatalErrorHandler()
{
$error = error_get_last();
- if ($error !== null) {
+ if ($error !== null && $error['type'] === E_ERROR) {
http_response_code(500);
}
}
diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx
index 696b1964101a5..5521f7024722b 100644
--- a/setup/performance-toolkit/benchmark.jmx
+++ b/setup/performance-toolkit/benchmark.jmx
@@ -374,6 +374,16 @@
${__P(graphqlAddSimpleProductToCartPercentage,0)}
=
+
+ graphqlApplyCouponToCartPercentage
+ ${__P(graphqlApplyCouponToCartPercentage,0)}
+ =
+
+
+ graphqlCatalogBrowsingByGuestPercentage
+ ${__P(graphqlCatalogBrowsingByGuestPercentage,0)}
+ =
+
graphqlCheckoutByGuestPercentage
${__P(graphqlCheckoutByGuestPercentage,0)}
@@ -444,6 +454,11 @@
${__P(graphqlRemoveConfigurableProductFromCartPercentage,0)}
=
+
+ graphqlRemoveCouponFromCartPercentage
+ ${__P(graphqlRemoveCouponFromCartPercentage,0)}
+ =
+
graphqlRemoveSimpleProductFromCartPercentage
${__P(graphqlRemoveSimpleProductFromCartPercentage,0)}
@@ -781,6 +796,7 @@ props.remove("customer_emails_list");
props.remove("categories");
props.remove("cms_pages");
props.remove("cms_blocks");
+props.remove("coupon_codes");
/* This is only used when admin is enabled. */
props.put("activeAdminThread", "");
@@ -2220,6 +2236,59 @@ adminCategoryIdsList.add(vars.get("category_id"));
+
+
+ mpaf/tool/fragments/ce/setup/extract_coupon_codes.jmx
+
+
+
+
+
+
+ false
+ 10
+ =
+ true
+ searchCriteria[pageSize]
+
+
+
+
+
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}rest/default/V1/coupons/search
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+ javascript
+
+
+
+ var data = JSON.parse(prev.getResponseDataAsString());
+
+var couponCodes = [];
+
+for (var i in data.items) {
+ var coupon = data.items[i];
+ couponCodes.push({"coupon_id":coupon.coupon_id, "rule_id":coupon.rule_id, "code": coupon.code});
+ }
+
+props.put("coupon_codes", couponCodes);
+
+
+
+
+
@@ -39930,7 +39999,7 @@ vars.putObject("category", categories[number]);
false
- {"query":"query productSearch($inputText: String!, $categoryId: String) {\n products(search: $inputText, filter: { category_id: { eq: $categoryId } }) {\n items {\n id\n name\n small_image {\n label\n url\n }\n url_key\n price {\n regularPrice {\n amount {\n value\n currency\n }\n }\n }\n }\n total_count\n filters {\n name\n filter_items_count\n request_var\n filter_items {\n label\n value_string\n }\n }\n }\n}","variables":{"inputText":"Product","categoryId":"${category_id}"},"operationName":"productSearch"}
+ {"query":"query productSearch($inputText: String!, $categoryId: String) {\n products(\n pageSize:12\n search: $inputText, filter: { category_id: { eq: $categoryId } }) {\n items {\n id\n name\n small_image {\n label\n url\n }\n url_key\n price {\n regularPrice {\n amount {\n value\n currency\n }\n }\n }\n }\n total_count\n filters {\n name\n filter_items_count\n request_var\n filter_items {\n label\n value_string\n }\n }\n }\n}","variables":{"inputText":"Product","categoryId":"${category_id}"},"operationName":"productSearch"}
=
@@ -41062,7 +41131,649 @@ vars.putObject("randomIntGenerator", random);
-
+
+ true
+
+
+
+ false
+ {"query":"{\n cart(cart_id: \"${quote_id}\") {\n items {\n id\n qty\n product {\n sku\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/get_empty_cart.jmx
+
+
+
+
+ {"data":{"cart":{"items":[]}}}
+
+ Assertion.response_data
+ false
+ 8
+
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n setBillingAddressOnCart(\n input: {\n cart_id: \"${quote_id}\"\n billing_address: {\n address: {\n firstname: \"test firstname\"\n lastname: \"test lastname\"\n company: \"test company\"\n street: [\"test street 1\", \"test street 2\"]\n city: \"test city\"\n region: \"test region\"\n postcode: \"887766\"\n country_code: \"US\"\n telephone: \"88776655\"\n save_in_address_book: false\n }\n }\n }\n ) {\n cart {\n billing_address {\n firstname\n lastname\n company\n street\n city\n postcode\n telephone\n country {\n code\n label\n }\n address_type\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/set_billing_address_on_cart.jmx
+
+
+
+
+ {"data":{"setBillingAddressOnCart":{"cart":{"billing_address":{"firstname":"test firstname","lastname":"test lastname","company":"test company","street":["test street 1","test street 2"],"city":"test city","postcode":"887766","telephone":"88776655","country":{"code":"US","label":"US"},"address_type":"BILLING"}}}}}
+
+ Assertion.response_data
+ false
+ 8
+
+
+
+
+
+
+
+ 1
+ false
+ 1
+ ${graphqlAddSimpleProductToCartPercentage}
+ mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
+
+
+
+var testLabel = "${testLabel}" ? " (${testLabel})" : "";
+if (testLabel
+ && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy'
+) {
+ if (sampler.getName().indexOf(testLabel) == -1) {
+ sampler.setName(sampler.getName() + testLabel);
+ }
+} else if (sampler.getName().indexOf("SetUp - ") == -1) {
+ sampler.setName("SetUp - " + sampler.getName());
+}
+
+ javascript
+ mpaf/tool/fragments/_system/setup_label.jmx
+
+
+
+ vars.put("testLabel", "GraphQL Add Simple Product To Cart");
+
+ true
+
+
+
+
+
+
+ Content-Type
+ application/json
+
+
+ Accept
+ */*
+
+
+ mpaf/tool/fragments/ce/api/header_manager_before_token.jmx
+
+
+
+ mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx
+
+import java.util.Random;
+
+Random random = new Random();
+if (${seedForRandom} > 0) {
+ random.setSeed(${seedForRandom} + ${__threadNum});
+}
+
+vars.putObject("randomIntGenerator", random);
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n createEmptyCart\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/create_empty_cart.jmx
+
+
+
+ quote_id
+ $.data.createEmptyCart
+
+
+ BODY
+
+
+
+
+ {"data":{"createEmptyCart":"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+
+import java.util.Random;
+
+Random random = vars.getObject("randomIntGenerator");
+number = random.nextInt(props.get("simple_products_list").size());
+product = props.get("simple_products_list").get(number);
+
+vars.put("product_url_key", product.get("url_key"));
+vars.put("product_id", product.get("id"));
+vars.put("product_name", product.get("title"));
+vars.put("product_uenc", product.get("uenc"));
+vars.put("product_sku", product.get("sku"));
+
+
+
+ true
+ mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation { \n addSimpleProductsToCart(\n input: {\n cart_id: \"${quote_id}\"\n cartItems: [\n {\n data: {\n qty: 2\n sku: \"${product_sku}\"\n }\n }\n ]\n }\n ) {\n cart {\n items {\n qty\n product {\n sku\n }\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/add_simple_product_to_cart.jmx
+
+
+
+
+ addSimpleProductsToCart
+ "sku":"${product_sku}"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+
+
+ 1
+ false
+ 1
+ ${graphqlAddConfigurableProductToCartPercentage}
+ mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
+
+
+
+var testLabel = "${testLabel}" ? " (${testLabel})" : "";
+if (testLabel
+ && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy'
+) {
+ if (sampler.getName().indexOf(testLabel) == -1) {
+ sampler.setName(sampler.getName() + testLabel);
+ }
+} else if (sampler.getName().indexOf("SetUp - ") == -1) {
+ sampler.setName("SetUp - " + sampler.getName());
+}
+
+ javascript
+ mpaf/tool/fragments/_system/setup_label.jmx
+
+
+
+ vars.put("testLabel", "GraphQL Add Configurable Product To Cart");
+
+ true
+
+
+
+
+
+
+ Content-Type
+ application/json
+
+
+ Accept
+ */*
+
+
+ mpaf/tool/fragments/ce/api/header_manager_before_token.jmx
+
+
+
+ mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx
+
+import java.util.Random;
+
+Random random = new Random();
+if (${seedForRandom} > 0) {
+ random.setSeed(${seedForRandom} + ${__threadNum});
+}
+
+vars.putObject("randomIntGenerator", random);
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n createEmptyCart\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/create_empty_cart.jmx
+
+
+
+ quote_id
+ $.data.createEmptyCart
+
+
+ BODY
+
+
+
+
+ {"data":{"createEmptyCart":"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+
+import java.util.Random;
+
+Random random = vars.getObject("randomIntGenerator");
+number = random.nextInt(props.get("configurable_products_list").size());
+product = props.get("configurable_products_list").get(number);
+
+vars.put("product_url_key", product.get("url_key"));
+vars.put("product_id", product.get("id"));
+vars.put("product_name", product.get("title"));
+vars.put("product_uenc", product.get("uenc"));
+vars.put("product_sku", product.get("sku"));
+
+
+
+ true
+ mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx
+
+
+
+ true
+
+
+
+ false
+ {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/get_configurable_product_details_by_name.jmx
+
+
+
+
+ "sku":"${product_sku}","name":"${product_name}"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+ product_option
+ $.data.products.items[0].variants[0].product.sku
+
+
+ BODY
+ mpaf/tool/fragments/ce/graphql/extract_configurable_product_option.jmx
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n addConfigurableProductsToCart(\n input: {\n cart_id: \"${quote_id}\"\n cartItems: [\n {\n variant_sku: \"${product_option}\"\n data: {\n qty: 2\n sku: \"${product_option}\"\n }\n }\n ]\n }\n ) {\n cart {\n items {\n id\n qty\n product {\n name\n sku\n }\n ... on ConfigurableCartItem {\n configurable_options {\n option_label\n }\n }\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/add_configurable_product_to_cart.jmx
+
+
+
+
+ addConfigurableProductsToCart
+ "sku":"${product_option}"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+
+
+ 1
+ false
+ 1
+ ${graphqlUpdateSimpleProductQtyInCartPercentage}
+ mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
+
+
+
+var testLabel = "${testLabel}" ? " (${testLabel})" : "";
+if (testLabel
+ && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy'
+) {
+ if (sampler.getName().indexOf(testLabel) == -1) {
+ sampler.setName(sampler.getName() + testLabel);
+ }
+} else if (sampler.getName().indexOf("SetUp - ") == -1) {
+ sampler.setName("SetUp - " + sampler.getName());
+}
+
+ javascript
+ mpaf/tool/fragments/_system/setup_label.jmx
+
+
+
+ vars.put("testLabel", "GraphQL Update Simple Product Qty In Cart");
+
+ true
+
+
+
+
+
+
+ Content-Type
+ application/json
+
+
+ Accept
+ */*
+
+
+ mpaf/tool/fragments/ce/api/header_manager_before_token.jmx
+
+
+
+ mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx
+
+import java.util.Random;
+
+Random random = new Random();
+if (${seedForRandom} > 0) {
+ random.setSeed(${seedForRandom} + ${__threadNum});
+}
+
+vars.putObject("randomIntGenerator", random);
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n createEmptyCart\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/create_empty_cart.jmx
+
+
+
+ quote_id
+ $.data.createEmptyCart
+
+
+ BODY
+
+
+
+
+ {"data":{"createEmptyCart":"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+
+import java.util.Random;
+
+Random random = vars.getObject("randomIntGenerator");
+number = random.nextInt(props.get("simple_products_list").size());
+product = props.get("simple_products_list").get(number);
+
+vars.put("product_url_key", product.get("url_key"));
+vars.put("product_id", product.get("id"));
+vars.put("product_name", product.get("title"));
+vars.put("product_uenc", product.get("uenc"));
+vars.put("product_sku", product.get("sku"));
+
+
+
+ true
+ mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation { \n addSimpleProductsToCart(\n input: {\n cart_id: \"${quote_id}\"\n cartItems: [\n {\n data: {\n qty: 2\n sku: \"${product_sku}\"\n }\n }\n ]\n }\n ) {\n cart {\n items {\n qty\n product {\n sku\n }\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/add_simple_product_to_cart.jmx
+
+
+
+
+ addSimpleProductsToCart
+ "sku":"${product_sku}"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
true
@@ -41087,27 +41798,35 @@ vars.putObject("randomIntGenerator", random);
false
false
- mpaf/tool/fragments/ce/graphql/get_empty_cart.jmx
+ mpaf/tool/fragments/ce/graphql/get_cart.jmx
+
+ item_id
+ $.data.cart.items[0].id
+
+
+ BODY
+
+
- {"data":{"cart":{"items":[]}}}
+ {"data":{"cart":{"items":
Assertion.response_data
false
- 8
+ 2
-
+
true
false
- {"query":"mutation {\n setBillingAddressOnCart(\n input: {\n cart_id: \"${quote_id}\"\n billing_address: {\n address: {\n firstname: \"test firstname\"\n lastname: \"test lastname\"\n company: \"test company\"\n street: [\"test street 1\", \"test street 2\"]\n city: \"test city\"\n region: \"test region\"\n postcode: \"887766\"\n country_code: \"US\"\n telephone: \"88776655\"\n save_in_address_book: false\n }\n }\n }\n ) {\n cart {\n billing_address {\n firstname\n lastname\n company\n street\n city\n postcode\n telephone\n country {\n code\n label\n }\n address_type\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"mutation {\n updateCartItems(input: {\n cart_id: \"${quote_id}\"\n cart_items: [\n {\n cart_item_id: ${item_id}\n quantity: 5\n }\n ]\n }) {\n cart {\n items {\n id\n qty\n }\n }\n }\n}","variables":null,"operationName":null}
=
@@ -41126,12 +41845,12 @@ vars.putObject("randomIntGenerator", random);
false
false
- mpaf/tool/fragments/ce/graphql/set_billing_address_on_cart.jmx
+ mpaf/tool/fragments/ce/graphql/update_simple_product_qty_in_cart.jmx
- {"data":{"setBillingAddressOnCart":{"cart":{"billing_address":{"firstname":"test firstname","lastname":"test lastname","company":"test company","street":["test street 1","test street 2"],"city":"test city","postcode":"887766","telephone":"88776655","country":{"code":"US","label":"US"},"address_type":"BILLING"}}}}}
+ {"data":{"updateCartItems":{"cart":{"items":[{"id":"${item_id}","qty":5}]}}}}
Assertion.response_data
false
@@ -41142,11 +41861,11 @@ vars.putObject("randomIntGenerator", random);
-
+
1
false
1
- ${graphqlAddSimpleProductToCartPercentage}
+ ${graphqlUpdateConfigurableProductQtyInCartPercentage}
mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
@@ -41167,7 +41886,7 @@ if (testLabel
- vars.put("testLabel", "GraphQL Add Simple Product To Cart");
+ vars.put("testLabel", "GraphQL Update Configurable Product Qty In Cart");
true
@@ -41252,13 +41971,13 @@ vars.putObject("randomIntGenerator", random);
-
+
import java.util.Random;
Random random = vars.getObject("randomIntGenerator");
-number = random.nextInt(props.get("simple_products_list").size());
-product = props.get("simple_products_list").get(number);
+number = random.nextInt(props.get("configurable_products_list").size());
+product = props.get("configurable_products_list").get(number);
vars.put("product_url_key", product.get("url_key"));
vars.put("product_id", product.get("id"));
@@ -41269,16 +41988,16 @@ vars.put("product_sku", product.get("sku"));
true
- mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx
+ mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx
-
+
true
false
- {"query":"mutation { \n addSimpleProductsToCart(\n input: {\n cart_id: \"${quote_id}\"\n cartItems: [\n {\n data: {\n qty: 2\n sku: \"${product_sku}\"\n }\n }\n ]\n }\n ) {\n cart {\n items {\n qty\n product {\n sku\n }\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
=
@@ -41297,93 +42016,36 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/add_simple_product_to_cart.jmx
+ mpaf/tool/fragments/ce/graphql/get_configurable_product_details_by_name.jmx
- addSimpleProductsToCart
- "sku":"${product_sku}"
+ "sku":"${product_sku}","name":"${product_name}"
Assertion.response_data
false
2
-
-
-
-
-
- 1
- false
- 1
- ${graphqlAddConfigurableProductToCartPercentage}
- mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
-
-
-
-var testLabel = "${testLabel}" ? " (${testLabel})" : "";
-if (testLabel
- && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy'
-) {
- if (sampler.getName().indexOf(testLabel) == -1) {
- sampler.setName(sampler.getName() + testLabel);
- }
-} else if (sampler.getName().indexOf("SetUp - ") == -1) {
- sampler.setName("SetUp - " + sampler.getName());
-}
-
- javascript
- mpaf/tool/fragments/_system/setup_label.jmx
-
-
-
- vars.put("testLabel", "GraphQL Add Configurable Product To Cart");
-
- true
-
-
-
-
-
- Content-Type
- application/json
-
-
- Accept
- */*
-
-
- mpaf/tool/fragments/ce/api/header_manager_before_token.jmx
+
+ product_option
+ $.data.products.items[0].variants[0].product.sku
+
+
+ BODY
+ mpaf/tool/fragments/ce/graphql/extract_configurable_product_option.jmx
+
-
- mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx
-
-import java.util.Random;
-
-Random random = new Random();
-if (${seedForRandom} > 0) {
- random.setSeed(${seedForRandom} + ${__threadNum});
-}
-
-vars.putObject("randomIntGenerator", random);
-
-
-
- true
-
-
-
-
+
true
false
- {"query":"mutation {\n createEmptyCart\n}","variables":null,"operationName":null}
+ {"query":"mutation {\n addConfigurableProductsToCart(\n input: {\n cart_id: \"${quote_id}\"\n cartItems: [\n {\n variant_sku: \"${product_option}\"\n data: {\n qty: 2\n sku: \"${product_option}\"\n }\n }\n ]\n }\n ) {\n cart {\n items {\n id\n qty\n product {\n name\n sku\n }\n ... on ConfigurableCartItem {\n configurable_options {\n option_label\n }\n }\n }\n }\n }\n}","variables":null,"operationName":null}
=
@@ -41402,20 +42064,13 @@ vars.putObject("randomIntGenerator", random);
false
false
- mpaf/tool/fragments/ce/graphql/create_empty_cart.jmx
+ mpaf/tool/fragments/ce/graphql/add_configurable_product_to_cart.jmx
-
- quote_id
- $.data.createEmptyCart
-
-
- BODY
-
-
- {"data":{"createEmptyCart":"
+ addConfigurableProductsToCart
+ "sku":"${product_option}"
Assertion.response_data
false
@@ -41424,33 +42079,13 @@ vars.putObject("randomIntGenerator", random);
-
-
-import java.util.Random;
-
-Random random = vars.getObject("randomIntGenerator");
-number = random.nextInt(props.get("configurable_products_list").size());
-product = props.get("configurable_products_list").get(number);
-
-vars.put("product_url_key", product.get("url_key"));
-vars.put("product_id", product.get("id"));
-vars.put("product_name", product.get("title"));
-vars.put("product_uenc", product.get("uenc"));
-vars.put("product_sku", product.get("sku"));
-
-
-
- true
- mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx
-
-
-
+
true
false
- {"query":"query productDetailByName($name: String, $onServer: Boolean!) {\n products(filter: { name: { eq: $name } }) {\n items {\n id\n sku\n name\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n #fashion_color\n #fashion_size\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetailByName"}
+ {"query":"{\n cart(cart_id: \"${quote_id}\") {\n items {\n id\n qty\n product {\n sku\n }\n }\n }\n}","variables":null,"operationName":null}
=
@@ -41469,36 +42104,35 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/get_configurable_product_details_by_name.jmx
+ mpaf/tool/fragments/ce/graphql/get_cart.jmx
+
+ item_id
+ $.data.cart.items[0].id
+
+
+ BODY
+
+
- "sku":"${product_sku}","name":"${product_name}"
+ {"data":{"cart":{"items":
Assertion.response_data
false
2
-
-
- product_option
- $.data.products.items[0].variants[0].product.sku
-
-
- BODY
- mpaf/tool/fragments/ce/graphql/extract_configurable_product_option.jmx
-
-
+
-
+
true
false
- {"query":"mutation {\n addConfigurableProductsToCart(\n input: {\n cart_id: \"${quote_id}\"\n cartItems: [\n {\n variant_sku: \"${product_option}\"\n data: {\n qty: 2\n sku: \"${product_option}\"\n }\n }\n ]\n }\n ) {\n cart {\n items {\n id\n qty\n product {\n name\n sku\n }\n ... on ConfigurableCartItem {\n configurable_options {\n option_label\n }\n }\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"mutation {\n updateCartItems(input: {\n cart_id: \"${quote_id}\"\n cart_items: [\n {\n cart_item_id: ${item_id}\n quantity: 5\n }\n ]\n }) {\n cart {\n items {\n id\n qty\n }\n }\n }\n}","variables":null,"operationName":null}
=
@@ -41517,28 +42151,27 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/add_configurable_product_to_cart.jmx
+ mpaf/tool/fragments/ce/graphql/update_configurable_product_qty_in_cart.jmx
- addConfigurableProductsToCart
- "sku":"${product_option}"
+ {"data":{"updateCartItems":{"cart":{"items":[{"id":"${item_id}","qty":5}]}}}}
Assertion.response_data
false
- 2
+ 8
-
+
1
false
1
- ${graphqlUpdateSimpleProductQtyInCartPercentage}
+ ${graphqlRemoveSimpleProductFromCartPercentage}
mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
@@ -41559,7 +42192,7 @@ if (testLabel
- vars.put("testLabel", "GraphQL Update Simple Product Qty In Cart");
+ vars.put("testLabel", "GraphQL Remove Simple Product From Cart");
true
@@ -41751,13 +42384,13 @@ vars.put("product_sku", product.get("sku"));
-
+
true
false
- {"query":"mutation {\n updateCartItems(input: {\n cart_id: \"${quote_id}\"\n cart_items: [\n {\n cart_item_id: ${item_id}\n quantity: 5\n }\n ]\n }) {\n cart {\n items {\n id\n qty\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"mutation {\n removeItemFromCart(\n input: {\n cart_id: \"${quote_id}\"\n cart_item_id: ${item_id}\n }\n ) {\n cart {\n items {\n qty\n }\n }\n }\n}","variables":null,"operationName":null}
=
@@ -41776,12 +42409,12 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/update_simple_product_qty_in_cart.jmx
+ mpaf/tool/fragments/ce/graphql/remove_simple_product_from_cart.jmx
- {"data":{"updateCartItems":{"cart":{"items":[{"id":"${item_id}","qty":5}]}}}}
+ {"data":{"removeItemFromCart":{"cart":{"items":[]}}}}
Assertion.response_data
false
@@ -41792,11 +42425,11 @@ vars.put("product_sku", product.get("sku"));
-
+
1
false
1
- ${graphqlUpdateConfigurableProductQtyInCartPercentage}
+ ${graphqlRemoveConfigurableProductFromCartPercentage}
mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
@@ -41817,7 +42450,7 @@ if (testLabel
- vars.put("testLabel", "GraphQL Update Configurable Product Qty In Cart");
+ vars.put("testLabel", "GraphQL Remove Configurable Product From Cart");
true
@@ -42057,13 +42690,13 @@ vars.put("product_sku", product.get("sku"));
-
+
true
false
- {"query":"mutation {\n updateCartItems(input: {\n cart_id: \"${quote_id}\"\n cart_items: [\n {\n cart_item_id: ${item_id}\n quantity: 5\n }\n ]\n }) {\n cart {\n items {\n id\n qty\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"mutation {\n removeItemFromCart(\n input: {\n cart_id: \"${quote_id}\"\n cart_item_id: ${item_id}\n }\n ) {\n cart {\n items {\n qty\n }\n }\n }\n}","variables":null,"operationName":null}
=
@@ -42082,12 +42715,12 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/update_configurable_product_qty_in_cart.jmx
+ mpaf/tool/fragments/ce/graphql/remove_configurable_product_from_cart.jmx
- {"data":{"updateCartItems":{"cart":{"items":[{"id":"${item_id}","qty":5}]}}}}
+ {"data":{"removeItemFromCart":{"cart":{"items":[]}}}}
Assertion.response_data
false
@@ -42098,11 +42731,11 @@ vars.put("product_sku", product.get("sku"));
-
+
1
false
1
- ${graphqlRemoveSimpleProductFromCartPercentage}
+ ${graphqlApplyCouponToCartPercentage}
mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
@@ -42123,7 +42756,7 @@ if (testLabel
- vars.put("testLabel", "GraphQL Remove Simple Product From Cart");
+ vars.put("testLabel", "GraphQL Apply Coupon To Cart");
true
@@ -42268,13 +42901,29 @@ vars.put("product_sku", product.get("sku"));
-
+
+ javascript
+
+
+
+ random = vars.getObject("randomIntGenerator");
+
+var coupons = props.get("coupon_codes");
+number = random.nextInt(coupons.length);
+
+vars.put("coupon_code", coupons[number].code);
+
+ mpaf/tool/fragments/ce/common/extract_coupon_code_setup.jmx
+
+
+
+
true
false
- {"query":"{\n cart(cart_id: \"${quote_id}\") {\n items {\n id\n qty\n product {\n sku\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"mutation {\n applyCouponToCart(input: {cart_id: \"${quote_id}\", coupon_code: \"${coupon_code}\"}) {\n cart {\n applied_coupon {\n code\n }\n }\n }\n}","variables":null,"operationName":null}
=
@@ -42293,12 +42942,116 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/get_cart.jmx
+ mpaf/tool/fragments/ce/graphql/apply_coupon_to_cart.jmx
-
- item_id
- $.data.cart.items[0].id
+
+
+ {"data":{"applyCouponToCart":{"cart":{"applied_coupon":{"code":"${coupon_code}"}}}}}
+
+ Assertion.response_data
+ false
+ 8
+
+
+
+
+
+
+
+ 1
+ false
+ 1
+ ${graphqlRemoveCouponFromCartPercentage}
+ mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
+
+
+
+var testLabel = "${testLabel}" ? " (${testLabel})" : "";
+if (testLabel
+ && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy'
+) {
+ if (sampler.getName().indexOf(testLabel) == -1) {
+ sampler.setName(sampler.getName() + testLabel);
+ }
+} else if (sampler.getName().indexOf("SetUp - ") == -1) {
+ sampler.setName("SetUp - " + sampler.getName());
+}
+
+ javascript
+ mpaf/tool/fragments/_system/setup_label.jmx
+
+
+
+ vars.put("testLabel", "GraphQL Remove Coupon From Cart");
+
+ true
+
+
+
+
+
+
+ Content-Type
+ application/json
+
+
+ Accept
+ */*
+
+
+ mpaf/tool/fragments/ce/api/header_manager_before_token.jmx
+
+
+
+ mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx
+
+import java.util.Random;
+
+Random random = new Random();
+if (${seedForRandom} > 0) {
+ random.setSeed(${seedForRandom} + ${__threadNum});
+}
+
+vars.putObject("randomIntGenerator", random);
+
+
+
+ true
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n createEmptyCart\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/create_empty_cart.jmx
+
+
+
+ quote_id
+ $.data.createEmptyCart
BODY
@@ -42306,7 +43059,7 @@ vars.put("product_sku", product.get("sku"));
- {"data":{"cart":{"items":
+ {"data":{"createEmptyCart":"
Assertion.response_data
false
@@ -42315,13 +43068,33 @@ vars.put("product_sku", product.get("sku"));
-
+
+
+import java.util.Random;
+
+Random random = vars.getObject("randomIntGenerator");
+number = random.nextInt(props.get("simple_products_list").size());
+product = props.get("simple_products_list").get(number);
+
+vars.put("product_url_key", product.get("url_key"));
+vars.put("product_id", product.get("id"));
+vars.put("product_name", product.get("title"));
+vars.put("product_uenc", product.get("uenc"));
+vars.put("product_sku", product.get("sku"));
+
+
+
+ true
+ mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx
+
+
+
true
false
- {"query":"mutation {\n removeItemFromCart(\n input: {\n cart_id: \"${quote_id}\"\n cart_item_id: ${item_id}\n }\n ) {\n cart {\n items {\n qty\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"mutation { \n addSimpleProductsToCart(\n input: {\n cart_id: \"${quote_id}\"\n cartItems: [\n {\n data: {\n qty: 2\n sku: \"${product_sku}\"\n }\n }\n ]\n }\n ) {\n cart {\n items {\n qty\n product {\n sku\n }\n }\n }\n }\n}","variables":null,"operationName":null}
=
@@ -42340,12 +43113,107 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/remove_simple_product_from_cart.jmx
+ mpaf/tool/fragments/ce/graphql/add_simple_product_to_cart.jmx
- {"data":{"removeItemFromCart":{"cart":{"items":[]}}}}
+ addSimpleProductsToCart
+ "sku":"${product_sku}"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+ javascript
+
+
+
+ random = vars.getObject("randomIntGenerator");
+
+var coupons = props.get("coupon_codes");
+number = random.nextInt(coupons.length);
+
+vars.put("coupon_code", coupons[number].code);
+
+ mpaf/tool/fragments/ce/common/extract_coupon_code_setup.jmx
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n applyCouponToCart(input: {cart_id: \"${quote_id}\", coupon_code: \"${coupon_code}\"}) {\n cart {\n applied_coupon {\n code\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/apply_coupon_to_cart.jmx
+
+
+
+
+ {"data":{"applyCouponToCart":{"cart":{"applied_coupon":{"code":"${coupon_code}"}}}}}
+
+ Assertion.response_data
+ false
+ 8
+
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n removeCouponFromCart(input: {cart_id: \"${quote_id}\"}) {\n cart {\n applied_coupon {\n code\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/remove_coupon_from_cart.jmx
+
+
+
+
+ {"data":{"removeCouponFromCart":{"cart":{"applied_coupon":null}}}}
Assertion.response_data
false
@@ -42356,11 +43224,11 @@ vars.put("product_sku", product.get("sku"));
-
+
1
false
1
- ${graphqlRemoveConfigurableProductFromCartPercentage}
+ ${graphqlCatalogBrowsingByGuestPercentage}
mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx
@@ -42381,7 +43249,7 @@ if (testLabel
- vars.put("testLabel", "GraphQL Remove Configurable Product From Cart");
+ vars.put("testLabel", "GraphQL Catalog Browsing By Guest");
true
@@ -42419,13 +43287,31 @@ vars.putObject("randomIntGenerator", random);
-
+
+ javascript
+
+
+
+ random = vars.getObject("randomIntGenerator");
+
+var categories = props.get("categories");
+number = random.nextInt(categories.length);
+
+vars.put("category_url_key", categories[number].url_key);
+vars.put("category_name", categories[number].name);
+vars.put("category_id", categories[number].id);
+vars.putObject("category", categories[number]);
+
+ mpaf/tool/fragments/ce/common/extract_category_setup.jmx
+
+
+
true
false
- {"query":"mutation {\n createEmptyCart\n}","variables":null,"operationName":null}
+ {"query":"query navigationMenu($id: Int!) {\n category(id: $id) {\n id\n name\n product_count\n path\n children {\n id\n name\n position\n level\n url_key\n url_path\n product_count\n children_count\n path\n productImagePreview: products(pageSize: 1) {\n items {\n small_image {\n label\n url\n }\n }\n }\n }\n }\n}","variables":{"id":${category_id}},"operationName":"navigationMenu"}
=
@@ -42444,20 +43330,151 @@ vars.putObject("randomIntGenerator", random);
false
false
- mpaf/tool/fragments/ce/graphql/create_empty_cart.jmx
+ mpaf/tool/fragments/ce/graphql/get_navigation_menu_by_category_id.jmx
-
- quote_id
- $.data.createEmptyCart
+
+
+ "id":${category_id},"name":"${category_name}","product_count"
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"query productSearch($inputText: String!, $categoryId: String) {\n products(\n pageSize:12\n search: $inputText, filter: { category_id: { eq: $categoryId } }) {\n items {\n id\n name\n small_image {\n label\n url\n }\n url_key\n price {\n regularPrice {\n amount {\n value\n currency\n }\n }\n }\n }\n total_count\n filters {\n name\n filter_items_count\n request_var\n filter_items {\n label\n value_string\n }\n }\n }\n}","variables":{"inputText":"Product","categoryId":"${category_id}"},"operationName":"productSearch"}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/get_product_search_by_text_and_category_id.jmx
+
+
+
+ graphql_search_products_query_total_count
+ $.data.products.total_count
BODY
+
+ String totalCount=vars.get("graphql_search_products_query_total_count");
+
+if (totalCount == null) {
+ Failure = true;
+ FailureMessage = "Not Expected \"totalCount\" to be null";
+} else {
+ if (Integer.parseInt(totalCount) < 1) {
+ Failure = true;
+ FailureMessage = "Expected \"totalCount\" to be greater than zero, Actual: " + totalCount;
+ } else {
+ Failure = false;
+ }
+}
+
+
+
+
+
+ false
+
+
+
+
+
+ true
+
+
+
+ false
+
+ {"query":"query resolveUrl($urlKey: String!) {\n urlResolver(url: $urlKey) {\n type\n id\n }\n}","variables":{"urlKey":"${category_url_key}${url_suffix}"},"operationName":"resolveUrl"}
+
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/get_url_info_by_url_key.jmx
+
+
+
+ {"type":"CATEGORY","id":${category_id}}
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"query category($id: Int!, $currentPage: Int, $pageSize: Int) {\n category(id: $id) {\n product_count\n description\n url_key\n name\n id\n breadcrumbs {\n category_name\n category_url_key\n __typename\n }\n products(pageSize: $pageSize, currentPage: $currentPage) {\n total_count\n items {\n id\n name\n # small_image\n # short_description\n url_key\n special_price\n special_from_date\n special_to_date\n price {\n regularPrice {\n amount {\n value\n currency\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n","variables":{"id":${category_id},"currentPage":1,"pageSize":12},"operationName":"category"}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/get_list_of_products_by_category_id.jmx
+
+
- {"data":{"createEmptyCart":"
+ "name":"${category_name}","id":${category_id},
Assertion.response_data
false
@@ -42523,24 +43540,15 @@ vars.put("product_sku", product.get("sku"));
2
-
-
- product_option
- $.data.products.items[0].variants[0].product.sku
-
-
- BODY
- mpaf/tool/fragments/ce/graphql/extract_configurable_product_option.jmx
-
-
+
-
+
true
false
- {"query":"mutation {\n addConfigurableProductsToCart(\n input: {\n cart_id: \"${quote_id}\"\n cartItems: [\n {\n variant_sku: \"${product_option}\"\n data: {\n qty: 2\n sku: \"${product_option}\"\n }\n }\n ]\n }\n ) {\n cart {\n items {\n id\n qty\n product {\n name\n sku\n }\n ... on ConfigurableCartItem {\n configurable_options {\n option_label\n }\n }\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"query productDetail($urlKey: String, $onServer: Boolean!) {\n productDetail: products(filter: { url_key: { eq: $urlKey } }) {\n items {\n sku\n name\n price {\n regularPrice {\n amount {\n currency\n value\n }\n }\n }\n description {html}\n media_gallery_entries {\n label\n position\n disabled\n file\n }\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n # Yes, Products have `meta_keyword` and\n # everything else has `meta_keywords`.\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"urlKey":"${product_url_key}","onServer":false},"operationName":"productDetail"}
=
@@ -42559,13 +43567,12 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/add_configurable_product_to_cart.jmx
+ mpaf/tool/fragments/ce/graphql/get_configurable_product_details_by_product_url_key.jmx
- addConfigurableProductsToCart
- "sku":"${product_option}"
+ "sku":"${product_sku}","name":"${product_name}"
Assertion.response_data
false
@@ -42574,13 +43581,33 @@ vars.put("product_sku", product.get("sku"));
-
+
+
+import java.util.Random;
+
+Random random = vars.getObject("randomIntGenerator");
+number = random.nextInt(props.get("simple_products_list").size());
+product = props.get("simple_products_list").get(number);
+
+vars.put("product_url_key", product.get("url_key"));
+vars.put("product_id", product.get("id"));
+vars.put("product_name", product.get("title"));
+vars.put("product_uenc", product.get("uenc"));
+vars.put("product_sku", product.get("sku"));
+
+
+
+ true
+ mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx
+
+
+
true
false
- {"query":"{\n cart(cart_id: \"${quote_id}\") {\n items {\n id\n qty\n product {\n sku\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"query productDetail($name: String, $onServer: Boolean!) {\n productDetail: products(filter: { name: { eq: $name } }) {\n items {\n sku\n name\n price {\n regularPrice {\n amount {\n currency\n value\n }\n }\n }\n description {html}\n media_gallery_entries {\n label\n position\n disabled\n file\n }\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n # Yes, Products have `meta_keyword` and\n # everything else has `meta_keywords`.\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"name":"${product_name}","onServer":false},"operationName":"productDetail"}
=
@@ -42599,20 +43626,12 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/get_cart.jmx
+ mpaf/tool/fragments/ce/graphql/get_simple_product_details_by_name.jmx
-
- item_id
- $.data.cart.items[0].id
-
-
- BODY
-
-
- {"data":{"cart":{"items":
+ "sku":"${product_sku}","name":"${product_name}"
Assertion.response_data
false
@@ -42621,13 +43640,13 @@ vars.put("product_sku", product.get("sku"));
-
+
true
false
- {"query":"mutation {\n removeItemFromCart(\n input: {\n cart_id: \"${quote_id}\"\n cart_item_id: ${item_id}\n }\n ) {\n cart {\n items {\n qty\n }\n }\n }\n}","variables":null,"operationName":null}
+ {"query":"query productDetail($urlKey: String, $onServer: Boolean!) {\n productDetail: products(filter: { url_key: { eq: $urlKey } }) {\n items {\n sku\n name\n price {\n regularPrice {\n amount {\n currency\n value\n }\n }\n }\n description {html}\n media_gallery_entries {\n label\n position\n disabled\n file\n }\n ... on ConfigurableProduct {\n configurable_options {\n attribute_code\n attribute_id\n id\n label\n values {\n default_label\n label\n store_label\n use_default_value\n value_index\n }\n }\n variants {\n product {\n id\n media_gallery_entries {\n disabled\n file\n label\n position\n }\n sku\n stock_status\n }\n }\n }\n meta_title @include(if: $onServer)\n # Yes, Products have `meta_keyword` and\n # everything else has `meta_keywords`.\n meta_keyword @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n }\n}","variables":{"urlKey":"${product_url_key}","onServer":false},"operationName":"productDetail"}
=
@@ -42646,19 +43665,74 @@ vars.put("product_sku", product.get("sku"));
false
false
- mpaf/tool/fragments/ce/graphql/remove_configurable_product_from_cart.jmx
+ mpaf/tool/fragments/ce/graphql/get_simple_product_details_by_product_url_key.jmx
- {"data":{"removeItemFromCart":{"cart":{"items":[]}}}}
+ "sku":"${product_sku}","name":"${product_name}"
Assertion.response_data
false
- 8
+ 2
+
+
+ javascript
+
+
+
+ random = vars.getObject("randomIntGenerator");
+
+var cmsPages = props.get("cms_pages");
+var number = random.nextInt(cmsPages.length);
+
+vars.put("cms_page_id", cmsPages[number].id);
+
+ mpaf/tool/fragments/ce/setup/prepare_cms_page.jmx
+
+
+
+ true
+
+
+
+ false
+
+ {"query":"query getCmsPage($id: Int!, $onServer: Boolean!) {\n cmsPage(id: $id) {\n url_key\n content\n content_heading\n title\n page_layout\n meta_title @include(if: $onServer)\n meta_keywords @include(if: $onServer)\n meta_description @include(if: $onServer)\n }\n}","variables":{"id":${cms_page_id},"onServer":false},"operationName":"getCmsPage"}
+
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/get_get_cms_page_by_id.jmx
+
+
+ $.data.cmsPage.url_key
+ ${cms_page_id}
+ false
+ false
+ false
+ false
+
+
+
@@ -43172,6 +44246,100 @@ vars.put("product_sku", product.get("sku"));
+
+
+ javascript
+
+
+
+ random = vars.getObject("randomIntGenerator");
+
+var coupons = props.get("coupon_codes");
+number = random.nextInt(coupons.length);
+
+vars.put("coupon_code", coupons[number].code);
+
+ mpaf/tool/fragments/ce/common/extract_coupon_code_setup.jmx
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n applyCouponToCart(input: {cart_id: \"${quote_id}\", coupon_code: \"${coupon_code}\"}) {\n cart {\n applied_coupon {\n code\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/apply_coupon_to_cart.jmx
+
+
+
+
+ {"data":{"applyCouponToCart":{"cart":{"applied_coupon":{"code":"${coupon_code}"}}}}}
+
+ Assertion.response_data
+ false
+ 8
+
+
+
+
+
+ true
+
+
+
+ false
+ {"query":"mutation {\n removeCouponFromCart(input: {cart_id: \"${quote_id}\"}) {\n cart {\n applied_coupon {\n code\n }\n }\n }\n}","variables":null,"operationName":null}
+ =
+
+
+
+
+ ${graphql_port_number}
+ 60000
+ 200000
+ ${request_protocol}
+
+ ${base_path}graphql
+ POST
+ true
+ false
+ true
+ false
+ false
+
+ mpaf/tool/fragments/ce/graphql/remove_coupon_from_cart.jmx
+
+
+
+
+ {"data":{"removeCouponFromCart":{"cart":{"applied_coupon":null}}}}
+
+ Assertion.response_data
+ false
+ 8
+
+
+
diff --git a/setup/performance-toolkit/profiles/ce/extra_large.xml b/setup/performance-toolkit/profiles/ce/extra_large.xml
index 390bf7fb12003..911ac7fe06d3b 100644
--- a/setup/performance-toolkit/profiles/ce/extra_large.xml
+++ b/setup/performance-toolkit/profiles/ce/extra_large.xml
@@ -39,6 +39,7 @@
20
20
2
+ 20
100
30
diff --git a/setup/performance-toolkit/profiles/ce/large.xml b/setup/performance-toolkit/profiles/ce/large.xml
index ed91b22930af5..79abab0ba4b95 100644
--- a/setup/performance-toolkit/profiles/ce/large.xml
+++ b/setup/performance-toolkit/profiles/ce/large.xml
@@ -39,6 +39,7 @@
20
20
2
+ 20
50
20
diff --git a/setup/performance-toolkit/profiles/ce/medium.xml b/setup/performance-toolkit/profiles/ce/medium.xml
index f01eabb7898f3..d02370a7770b3 100644
--- a/setup/performance-toolkit/profiles/ce/medium.xml
+++ b/setup/performance-toolkit/profiles/ce/medium.xml
@@ -39,6 +39,7 @@
20
20
2
+ 20
30
10
diff --git a/setup/performance-toolkit/profiles/ce/medium_msite.xml b/setup/performance-toolkit/profiles/ce/medium_msite.xml
index a57fcad0779fe..2f9310c5242cb 100644
--- a/setup/performance-toolkit/profiles/ce/medium_msite.xml
+++ b/setup/performance-toolkit/profiles/ce/medium_msite.xml
@@ -45,6 +45,7 @@
20
20
2
+ 20
30
10
diff --git a/setup/performance-toolkit/profiles/ce/small.xml b/setup/performance-toolkit/profiles/ce/small.xml
index 60ae901d8f5e0..cf7768328bd69 100644
--- a/setup/performance-toolkit/profiles/ce/small.xml
+++ b/setup/performance-toolkit/profiles/ce/small.xml
@@ -39,6 +39,7 @@
20
20
2
+ 20
10
5
diff --git a/setup/src/Magento/Setup/Fixtures/CouponCodesFixture.php b/setup/src/Magento/Setup/Fixtures/CouponCodesFixture.php
new file mode 100644
index 0000000000000..e60a2c9f30765
--- /dev/null
+++ b/setup/src/Magento/Setup/Fixtures/CouponCodesFixture.php
@@ -0,0 +1,168 @@
+
+ * {int}
+ *
+ * @see setup/performance-toolkit/profiles/ce/small.xml
+ */
+class CouponCodesFixture extends Fixture
+{
+ /**
+ * @var int
+ */
+ protected $priority = 129;
+
+ /**
+ * @var int
+ */
+ protected $couponCodesCount = 0;
+
+ /**
+ * @var \Magento\SalesRule\Model\RuleFactory
+ */
+ private $ruleFactory;
+
+ /**
+ * @var \Magento\SalesRule\Model\CouponFactory
+ */
+ private $couponCodeFactory;
+
+ /**
+ * Constructor
+ *
+ * @param FixtureModel $fixtureModel
+ * @param \Magento\SalesRule\Model\RuleFactory|null $ruleFactory
+ * @param \Magento\SalesRule\Model\CouponFactory|null $couponCodeFactory
+ */
+ public function __construct(
+ FixtureModel $fixtureModel,
+ \Magento\SalesRule\Model\RuleFactory $ruleFactory = null,
+ \Magento\SalesRule\Model\CouponFactory $couponCodeFactory = null
+ ) {
+ parent::__construct($fixtureModel);
+ $this->ruleFactory = $ruleFactory ?: $this->fixtureModel->getObjectManager()
+ ->get(\Magento\SalesRule\Model\RuleFactory::class);
+ $this->couponCodeFactory = $couponCodeFactory ?: $this->fixtureModel->getObjectManager()
+ ->get(\Magento\SalesRule\Model\CouponFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * @SuppressWarnings(PHPMD)
+ */
+ public function execute()
+ {
+ $this->fixtureModel->resetObjectManager();
+ $this->couponCodesCount = $this->fixtureModel->getValue('coupon_codes', 0);
+ if (!$this->couponCodesCount) {
+ return;
+ }
+
+ /** @var \Magento\Store\Model\StoreManager $storeManager */
+ $storeManager = $this->fixtureModel->getObjectManager()->create(\Magento\Store\Model\StoreManager::class);
+ /** @var $category \Magento\Catalog\Model\Category */
+ $category = $this->fixtureModel->getObjectManager()->get(\Magento\Catalog\Model\Category::class);
+
+ //Get all websites
+ $categoriesArray = [];
+ $websites = $storeManager->getWebsites();
+ foreach ($websites as $website) {
+ //Get all groups
+ $websiteGroups = $website->getGroups();
+ foreach ($websiteGroups as $websiteGroup) {
+ $websiteGroupRootCategory = $websiteGroup->getRootCategoryId();
+ $category->load($websiteGroupRootCategory);
+ $categoryResource = $category->getResource();
+ //Get all categories
+ $resultsCategories = $categoryResource->getAllChildren($category);
+ foreach ($resultsCategories as $resultsCategory) {
+ $category->load($resultsCategory);
+ $structure = explode('/', $category->getPath());
+ if (count($structure) > 2) {
+ $categoriesArray[] = [$category->getId(), $website->getId()];
+ }
+ }
+ }
+ }
+ asort($categoriesArray);
+ $categoriesArray = array_values($categoriesArray);
+
+ $this->generateCouponCodes($this->ruleFactory, $this->couponCodeFactory, $categoriesArray);
+ }
+
+ /**
+ * Generate Coupon Codes
+ *
+ * @param \Magento\SalesRule\Model\RuleFactory $ruleFactory
+ * @param \Magento\SalesRule\Model\CouponFactory $couponCodeFactory
+ * @param array $categoriesArray
+ *
+ * @return void
+ */
+ public function generateCouponCodes($ruleFactory, $couponCodeFactory, $categoriesArray)
+ {
+ for ($i = 0; $i < $this->couponCodesCount; $i++) {
+ $ruleName = sprintf('Coupon Code %1$d', $i);
+ $data = [
+ 'rule_id' => null,
+ 'name' => $ruleName,
+ 'is_active' => '1',
+ 'website_ids' => $categoriesArray[$i % count($categoriesArray)][1],
+ 'customer_group_ids' => [
+ 0 => '0',
+ 1 => '1',
+ 2 => '2',
+ 3 => '3',
+ ],
+ 'coupon_type' => \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC,
+ 'conditions' => [],
+ 'simple_action' => \Magento\SalesRule\Model\Rule::BY_PERCENT_ACTION,
+ 'discount_amount' => 5,
+ 'discount_step' => 0,
+ 'stop_rules_processing' => 1,
+ ];
+
+ $model = $ruleFactory->create();
+ $model->loadPost($data);
+ $useAutoGeneration = (int)!empty($data['use_auto_generation']);
+ $model->setUseAutoGeneration($useAutoGeneration);
+ $model->save();
+
+ $coupon = $couponCodeFactory->create();
+ $coupon->setRuleId($model->getId())
+ ->setCode('CouponCode' . $i)
+ ->setIsPrimary(true)
+ ->setType(0);
+ $coupon->save();
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getActionTitle()
+ {
+ return 'Generating coupon codes';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function introduceParamLabels()
+ {
+ return [
+ 'coupon_codes' => 'Coupon Codes'
+ ];
+ }
+}
diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/CouponCodesFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/CouponCodesFixtureTest.php
new file mode 100644
index 0000000000000..e28415961f26a
--- /dev/null
+++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/CouponCodesFixtureTest.php
@@ -0,0 +1,198 @@
+fixtureModelMock = $this->createMock(\Magento\Setup\Fixtures\FixtureModel::class);
+ $this->ruleFactoryMock = $this->createPartialMock(\Magento\SalesRule\Model\RuleFactory::class, ['create']);
+ $this->couponCodeFactoryMock = $this->createPartialMock(
+ \Magento\SalesRule\Model\CouponFactory::class,
+ ['create']
+ );
+ $this->model = new CouponCodesFixture(
+ $this->fixtureModelMock,
+ $this->ruleFactoryMock,
+ $this->couponCodeFactoryMock
+ );
+ }
+
+ /**
+ * testExecute
+ */
+ public function testExecute()
+ {
+ $storeMock = $this->createMock(\Magento\Store\Model\Store::class);
+ $storeMock->expects($this->once())
+ ->method('getRootCategoryId')
+ ->will($this->returnValue(2));
+
+ $websiteMock = $this->createMock(\Magento\Store\Model\Website::class);
+ $websiteMock->expects($this->once())
+ ->method('getGroups')
+ ->will($this->returnValue([$storeMock]));
+ $websiteMock->expects($this->once())
+ ->method('getId')
+ ->will($this->returnValue('website_id'));
+
+ $contextMock = $this->createMock(\Magento\Framework\Model\ResourceModel\Db\Context::class);
+ $abstractDbMock = $this->getMockForAbstractClass(
+ \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+ [$contextMock],
+ '',
+ true,
+ true,
+ true,
+ ['getAllChildren']
+ );
+ $abstractDbMock->expects($this->once())
+ ->method('getAllChildren')
+ ->will($this->returnValue([1]));
+
+ $storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManager::class);
+ $storeManagerMock->expects($this->once())
+ ->method('getWebsites')
+ ->will($this->returnValue([$websiteMock]));
+
+ $categoryMock = $this->createMock(\Magento\Catalog\Model\Category::class);
+ $categoryMock->expects($this->once())
+ ->method('getResource')
+ ->will($this->returnValue($abstractDbMock));
+ $categoryMock->expects($this->once())
+ ->method('getPath')
+ ->will($this->returnValue('path/to/file'));
+ $categoryMock->expects($this->once())
+ ->method('getId')
+ ->will($this->returnValue('category_id'));
+
+ $objectValueMap = [
+ [\Magento\Catalog\Model\Category::class, $categoryMock]
+ ];
+
+ $objectManagerMock = $this->createMock(\Magento\Framework\ObjectManager\ObjectManager::class);
+ $objectManagerMock->expects($this->once())
+ ->method('create')
+ ->will($this->returnValue($storeManagerMock));
+ $objectManagerMock->expects($this->once())
+ ->method('get')
+ ->will($this->returnValueMap($objectValueMap));
+
+ $valueMap = [
+ ['coupon_codes', 0, 1]
+ ];
+
+ $this->fixtureModelMock
+ ->expects($this->exactly(1))
+ ->method('getValue')
+ ->will($this->returnValueMap($valueMap));
+ $this->fixtureModelMock
+ ->expects($this->exactly(2))
+ ->method('getObjectManager')
+ ->will($this->returnValue($objectManagerMock));
+
+ $ruleMock = $this->createMock(\Magento\SalesRule\Model\Rule::class);
+ $this->ruleFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($ruleMock);
+
+ $couponMock = $this->createMock(\Magento\SalesRule\Model\Coupon::class);
+ $couponMock->expects($this->once())
+ ->method('setRuleId')
+ ->willReturnSelf();
+ $couponMock->expects($this->once())
+ ->method('setCode')
+ ->willReturnSelf();
+ $couponMock->expects($this->once())
+ ->method('setIsPrimary')
+ ->willReturnSelf();
+ $couponMock->expects($this->once())
+ ->method('setType')
+ ->willReturnSelf();
+ $couponMock->expects($this->once())
+ ->method('save')
+ ->willReturnSelf();
+ $this->couponCodeFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($couponMock);
+
+ $this->model->execute();
+ }
+
+ /**
+ * testNoFixtureConfigValue
+ */
+ public function testNoFixtureConfigValue()
+ {
+ $ruleMock = $this->createMock(\Magento\SalesRule\Model\Rule::class);
+ $ruleMock->expects($this->never())->method('save');
+
+ $objectManagerMock = $this->createMock(\Magento\Framework\ObjectManager\ObjectManager::class);
+ $objectManagerMock->expects($this->never())
+ ->method('get')
+ ->with($this->equalTo(\Magento\SalesRule\Model\Rule::class))
+ ->willReturn($ruleMock);
+
+ $this->fixtureModelMock
+ ->expects($this->never())
+ ->method('getObjectManager')
+ ->willReturn($objectManagerMock);
+ $this->fixtureModelMock
+ ->expects($this->once())
+ ->method('getValue')
+ ->willReturn(false);
+
+ $this->model->execute();
+ }
+
+ /**
+ * testGetActionTitle
+ */
+ public function testGetActionTitle()
+ {
+ $this->assertSame('Generating coupon codes', $this->model->getActionTitle());
+ }
+
+ /**
+ * testIntroduceParamLabels
+ */
+ public function testIntroduceParamLabels()
+ {
+ $this->assertSame([
+ 'coupon_codes' => 'Coupon Codes'
+ ], $this->model->introduceParamLabels());
+ }
+}