diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php
index 9890a10a4ceb0..891b2a3ada724 100644
--- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php
+++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php
@@ -282,25 +282,23 @@ public function getGridIdsJson()
if (!$this->getUseSelectAll()) {
return '';
}
- /** @var \Magento\Framework\Data\Collection $allIdsCollection */
- $allIdsCollection = clone $this->getParentBlock()->getCollection();
- if ($this->getMassactionIdField()) {
- $massActionIdField = $this->getMassactionIdField();
+ /** @var \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection $collection */
+ $collection = clone $this->getParentBlock()->getCollection();
+
+ if ($collection instanceof AbstractDb) {
+ $idsSelect = clone $collection->getSelect();
+ $idsSelect->reset(\Magento\Framework\DB\Select::ORDER);
+ $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_COUNT);
+ $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET);
+ $idsSelect->reset(\Magento\Framework\DB\Select::COLUMNS);
+ $idsSelect->columns($this->getMassactionIdField(), 'main_table');
+ $idList = $collection->getConnection()->fetchCol($idsSelect);
} else {
- $massActionIdField = $this->getParentBlock()->getMassactionIdField();
+ $idList = $collection->setPageSize(0)->getColumnValues($this->getMassactionIdField());
}
- if ($allIdsCollection instanceof AbstractDb) {
- $allIdsCollection->getSelect()->limit();
- $allIdsCollection->clear();
- }
-
- $gridIds = $allIdsCollection->setPageSize(0)->getColumnValues($massActionIdField);
- if (!empty($gridIds)) {
- return join(",", $gridIds);
- }
- return '';
+ return implode(',', $idList);
}
/**
diff --git a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php
index e8143b5f6b43a..e62b73f39241d 100644
--- a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php
+++ b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php
@@ -269,62 +269,6 @@ public function testGetGridIdsJsonWithoutUseSelectAll()
$this->assertEmpty($this->_block->getGridIdsJson());
}
- /**
- * @param array $items
- * @param string $result
- *
- * @dataProvider dataProviderGetGridIdsJsonWithUseSelectAll
- */
- public function testGetGridIdsJsonWithUseSelectAll(array $items, $result)
- {
- $this->_block->setUseSelectAll(true);
-
- if ($this->_block->getMassactionIdField()) {
- $massActionIdField = $this->_block->getMassactionIdField();
- } else {
- $massActionIdField = $this->_block->getParentBlock()->getMassactionIdField();
- }
-
- $collectionMock = $this->getMockBuilder(\Magento\Framework\Data\Collection::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->_gridMock->expects($this->once())
- ->method('getCollection')
- ->willReturn($collectionMock);
- $collectionMock->expects($this->once())
- ->method('setPageSize')
- ->with(0)
- ->willReturnSelf();
- $collectionMock->expects($this->once())
- ->method('getColumnValues')
- ->with($massActionIdField)
- ->willReturn($items);
-
- $this->assertEquals($result, $this->_block->getGridIdsJson());
- }
-
- /**
- * @return array
- */
- public function dataProviderGetGridIdsJsonWithUseSelectAll()
- {
- return [
- [
- [],
- '',
- ],
- [
- [1],
- '1',
- ],
- [
- [1, 2, 3],
- '1,2,3',
- ],
- ];
- }
-
/**
* @param string $itemId
* @param array|\Magento\Framework\DataObject $item
diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php
index b2ef78bab9909..ca563bd9d8f61 100644
--- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php
+++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php
@@ -270,7 +270,8 @@ public function getDirsCollection($path)
$collection = $this->getCollection($path)
->setCollectDirs(true)
->setCollectFiles(false)
- ->setCollectRecursively(false);
+ ->setCollectRecursively(false)
+ ->setOrder('basename', \Magento\Framework\Data\Collection\Filesystem::SORT_ORDER_ASC);
$conditions = $this->getConditionsForExcludeDirs();
diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php
index 309f08a54aab6..7bec1e3601461 100644
--- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php
@@ -417,6 +417,10 @@ protected function generalTestGetDirsCollection($path, $collectionArray = [], $e
->method('setCollectRecursively')
->with(false)
->willReturnSelf();
+ $storageCollectionMock->expects($this->once())
+ ->method('setOrder')
+ ->with('basename', \Magento\Framework\Data\Collection\Filesystem::SORT_ORDER_ASC)
+ ->willReturnSelf();
$storageCollectionMock->expects($this->once())
->method('getIterator')
->willReturn(new \ArrayIterator($collectionArray));
diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
index d0a5e8de53ae9..1fd71e446e6bb 100644
--- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
+++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
@@ -1683,14 +1683,16 @@ public function saveAttribute(DataObject $object, $attributeCode)
$connection->beginTransaction();
try {
- $select = $connection->select()->from($table, 'value_id')->where($where);
- $origValueId = $connection->fetchOne($select);
+ $select = $connection->select()->from($table, ['value_id', 'value'])->where($where);
+ $origRow = $connection->fetchRow($select);
+ $origValueId = $origRow['value_id'] ?? false;
+ $origValue = $origRow['value'] ?? null;
if ($origValueId === false && $newValue !== null) {
$this->_insertAttribute($object, $attribute, $newValue);
} elseif ($origValueId !== false && $newValue !== null) {
$this->_updateAttribute($object, $attribute, $origValueId, $newValue);
- } elseif ($origValueId !== false && $newValue === null) {
+ } elseif ($origValueId !== false && $newValue === null && $origValue !== null) {
$connection->delete($table, $where);
}
$this->_processAttributeValues();
diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php
index 0c03a9df18dc8..eeb48f805bccf 100644
--- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php
+++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php
@@ -22,13 +22,15 @@ public function build(
array $queryResult,
DataProviderInterface $dataProvider
) {
+ $buckets = $queryResult['aggregations'][$bucket->getName()]['buckets'] ?? [];
$values = [];
- foreach ($queryResult['aggregations'][$bucket->getName()]['buckets'] as $resultBucket) {
+ foreach ($buckets as $resultBucket) {
$values[$resultBucket['key']] = [
'value' => $resultBucket['key'],
'count' => $resultBucket['doc_count'],
];
}
+
return $values;
}
}
diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php
index aaa9d8a88382f..afd383c13421f 100644
--- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php
+++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php
@@ -5,6 +5,10 @@
*/
namespace Magento\Elasticsearch\SearchAdapter\Query\Builder;
+use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider;
+use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface as TypeResolver;
+use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\Search\Request\Query\BoolExpression;
use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface;
use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface;
@@ -26,20 +30,49 @@ class Match implements QueryInterface
private $fieldMapper;
/**
+ * @deprecated
+ * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer
* @var PreprocessorInterface[]
*/
protected $preprocessorContainer;
+ /**
+ * @var AttributeProvider
+ */
+ private $attributeProvider;
+
+ /**
+ * @var TypeResolver
+ */
+ private $fieldTypeResolver;
+
+ /**
+ * @var ValueTransformerPool
+ */
+ private $valueTransformerPool;
+
/**
* @param FieldMapperInterface $fieldMapper
* @param PreprocessorInterface[] $preprocessorContainer
+ * @param AttributeProvider|null $attributeProvider
+ * @param TypeResolver|null $fieldTypeResolver
+ * @param ValueTransformerPool|null $valueTransformerPool
*/
public function __construct(
FieldMapperInterface $fieldMapper,
- array $preprocessorContainer
+ array $preprocessorContainer,
+ AttributeProvider $attributeProvider = null,
+ TypeResolver $fieldTypeResolver = null,
+ ValueTransformerPool $valueTransformerPool = null
) {
$this->fieldMapper = $fieldMapper;
$this->preprocessorContainer = $preprocessorContainer;
+ $this->attributeProvider = $attributeProvider ?? ObjectManager::getInstance()
+ ->get(AttributeProvider::class);
+ $this->fieldTypeResolver = $fieldTypeResolver ?? ObjectManager::getInstance()
+ ->get(TypeResolver::class);
+ $this->valueTransformerPool = $valueTransformerPool ?? ObjectManager::getInstance()
+ ->get(ValueTransformerPool::class);
}
/**
@@ -72,10 +105,6 @@ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $
*/
protected function prepareQuery($queryValue, $conditionType)
{
- $queryValue = $this->escape($queryValue);
- foreach ($this->preprocessorContainer as $preprocessor) {
- $queryValue = $preprocessor->process($queryValue);
- }
$condition = $conditionType === BoolExpression::QUERY_CONDITION_NOT ?
self::QUERY_CONDITION_MUST_NOT : $conditionType;
return [
@@ -104,10 +133,24 @@ protected function buildQueries(array $matches, array $queryValue)
// Checking for quoted phrase \"phrase test\", trim escaped surrounding quotes if found
$count = 0;
- $value = preg_replace('#^\\\\"(.*)\\\\"$#m', '$1', $queryValue['value'], -1, $count);
+ $value = preg_replace('#^"(.*)"$#m', '$1', $queryValue['value'], -1, $count);
$condition = ($count) ? 'match_phrase' : 'match';
+ $transformedTypes = [];
foreach ($matches as $match) {
+ $attributeAdapter = $this->attributeProvider->getByAttributeCode($match['field']);
+ $fieldType = $this->fieldTypeResolver->getFieldType($attributeAdapter);
+ $valueTransformer = $this->valueTransformerPool->get($fieldType ?? 'text');
+ $valueTransformerHash = \spl_object_hash($valueTransformer);
+ if (!isset($transformedTypes[$valueTransformerHash])) {
+ $transformedTypes[$valueTransformerHash] = $valueTransformer->transform($value);
+ }
+ $transformedValue = $transformedTypes[$valueTransformerHash];
+ if (null === $transformedValue) {
+ //Value is incompatible with this field type.
+ continue;
+ }
+
$resolvedField = $this->fieldMapper->getFieldName(
$match['field'],
['type' => FieldMapperInterface::TYPE_QUERY]
@@ -117,8 +160,8 @@ protected function buildQueries(array $matches, array $queryValue)
'body' => [
$condition => [
$resolvedField => [
- 'query' => $value,
- 'boost' => isset($match['boost']) ? $match['boost'] : 1,
+ 'query' => $transformedValue,
+ 'boost' => $match['boost'] ?? 1,
],
],
],
@@ -131,16 +174,13 @@ protected function buildQueries(array $matches, array $queryValue)
/**
* Escape a value for special query characters such as ':', '(', ')', '*', '?', etc.
*
- * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error.
- * https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs.
- *
+ * @deprecated
+ * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer
* @param string $value
* @return string
*/
protected function escape($value)
{
- $value = preg_replace('/@+|[@+-]+$/', '', $value);
-
$pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/';
$replace = '\\\$1';
diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php
new file mode 100644
index 0000000000000..49eca6e9d82a6
--- /dev/null
+++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php
@@ -0,0 +1,44 @@
+dateFieldType = $dateFieldType;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function transform(string $value): ?string
+ {
+ try {
+ $formattedDate = $this->dateFieldType->formatDate(null, $value);
+ } catch (\Exception $e) {
+ $formattedDate = null;
+ }
+
+ return $formattedDate;
+ }
+}
diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php
new file mode 100644
index 0000000000000..5e330076d3df7
--- /dev/null
+++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php
@@ -0,0 +1,24 @@
+preprocessors = $preprocessors;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function transform(string $value): string
+ {
+ $value = $this->escape($value);
+ foreach ($this->preprocessors as $preprocessor) {
+ $value = $preprocessor->process($value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc.
+ *
+ * @param string $value
+ * @return string
+ */
+ private function escape(string $value): string
+ {
+ $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/';
+ $replace = '\\\$1';
+
+ return preg_replace($pattern, $replace, $value);
+ }
+}
diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php
new file mode 100644
index 0000000000000..c84ddc69cc7a8
--- /dev/null
+++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php
@@ -0,0 +1,22 @@
+transformers = $valueTransformers;
+ }
+
+ /**
+ * Get value transformer related to field type.
+ *
+ * @param string $fieldType
+ * @return ValueTransformerInterface
+ */
+ public function get(string $fieldType): ValueTransformerInterface
+ {
+ return $this->transformers[$fieldType] ?? $this->transformers['default'];
+ }
+}
diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php
index 8114feb09d35d..d0ffc6debcd8a 100644
--- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php
+++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php
@@ -5,14 +5,29 @@
*/
namespace Magento\Elasticsearch\Test\Unit\SearchAdapter\Query\Builder;
+use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter;
+use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider;
+use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface as TypeResolver;
use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface;
use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder;
+use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface;
+use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool;
use Magento\Framework\Search\Request\Query\Match as MatchRequestQuery;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
-use PHPUnit_Framework_MockObject_MockObject as MockObject;
+use PHPUnit\Framework\MockObject\MockObject as MockObject;
class MatchTest extends \PHPUnit\Framework\TestCase
{
+ /**
+ * @var AttributeProvider|MockObject
+ */
+ private $attributeProvider;
+
+ /**
+ * @var TypeResolver|MockObject
+ */
+ private $fieldTypeResolver;
+
/**
* @var MatchQueryBuilder
*/
@@ -23,46 +38,63 @@ class MatchTest extends \PHPUnit\Framework\TestCase
*/
protected function setUp()
{
+ $this->attributeProvider = $this->createMock(AttributeProvider::class);
+ $this->fieldTypeResolver = $this->createMock(TypeResolver::class);
+
+ $valueTransformerPoolMock = $this->createMock(ValueTransformerPool::class);
+ $valueTransformerMock = $this->createMock(ValueTransformerInterface::class);
+ $valueTransformerPoolMock->method('get')
+ ->willReturn($valueTransformerMock);
+ $valueTransformerMock->method('transform')
+ ->willReturnArgument(0);
+
$this->matchQueryBuilder = (new ObjectManager($this))->getObject(
MatchQueryBuilder::class,
[
'fieldMapper' => $this->getFieldMapper(),
'preprocessorContainer' => [],
+ 'attributeProvider' => $this->attributeProvider,
+ 'fieldTypeResolver' => $this->fieldTypeResolver,
+ 'valueTransformerPool' => $valueTransformerPoolMock,
]
);
}
/**
* Tests that method constructs a correct select query.
- * @see MatchQueryBuilder::build
- *
- * @dataProvider queryValuesInvariantsProvider
*
- * @param string $rawQueryValue
- * @param string $errorMessage
+ * @see MatchQueryBuilder::build
*/
- public function testBuild($rawQueryValue, $errorMessage)
+ public function testBuild()
{
- $this->assertSelectQuery(
- $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'not'),
- $errorMessage
- );
- }
+ $attributeAdapter = $this->createMock(AttributeAdapter::class);
+ $this->attributeProvider->expects($this->once())
+ ->method('getByAttributeCode')
+ ->with('some_field')
+ ->willReturn($attributeAdapter);
+ $this->fieldTypeResolver->expects($this->once())
+ ->method('getFieldType')
+ ->with($attributeAdapter)
+ ->willReturn('text');
+
+ $rawQueryValue = 'query_value';
+ $selectQuery = $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'not');
- /**
- * @link https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs.
- *
- * @return array
- */
- public function queryValuesInvariantsProvider()
- {
- return [
- ['query_value', 'Select query field must match simple raw query value.'],
- ['query_value+', 'Specifying a trailing plus sign causes InnoDB to report a syntax error.'],
- ['query_value-', 'Specifying a trailing minus sign causes InnoDB to report a syntax error.'],
- ['query_@value', 'The @ symbol is reserved for use by the @distance proximity search operator.'],
- ['query_value+@', 'The @ symbol is reserved for use by the @distance proximity search operator.'],
+ $expectedSelectQuery = [
+ 'bool' => [
+ 'must_not' => [
+ [
+ 'match' => [
+ 'some_field' => [
+ 'query' => $rawQueryValue,
+ 'boost' => 43,
+ ],
+ ],
+ ],
+ ],
+ ],
];
+ $this->assertEquals($expectedSelectQuery, $selectQuery);
}
/**
@@ -76,6 +108,16 @@ public function queryValuesInvariantsProvider()
*/
public function testBuildMatchQuery($rawQueryValue, $queryValue, $match)
{
+ $attributeAdapter = $this->createMock(AttributeAdapter::class);
+ $this->attributeProvider->expects($this->once())
+ ->method('getByAttributeCode')
+ ->with('some_field')
+ ->willReturn($attributeAdapter);
+ $this->fieldTypeResolver->expects($this->once())
+ ->method('getFieldType')
+ ->with($attributeAdapter)
+ ->willReturn('text');
+
$query = $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'should');
$expectedSelectQuery = [
@@ -111,30 +153,6 @@ public function matchProvider()
];
}
- /**
- * @param array $selectQuery
- * @param string $errorMessage
- */
- private function assertSelectQuery($selectQuery, $errorMessage)
- {
- $expectedSelectQuery = [
- 'bool' => [
- 'must_not' => [
- [
- 'match' => [
- 'some_field' => [
- 'query' => 'query_value',
- 'boost' => 43,
- ],
- ],
- ],
- ],
- ],
- ];
-
- $this->assertEquals($expectedSelectQuery, $selectQuery, $errorMessage);
- }
-
/**
* Gets fieldMapper mock object.
*
diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml
index 4a354a2ea528f..9732ae8226431 100644
--- a/app/code/Magento/Elasticsearch/etc/di.xml
+++ b/app/code/Magento/Elasticsearch/etc/di.xml
@@ -540,4 +540,22 @@
+
+
+
+ - Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer
+ - Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\DateTransformer
+ - Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\FloatTransformer
+ - Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\IntegerTransformer
+
+
+
+
+
+
+ - Magento\Elasticsearch\SearchAdapter\Query\Preprocessor\Stopwords
+ - Magento\Search\Adapter\Query\Preprocessor\Synonyms
+
+
+
diff --git a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php
index 46e794a1954cf..45eee0a4001d1 100644
--- a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php
+++ b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php
@@ -87,7 +87,7 @@ private function queryByPhrase($phrase)
{
$matchQuery = $this->fullTextSelect->getMatchQuery(
['synonyms' => 'synonyms'],
- $phrase,
+ $this->escapePhrase($phrase),
Fulltext::FULLTEXT_MODE_BOOLEAN
);
$query = $this->getConnection()->select()->from(
@@ -97,6 +97,18 @@ private function queryByPhrase($phrase)
return $this->getConnection()->fetchAll($query);
}
+ /**
+ * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error.
+ *
+ * @see https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
+ * @param string $phrase
+ * @return string
+ */
+ private function escapePhrase(string $phrase): string
+ {
+ return preg_replace('/@+|[@+-]+$/', '', $phrase);
+ }
+
/**
* A private helper function to retrieve matching synonym groups per scope
*
diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php
index d8ceb16d71fdc..2ac1bdd712114 100644
--- a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php
+++ b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php
@@ -105,42 +105,18 @@ protected function doFindOneByData(array $data)
$result = null;
$requestPath = $data[UrlRewrite::REQUEST_PATH];
-
- $data[UrlRewrite::REQUEST_PATH] = [
+ $decodedRequestPath = urldecode($requestPath);
+ $data[UrlRewrite::REQUEST_PATH] = array_unique([
rtrim($requestPath, '/'),
rtrim($requestPath, '/') . '/',
- ];
+ rtrim($decodedRequestPath, '/'),
+ rtrim($decodedRequestPath, '/') . '/',
+ ]);
$resultsFromDb = $this->connection->fetchAll($this->prepareSelect($data));
-
- if (count($resultsFromDb) === 1) {
- $resultFromDb = current($resultsFromDb);
- $redirectTypes = [OptionProvider::TEMPORARY, OptionProvider::PERMANENT];
-
- // If request path matches the DB value or it's redirect - we can return result from DB
- $canReturnResultFromDb = ($resultFromDb[UrlRewrite::REQUEST_PATH] === $requestPath
- || in_array((int)$resultFromDb[UrlRewrite::REDIRECT_TYPE], $redirectTypes, true));
-
- // Otherwise return 301 redirect to request path from DB results
- $result = $canReturnResultFromDb ? $resultFromDb : [
- UrlRewrite::ENTITY_TYPE => 'custom',
- UrlRewrite::ENTITY_ID => '0',
- UrlRewrite::REQUEST_PATH => $requestPath,
- UrlRewrite::TARGET_PATH => $resultFromDb[UrlRewrite::REQUEST_PATH],
- UrlRewrite::REDIRECT_TYPE => OptionProvider::PERMANENT,
- UrlRewrite::STORE_ID => $resultFromDb[UrlRewrite::STORE_ID],
- UrlRewrite::DESCRIPTION => null,
- UrlRewrite::IS_AUTOGENERATED => '0',
- UrlRewrite::METADATA => null,
- ];
- } else {
- // If we have 2 results - return the row that matches request path
- foreach ($resultsFromDb as $resultFromDb) {
- if ($resultFromDb[UrlRewrite::REQUEST_PATH] === $requestPath) {
- $result = $resultFromDb;
- break;
- }
- }
+ if ($resultsFromDb) {
+ $urlRewrite = $this->extractMostRelevantUrlRewrite($requestPath, $resultsFromDb);
+ $result = $this->prepareUrlRewrite($requestPath, $urlRewrite);
}
return $result;
@@ -149,6 +125,75 @@ protected function doFindOneByData(array $data)
return $this->connection->fetchRow($this->prepareSelect($data));
}
+ /**
+ * Extract most relevant url rewrite from url rewrites list
+ *
+ * @param string $requestPath
+ * @param array $urlRewrites
+ * @return array|null
+ */
+ private function extractMostRelevantUrlRewrite(string $requestPath, array $urlRewrites): ?array
+ {
+ $prioritizedUrlRewrites = [];
+ foreach ($urlRewrites as $urlRewrite) {
+ switch (true) {
+ case $urlRewrite[UrlRewrite::REQUEST_PATH] === $requestPath:
+ $priority = 1;
+ break;
+ case $urlRewrite[UrlRewrite::REQUEST_PATH] === urldecode($requestPath):
+ $priority = 2;
+ break;
+ case rtrim($urlRewrite[UrlRewrite::REQUEST_PATH], '/') === rtrim($requestPath, '/'):
+ $priority = 3;
+ break;
+ case rtrim($urlRewrite[UrlRewrite::REQUEST_PATH], '/') === rtrim(urldecode($requestPath), '/'):
+ $priority = 4;
+ break;
+ default:
+ $priority = 5;
+ break;
+ }
+ $prioritizedUrlRewrites[$priority] = $urlRewrite;
+ }
+ ksort($prioritizedUrlRewrites);
+
+ return array_shift($prioritizedUrlRewrites);
+ }
+
+ /**
+ * Prepare url rewrite
+ *
+ * If request path matches the DB value or it's redirect - we can return result from DB
+ * Otherwise return 301 redirect to request path from DB results
+ *
+ * @param string $requestPath
+ * @param array $urlRewrite
+ * @return array
+ */
+ private function prepareUrlRewrite(string $requestPath, array $urlRewrite): array
+ {
+ $redirectTypes = [OptionProvider::TEMPORARY, OptionProvider::PERMANENT];
+ $canReturnResultFromDb = (
+ in_array($urlRewrite[UrlRewrite::REQUEST_PATH], [$requestPath, urldecode($requestPath)], true)
+ || in_array((int) $urlRewrite[UrlRewrite::REDIRECT_TYPE], $redirectTypes, true)
+ );
+ if (!$canReturnResultFromDb) {
+ $urlRewrite = [
+ UrlRewrite::ENTITY_TYPE => 'custom',
+ UrlRewrite::ENTITY_ID => '0',
+ UrlRewrite::REQUEST_PATH => $requestPath,
+ UrlRewrite::TARGET_PATH => $urlRewrite[UrlRewrite::REQUEST_PATH],
+ UrlRewrite::REDIRECT_TYPE => OptionProvider::PERMANENT,
+ UrlRewrite::STORE_ID => $urlRewrite[UrlRewrite::STORE_ID],
+ UrlRewrite::DESCRIPTION => null,
+ UrlRewrite::IS_AUTOGENERATED => '0',
+ UrlRewrite::METADATA => null,
+ ];
+ }
+
+ return $urlRewrite;
+ }
+
/**
* Delete old URLs from DB.
*
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php
index 8aeee9cf12494..e11c5ce5d9cf3 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php
@@ -87,41 +87,6 @@ public function testMassactionDefaultValues()
$this->assertFalse($blockEmpty->isAvailable());
}
- public function testGetJavaScript()
- {
- $this->loadLayout();
-
- $javascript = $this->_block->getJavaScript();
-
- $expectedItemFirst = '#"option_id1":{"label":"Option One",' .
- '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' .
- '"complete":"Test","id":"option_id1"}#';
- $this->assertRegExp($expectedItemFirst, $javascript);
-
- $expectedItemSecond = '#"option_id2":{"label":"Option Two",' .
- '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' .
- '"confirm":"Are you sure\?","id":"option_id2"}#';
- $this->assertRegExp($expectedItemSecond, $javascript);
- }
-
- public function testGetJavaScriptWithAddedItem()
- {
- $this->loadLayout();
-
- $input = [
- 'id' => 'option_id3',
- 'label' => 'Option Three',
- 'url' => '*/*/option3',
- 'block_name' => 'admin.test.grid.massaction.option3',
- ];
- $expected = '#"option_id3":{"id":"option_id3","label":"Option Three",' .
- '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' .
- '"block_name":"admin.test.grid.massaction.option3"}#';
-
- $this->_block->addItem($input['id'], $input);
- $this->assertRegExp($expected, $this->_block->getJavaScript());
- }
-
/**
* @param string $mageMode
* @param int $expectedCount
@@ -213,21 +178,4 @@ public function getItemsDataProvider()
]
];
}
-
- public function testGridContainsMassactionColumn()
- {
- $this->loadLayout();
- $this->_layout->getBlock('admin.test.grid')->toHtml();
-
- $gridMassactionColumn = $this->_layout->getBlock('admin.test.grid')
- ->getColumnSet()
- ->getChildBlock('massaction');
-
- $this->assertNotNull($gridMassactionColumn, 'Massaction column does not exist in the grid column set');
- $this->assertInstanceOf(
- \Magento\Backend\Block\Widget\Grid\Column::class,
- $gridMassactionColumn,
- 'Massaction column is not an instance of \Magento\Backend\Block\Widget\Column'
- );
- }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
index 7954e2c36227f..476f01eb277df 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php
@@ -12,6 +12,11 @@
class ProductTest extends TestCase
{
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
/**
* @var Product
*/
@@ -29,7 +34,8 @@ protected function setUp()
{
$this->objectManager = Bootstrap::getObjectManager();
- $this->model = $this->objectManager->get(Product::class);
+ $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
+ $this->model = $this->objectManager->create(Product::class);
}
/**
@@ -42,11 +48,29 @@ public function testGetAttributeRawValue()
$sku = 'simple';
$attribute = 'name';
- /** @var ProductRepositoryInterface $productRepository */
- $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
- $product = $productRepository->get($sku);
-
+ $product = $this->productRepository->get($sku);
$actual = $this->model->getAttributeRawValue($product->getId(), $attribute, null);
self::assertEquals($product->getName(), $actual);
}
+
+ /**
+ * @magentoAppArea adminhtml
+ * @magentoDataFixture Magento/Catalog/_files/product_special_price.php
+ * @magentoAppIsolation enabled
+ * @magentoConfigFixture default_store catalog/price/scope 1
+ */
+ public function testUpdateStoreSpecificSpecialPrice()
+ {
+ /** @var \Magento\Catalog\Model\Product $product */
+ $product = $this->productRepository->get('simple', true, 1);
+ $this->assertEquals(5.99, $product->getSpecialPrice());
+
+ $product->setSpecialPrice('');
+ $this->model->save($product);
+ $product = $this->productRepository->get('simple', false, 1, true);
+ $this->assertEmpty($product->getSpecialPrice());
+
+ $product = $this->productRepository->get('simple', false, 0, true);
+ $this->assertEquals(5.99, $product->getSpecialPrice());
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php
similarity index 84%
rename from dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php
rename to dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php
index cbc0476efd1b5..a9ab0e11312b2 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php
@@ -3,13 +3,15 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
/* Delete attribute with text_attribute code */
-$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry');
+$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', true);
+
/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
$attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- 'Magento\Catalog\Model\ResourceModel\Eav\Attribute'
+ \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class
);
$attribute->load('text_attribute', 'attribute_code');
$attribute->delete();
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php
index 168073bc6ab74..c57c7c3fd6a92 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php
@@ -5,10 +5,10 @@
*/
/** Delete all products */
-require dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php';
+include dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php';
/** Delete text attribute */
-require dirname(dirname(__DIR__)) . '/Catalog/_files/text_attribute_rollback.php';
+include dirname(dirname(__DIR__)) . '/Catalog/_files/product_text_attribute_rollback.php';
-require dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php';
+include dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php';
-require dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php';
+include dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php';
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php
index 168073bc6ab74..c57c7c3fd6a92 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php
@@ -5,10 +5,10 @@
*/
/** Delete all products */
-require dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php';
+include dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php';
/** Delete text attribute */
-require dirname(dirname(__DIR__)) . '/Catalog/_files/text_attribute_rollback.php';
+include dirname(dirname(__DIR__)) . '/Catalog/_files/product_text_attribute_rollback.php';
-require dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php';
+include dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php';
-require dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php';
+include dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php';
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php
index 6bb7d6ac568fc..a3da32e0d6c40 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php
@@ -367,4 +367,18 @@ public function dateDataProvider()
[['from' => '2000-02-01T00:00:00Z', 'to' => ''], 0],
];
}
+
+ public function filterByAttributeValuesDataProvider()
+ {
+ $variations = parent::filterByAttributeValuesDataProvider();
+
+ $variations['quick search by date'] = [
+ 'quick_search_container',
+ [
+ 'search_term' => '2000-10-30',
+ ],
+ ];
+
+ return $variations;
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php
index b09af48b5f943..f4f3337a253c0 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php
@@ -20,6 +20,10 @@
CategorySetup::class,
['resourceName' => 'catalog_setup']
);
+$productEntityTypeId = $installer->getEntityTypeId(
+ \Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE
+);
+
$selectOptions = [];
$selectAttributes = [];
foreach (range(1, 2) as $index) {
@@ -30,7 +34,7 @@
$selectAttribute->setData(
[
'attribute_code' => 'select_attribute_' . $index,
- 'entity_type_id' => $installer->getEntityTypeId('catalog_product'),
+ 'entity_type_id' => $productEntityTypeId,
'is_global' => 1,
'is_user_defined' => 1,
'frontend_input' => 'select',
@@ -56,7 +60,8 @@
);
$selectAttribute->save();
/* Assign attribute to attribute set */
- $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $selectAttribute->getId());
+ $installer->addAttributeToGroup($productEntityTypeId, 'Default', 'General', $selectAttribute->getId());
+
/** @var $selectOptions Collection */
$selectOption = Bootstrap::getObjectManager()->create(
Collection::class
@@ -65,6 +70,26 @@
$selectAttributes[$index] = $selectAttribute;
$selectOptions[$index] = $selectOption;
}
+
+$dateAttribute = Bootstrap::getObjectManager()->create(Attribute::class);
+$dateAttribute->setData(
+ [
+ 'attribute_code' => 'date_attribute',
+ 'entity_type_id' => $productEntityTypeId,
+ 'is_global' => 1,
+ 'is_filterable' => 1,
+ 'backend_type' => 'datetime',
+ 'frontend_input' => 'date',
+ 'frontend_label' => 'Test Date',
+ 'is_searchable' => 1,
+ 'is_filterable_in_search' => 1,
+ ]
+);
+$dateAttribute->save();
+/* Assign attribute to attribute set */
+$installer->addAttributeToGroup($productEntityTypeId, 'Default', 'General', $dateAttribute->getId());
+
+$productAttributeSetId = $installer->getAttributeSetId($productEntityTypeId, 'Default');
/* Create simple products per each first attribute option */
foreach ($selectOptions[1] as $option) {
/** @var $product Product */
@@ -74,7 +99,7 @@
$product->setTypeId(
Type::TYPE_SIMPLE
)->setAttributeSetId(
- $installer->getAttributeSetId('catalog_product', 'Default')
+ $productAttributeSetId
)->setWebsiteIds(
[1]
)->setName(
@@ -92,6 +117,7 @@
)->setStockData(
['use_config_manage_stock' => 1, 'qty' => 5, 'is_in_stock' => 1]
)->save();
+
Bootstrap::getObjectManager()->get(
Action::class
)->updateAttributes(
@@ -99,6 +125,7 @@
[
$selectAttributes[1]->getAttributeCode() => $option->getId(),
$selectAttributes[2]->getAttributeCode() => $selectOptions[2]->getLastItem()->getId(),
+ $dateAttribute->getAttributeCode() => '10/30/2000',
],
$product->getStoreId()
);
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php
index 18a5372d06d98..fd413726b2637 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php
@@ -13,6 +13,7 @@
$registry = Bootstrap::getObjectManager()->get(Registry::class);
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', true);
+
/** @var $productCollection \Magento\Catalog\Model\ResourceModel\Product\Collection */
$productCollection = Bootstrap::getObjectManager()
->create(Product::class)
@@ -20,17 +21,26 @@
foreach ($productCollection as $product) {
$product->delete();
}
+
/** @var $attribute Attribute */
$attribute = Bootstrap::getObjectManager()->create(
Attribute::class
);
/** @var $installer CategorySetup */
$installer = Bootstrap::getObjectManager()->create(CategorySetup::class);
+$productEntityTypeId = $installer->getEntityTypeId(
+ \Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE
+);
foreach (range(1, 2) as $index) {
- $attribute->loadByCode($installer->getEntityTypeId('catalog_product'), 'select_attribute_' . $index);
+ $attribute->loadByCode($productEntityTypeId, 'select_attribute_' . $index);
if ($attribute->getId()) {
$attribute->delete();
}
}
+$attribute->loadByCode($productEntityTypeId, 'date_attribute');
+if ($attribute->getId()) {
+ $attribute->delete();
+}
+
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php
new file mode 100644
index 0000000000000..48d3356525f49
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php
@@ -0,0 +1,105 @@
+objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+ $this->layout = $this->objectManager->create(\Magento\Framework\View\LayoutInterface::class);
+ $this->layout->getUpdate()->load('newsletter_subscriber_grid');
+ $this->layout->generateXml();
+ $this->layout->generateElements();
+ }
+
+ /**
+ * Check if mass action block exists.
+ */
+ public function testMassActionBlockExists()
+ {
+ $this->assertNotFalse(
+ $this->getMassActionBlock(),
+ 'Mass action block does not exist in the grid, or it name was changed.'
+ );
+ }
+
+ /**
+ * Check if mass action id field is correct.
+ */
+ public function testMassActionFieldIdIsCorrect()
+ {
+ $this->assertEquals(
+ 'subscriber_id',
+ $this->getMassActionBlock()->getMassactionIdField(),
+ 'Mass action id field is incorrect.'
+ );
+ }
+
+ /**
+ * Check if function returns correct result.
+ *
+ * @magentoDataFixture Magento/Newsletter/_files/subscribers.php
+ */
+ public function testMassActionBlockContainsCorrectIdList()
+ {
+ $this->assertEquals(
+ implode(',', $this->getAllSubscriberIdList()),
+ $this->getMassActionBlock()->getGridIdsJson(),
+ 'Function returns incorrect result.'
+ );
+ }
+
+ /**
+ * Retrieve mass action block.
+ *
+ * @return bool|\Magento\Backend\Block\Widget\Grid\Massaction
+ */
+ private function getMassActionBlock()
+ {
+ return $this->layout->getBlock('adminhtml.newslettrer.subscriber.grid.massaction');
+ }
+
+ /**
+ * Retrieve list of id of all subscribers.
+ *
+ * @return array
+ */
+ private function getAllSubscriberIdList()
+ {
+ /** @var \Magento\Framework\App\ResourceConnection $resourceConnection */
+ $resourceConnection = $this->objectManager->get(\Magento\Framework\App\ResourceConnection::class);
+ $select = $resourceConnection->getConnection()
+ ->select()
+ ->from($resourceConnection->getTableName('newsletter_subscriber'))
+ ->columns(['subscriber_id' => 'subscriber_id']);
+
+ return $resourceConnection->getConnection()->fetchCol($select);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php
index b9ba89ba53144..2d0020ba22680 100644
--- a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php
@@ -48,7 +48,22 @@ public static function loadByPhraseDataProvider()
['synonyms' => 'queen,monarch', 'store_id' => 1, 'website_id' => 0],
['synonyms' => 'british,english', 'store_id' => 1, 'website_id' => 0]
]
- ]
+ ],
+ [
+ 'query_value', []
+ ],
+ [
+ 'query_value+', []
+ ],
+ [
+ 'query_value-', []
+ ],
+ [
+ 'query_@value', []
+ ],
+ [
+ 'query_value+@', []
+ ],
];
}
diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php
new file mode 100644
index 0000000000000..b6055f14e79d2
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php
@@ -0,0 +1,71 @@
+urlFinder = Bootstrap::getObjectManager()->create(UrlFinderInterface::class);
+ }
+
+ /**
+ * @dataProvider findOneDataProvider
+ * @param string $requestPath
+ * @param string $targetPath
+ * @param int $redirectType
+ */
+ public function testFindOneByData(string $requestPath, string $targetPath, int $redirectType)
+ {
+ $data = [
+ UrlRewrite::REQUEST_PATH => $requestPath,
+ ];
+ $urlRewrite = $this->urlFinder->findOneByData($data);
+ $this->assertEquals($targetPath, $urlRewrite->getTargetPath());
+ $this->assertEquals($redirectType, $urlRewrite->getRedirectType());
+ }
+
+ /**
+ * @return array
+ */
+ public function findOneDataProvider(): array
+ {
+ return [
+ ['string', 'test_page1', 0],
+ ['string/', 'string', 301],
+ ['string_permanent', 'test_page1', 301],
+ ['string_permanent/', 'test_page1', 301],
+ ['string_temporary', 'test_page1', 302],
+ ['string_temporary/', 'test_page1', 302],
+ ['строка', 'test_page1', 0],
+ ['строка/', 'строка', 301],
+ [urlencode('строка'), 'test_page2', 0],
+ [urlencode('строка') . '/', urlencode('строка'), 301],
+ ['другая_строка', 'test_page1', 302],
+ ['другая_строка/', 'test_page1', 302],
+ [urlencode('другая_строка'), 'test_page1', 302],
+ [urlencode('другая_строка') . '/', 'test_page1', 302],
+ ['السلسلة', 'test_page1', 0],
+ [urlencode('السلسلة'), 'test_page1', 0],
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php
new file mode 100644
index 0000000000000..9edc6507308ee
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php
@@ -0,0 +1,42 @@
+create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewrite::class);
+foreach ($rewritesData as $rewriteData) {
+ list ($requestPath, $targetPath, $redirectType) = $rewriteData;
+ $rewrite = $objectManager->create(\Magento\UrlRewrite\Model\UrlRewrite::class);
+ $rewrite->setEntityType('custom')
+ ->setRequestPath($requestPath)
+ ->setTargetPath($targetPath)
+ ->setRedirectType($redirectType);
+ $rewriteResource->save($rewrite);
+}
diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php
new file mode 100644
index 0000000000000..a98f947d614e0
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php
@@ -0,0 +1,20 @@
+get(\Magento\Framework\Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$urlRewriteCollection = $objectManager->create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection::class);
+$collection = $urlRewriteCollection
+ ->addFieldToFilter('target_path', ['test_page1', 'test_page2'])
+ ->load()
+ ->walk('delete');
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);