first commit
This commit is contained in:
@@ -0,0 +1,547 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\Module\FacetedSearch\Tests\Adapter;
|
||||
|
||||
use Configuration;
|
||||
use Context;
|
||||
use Db;
|
||||
use Mockery;
|
||||
use Mockery\Adapter\Phpunit\MockeryTestCase;
|
||||
use PrestaShop\Module\FacetedSearch\Adapter\MySQL;
|
||||
use PrestaShop\Module\FacetedSearch\Product\Search;
|
||||
use Product;
|
||||
use stdClass;
|
||||
use StockAvailable;
|
||||
|
||||
class MySQLTest extends MockeryTestCase
|
||||
{
|
||||
private $adapter;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->adapter = new MySQL();
|
||||
|
||||
$mock = Mockery::mock(StockAvailable::class);
|
||||
$mock->shouldReceive('addSqlShopRestriction')
|
||||
->with(null, null, 'sa')
|
||||
->andReturn('');
|
||||
|
||||
StockAvailable::setStaticExpectations($mock);
|
||||
|
||||
$stdClass = new stdClass();
|
||||
$stdClass->shop = new stdClass();
|
||||
$stdClass->shop->id = 1;
|
||||
$stdClass->language = new stdClass();
|
||||
$stdClass->language->id = 2;
|
||||
$stdClass->country = new stdClass();
|
||||
$stdClass->country->id = 3;
|
||||
$stdClass->currency = new stdClass();
|
||||
$stdClass->currency->id = 4;
|
||||
|
||||
$contextMock = Mockery::mock(Context::class);
|
||||
$contextMock->shouldReceive('getContext')
|
||||
->andReturn($stdClass);
|
||||
Context::setStaticExpectations($contextMock);
|
||||
|
||||
$configurationMock = Mockery::mock(Configuration::class);
|
||||
$configurationMock->shouldReceive('get')
|
||||
->with('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST')
|
||||
->andReturn(0);
|
||||
Configuration::setStaticExpectations($configurationMock);
|
||||
}
|
||||
|
||||
public function testGetEmptyQuery()
|
||||
{
|
||||
$this->assertEquals(
|
||||
'SELECT FROM ps_product p ORDER BY p.id_product DESC LIMIT 0, 20',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider oneSelectFieldDataProvider
|
||||
*/
|
||||
public function testGetQueryWithOneSelectField($type, $expected)
|
||||
{
|
||||
$this->adapter->addSelectField($type);
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetMinMaxPriceValue()
|
||||
{
|
||||
$dbInstanceMock = Mockery::mock(Db::class)->makePartial();
|
||||
$dbInstanceMock->shouldReceive('executeS')
|
||||
->once()
|
||||
->with('SELECT psi.price_min, MIN(price_min) as min, MAX(price_max) as max FROM ps_product p INNER JOIN ps_layered_price_index psi ON (psi.id_product = p.id_product AND psi.id_shop = 1 AND psi.id_currency = 4 AND psi.id_country = 3)')
|
||||
->andReturn(
|
||||
[
|
||||
[
|
||||
'price_min' => '11',
|
||||
'min' => '11',
|
||||
'max' => '35',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$dbMock = Mockery::mock(Db::class)->makePartial();
|
||||
$dbMock->shouldReceive('getInstance')
|
||||
->andReturn($dbInstanceMock);
|
||||
|
||||
Db::setStaticExpectations($dbMock);
|
||||
$this->assertEquals(
|
||||
[11.0, 35.0],
|
||||
$this->adapter->getMinMaxPriceValue()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetMinMaxValueForWeight()
|
||||
{
|
||||
$dbInstanceMock = Mockery::mock(Db::class);
|
||||
$dbInstanceMock->shouldReceive('executeS')
|
||||
->once()
|
||||
->with('SELECT MIN(weight) as min, MAX(weight) as max FROM ps_product p')
|
||||
->andReturn(
|
||||
[
|
||||
[
|
||||
'min' => '10',
|
||||
'max' => '42',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$dbMock = Mockery::mock(Db::class);
|
||||
$dbMock->shouldReceive('getInstance')
|
||||
->andReturn($dbInstanceMock);
|
||||
|
||||
Db::setStaticExpectations($dbMock);
|
||||
$this->assertEquals(
|
||||
[10.0, 42.0],
|
||||
$this->adapter->getMinMaxValue('weight')
|
||||
);
|
||||
}
|
||||
|
||||
public function testCount()
|
||||
{
|
||||
$dbInstanceMock = Mockery::mock(Db::class);
|
||||
$dbInstanceMock->shouldReceive('executeS')
|
||||
->once()
|
||||
->with('SELECT COUNT(DISTINCT p.id_product) c FROM ps_product p')
|
||||
->andReturn(
|
||||
[
|
||||
[
|
||||
'c' => '100',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$dbMock = Mockery::mock(Db::class);
|
||||
|
||||
$dbMock->shouldReceive('getInstance')
|
||||
->andReturn($dbInstanceMock);
|
||||
|
||||
Db::setStaticExpectations($dbMock);
|
||||
$this->assertEquals(
|
||||
100,
|
||||
$this->adapter->count()
|
||||
);
|
||||
}
|
||||
|
||||
public function testValueCount()
|
||||
{
|
||||
$dbInstanceMock = Mockery::mock(Db::class);
|
||||
$dbInstanceMock->shouldReceive('executeS')
|
||||
->once()
|
||||
->with('SELECT p.weight, COUNT(DISTINCT p.id_product) c FROM ps_product p GROUP BY p.weight')
|
||||
->andReturn(
|
||||
[
|
||||
[
|
||||
'weight' => '10',
|
||||
'c' => '100',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$dbMock = Mockery::mock(Db::class);
|
||||
$dbMock->shouldReceive('getInstance')
|
||||
->andReturn($dbInstanceMock);
|
||||
|
||||
Db::setStaticExpectations($dbMock);
|
||||
$this->assertEquals(
|
||||
[
|
||||
0 => [
|
||||
'weight' => '10',
|
||||
'c' => '100',
|
||||
],
|
||||
],
|
||||
$this->adapter->valueCount('weight')
|
||||
);
|
||||
}
|
||||
|
||||
public function testValueCountWithInitialPopulation()
|
||||
{
|
||||
$this->adapter->useFiltersAsInitialPopulation();
|
||||
$dbInstanceMock = Mockery::mock(Db::class);
|
||||
$dbInstanceMock->shouldReceive('executeS')
|
||||
->once()
|
||||
->with('SELECT p.id_product, p.weight, COUNT(DISTINCT p.id_product) c FROM (SELECT p.id_product, p.id_manufacturer, SUM(sa.quantity) as quantity, p.condition, p.weight, p.price, psales.quantity as sales FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) LEFT JOIN ps_product_sale psales ON (psales.id_product = p.id_product)) p GROUP BY p.weight')
|
||||
->andReturn(
|
||||
[
|
||||
[
|
||||
'weight' => '10',
|
||||
'c' => '1000',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$dbMock = Mockery::mock(Db::class);
|
||||
$dbMock->shouldReceive('getInstance')
|
||||
->andReturn($dbInstanceMock);
|
||||
|
||||
Db::setStaticExpectations($dbMock);
|
||||
$this->assertEquals(
|
||||
[
|
||||
0 => [
|
||||
'weight' => '10',
|
||||
'c' => '1000',
|
||||
],
|
||||
],
|
||||
$this->adapter->valueCount('weight')
|
||||
);
|
||||
}
|
||||
|
||||
public function testValueCountWithInitialPopulationAndStockManagement()
|
||||
{
|
||||
$this->adapter->useFiltersAsInitialPopulation();
|
||||
$this->adapter->getInitialPopulation()->addOperationsFilter(
|
||||
Search::STOCK_MANAGEMENT_FILTER,
|
||||
[[['quantity', [0], '>=']]]
|
||||
);
|
||||
|
||||
$dbInstanceMock = Mockery::mock(Db::class);
|
||||
$dbInstanceMock->shouldReceive('executeS')
|
||||
->once()
|
||||
->with('SELECT p.id_product, p.weight, COUNT(DISTINCT p.id_product) c FROM (SELECT p.id_product, p.id_manufacturer, SUM(sa.quantity) as quantity, p.condition, p.weight, p.price, psales.quantity as sales FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) LEFT JOIN ps_product_sale psales ON (psales.id_product = p.id_product) WHERE ((sa.quantity>=0))) p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) WHERE ((sa.quantity>=0)) GROUP BY p.weight')
|
||||
->andReturn(
|
||||
[
|
||||
[
|
||||
'weight' => '10',
|
||||
'c' => '1000',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$dbMock = Mockery::mock(Db::class);
|
||||
$dbMock->shouldReceive('getInstance')
|
||||
->andReturn($dbInstanceMock);
|
||||
|
||||
Db::setStaticExpectations($dbMock);
|
||||
$this->assertEquals(
|
||||
[
|
||||
0 => [
|
||||
'weight' => '10',
|
||||
'c' => '1000',
|
||||
],
|
||||
],
|
||||
$this->adapter->valueCount('weight')
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithAllSelectField()
|
||||
{
|
||||
$this->adapter->setSelectFields(
|
||||
[
|
||||
'id_product',
|
||||
'id_product_attribute',
|
||||
'id_attribute',
|
||||
'id_attribute_group',
|
||||
'id_feature',
|
||||
'id_shop',
|
||||
'id_feature_çvalue',
|
||||
'id_category',
|
||||
'name',
|
||||
'nleft',
|
||||
'nright',
|
||||
'level_depth',
|
||||
'out_of_stock',
|
||||
'quantity',
|
||||
'price_min',
|
||||
'price_max',
|
||||
'range_start',
|
||||
'range_end',
|
||||
'id_group',
|
||||
'manufacturer_name',
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product, pa.id_product_attribute, pac.id_attribute, a.id_attribute_group, fp.id_feature, ps.id_shop, p.id_feature_çvalue, cp.id_category, pl.name, c.nleft, c.nright, c.level_depth, sa.out_of_stock, SUM(sa.quantity) as quantity, psi.price_min, psi.price_max, psi.range_start, psi.range_end, cg.id_group, m.name FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) INNER JOIN ps_attribute a ON (a.id_attribute = pac.id_attribute) INNER JOIN ps_feature_product fp ON (p.id_product = fp.id_product) INNER JOIN ps_product_shop ps ON (p.id_product = ps.id_product AND ps.id_shop = 1 AND ps.active = TRUE) INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) INNER JOIN ps_product_lang pl ON (p.id_product = pl.id_product AND pl.id_shop = 1 AND pl.id_lang = 2) INNER JOIN ps_category c ON (cp.id_category = c.id_category AND c.active=1) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) INNER JOIN ps_layered_price_index psi ON (psi.id_product = p.id_product AND psi.id_shop = 1 AND psi.id_currency = 4 AND psi.id_country = 3) LEFT JOIN ps_category_group cg ON (cg.id_category = c.id_category) INNER JOIN ps_manufacturer m ON (p.id_manufacturer = m.id_manufacturer) ORDER BY p.id_product DESC LIMIT 0, 20',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithManyFilters()
|
||||
{
|
||||
$this->adapter->setSelectFields(
|
||||
[
|
||||
'id_product',
|
||||
'out_of_stock',
|
||||
'quantity',
|
||||
'price_min',
|
||||
'price_max',
|
||||
'range_start',
|
||||
'range_end',
|
||||
]
|
||||
);
|
||||
|
||||
$this->adapter->addFilter('condition', ['new', 'used'], '=');
|
||||
$this->adapter->addFilter('weight', [10], '=');
|
||||
$this->adapter->addFilter('price_min', [10], '>=');
|
||||
$this->adapter->addFilter('price_min', [100], '<=');
|
||||
$this->adapter->addFilter('id_product', [2, 20, 200], '=');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product, sa.out_of_stock, SUM(sa.quantity) as quantity, psi.price_min, psi.price_max, psi.range_start, psi.range_end FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) INNER JOIN ps_layered_price_index psi ON (psi.id_product = p.id_product AND psi.id_shop = 1 AND psi.id_currency = 4 AND psi.id_country = 3) WHERE p.condition IN (\'new\', \'used\') AND p.weight=\'10\' AND psi.price_min>=10 AND psi.price_min<=100 AND p.id_product IN (2, 20, 200) ORDER BY p.id_product DESC LIMIT 0, 20',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getManyOperationsFilters
|
||||
*/
|
||||
public function testGetQueryWithManyOperationsFilters($fields, $operationsFilter, $expected)
|
||||
{
|
||||
$this->adapter->setSelectFields($fields);
|
||||
|
||||
$this->adapter->addOperationsFilter(
|
||||
'out_of_stock_filter',
|
||||
$operationsFilter
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithGroup()
|
||||
{
|
||||
$this->adapter->addSelectField('id_product');
|
||||
$this->adapter->addGroupBy('id_product');
|
||||
$this->adapter->addGroupBy('id_feature_value');
|
||||
$this->adapter->addGroupBy('p.something_defined_by_me');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product FROM ps_product p GROUP BY p.id_product, fp.id_feature_value, p.something_defined_by_me ORDER BY p.id_product DESC LIMIT 0, 20',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithPriceOrderFieldInDesc()
|
||||
{
|
||||
$this->adapter->addSelectField('id_product');
|
||||
$this->adapter->setOrderField('price');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product FROM ps_product p ORDER BY psi.price_max DESC, p.id_product DESC LIMIT 0, 20',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithPriceOrderFieldInAsc()
|
||||
{
|
||||
$this->adapter->addSelectField('id_product');
|
||||
$this->adapter->setOrderField('price');
|
||||
$this->adapter->setOrderDirection('asc');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product FROM ps_product p ORDER BY psi.price_min ASC, p.id_product DESC LIMIT 0, 20',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithPriceOrderFieldInAscWithInitialPopulation()
|
||||
{
|
||||
$this->adapter->addSelectField('manufacturer_name');
|
||||
$this->adapter->useFiltersAsInitialPopulation();
|
||||
$this->adapter->setOrderField('manufacturer_name');
|
||||
$this->adapter->setOrderDirection('asc');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product FROM (SELECT p.id_product, p.id_manufacturer, SUM(sa.quantity) as quantity, p.condition, p.weight, p.price, psales.quantity as sales, m.name FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) LEFT JOIN ps_product_sale psales ON (psales.id_product = p.id_product) INNER JOIN ps_manufacturer m ON (p.id_manufacturer = m.id_manufacturer)) p INNER JOIN ps_manufacturer m ON (p.id_manufacturer = m.id_manufacturer) ORDER BY m.name ASC, p.id_product DESC',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithPositionOrderFieldInAscWithInitialPopulation()
|
||||
{
|
||||
$this->adapter->addSelectField('id_product');
|
||||
$this->adapter->useFiltersAsInitialPopulation();
|
||||
$this->adapter->setOrderField('position');
|
||||
$this->adapter->setOrderDirection('desc');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product FROM (SELECT p.id_product, p.id_manufacturer, SUM(sa.quantity) as quantity, p.condition, p.weight, p.price, psales.quantity as sales, cp.position FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) LEFT JOIN ps_product_sale psales ON (psales.id_product = p.id_product) INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product)) p INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) ORDER BY p.position DESC, p.id_product DESC',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithComputeShowLastEnabled()
|
||||
{
|
||||
$configurationMock = Mockery::mock(Configuration::class);
|
||||
$configurationMock->shouldReceive('get')
|
||||
->with('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST')
|
||||
->andReturn(true);
|
||||
Configuration::setStaticExpectations($configurationMock);
|
||||
|
||||
$productMock = Mockery::namedMock(Product::class);
|
||||
$productMock->shouldReceive('isAvailableWhenOutOfStock')
|
||||
->with(2)
|
||||
->andReturn(true);
|
||||
|
||||
$this->adapter->addSelectField('id_product');
|
||||
$this->adapter->useFiltersAsInitialPopulation();
|
||||
$this->adapter->setOrderField('position');
|
||||
$this->adapter->setOrderDirection('desc');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product, sa.out_of_stock FROM (SELECT p.id_product, p.id_manufacturer, SUM(sa.quantity) as quantity, p.condition, p.weight, p.price, psales.quantity as sales, cp.position FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) LEFT JOIN ps_product_sale psales ON (psales.id_product = p.id_product) INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product)) p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) ORDER BY IFNULL(p.quantity, 0) <= 0, IFNULL(p.quantity, 0) <= 0 AND FIELD(sa.out_of_stock, 0) ASC, p.position DESC, p.id_product DESC',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetQueryWithComputeShowLastEnabledAndDenyOrderOutOfStockProducts()
|
||||
{
|
||||
$configurationMock = Mockery::mock(Configuration::class);
|
||||
$configurationMock->shouldReceive('get')
|
||||
->with('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST')
|
||||
->andReturn(true);
|
||||
Configuration::setStaticExpectations($configurationMock);
|
||||
|
||||
$productMock = Mockery::namedMock(Product::class);
|
||||
$productMock->shouldReceive('isAvailableWhenOutOfStock')
|
||||
->with(2)
|
||||
->andReturn(false);
|
||||
|
||||
$this->adapter->addSelectField('id_product');
|
||||
$this->adapter->useFiltersAsInitialPopulation();
|
||||
$this->adapter->setOrderField('position');
|
||||
$this->adapter->setOrderDirection('desc');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT p.id_product, sa.out_of_stock FROM (SELECT p.id_product, p.id_manufacturer, SUM(sa.quantity) as quantity, p.condition, p.weight, p.price, psales.quantity as sales, cp.position FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) LEFT JOIN ps_product_sale psales ON (psales.id_product = p.id_product) INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product)) p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) ORDER BY IFNULL(p.quantity, 0) <= 0, IFNULL(p.quantity, 0) <= 0 AND FIELD(sa.out_of_stock, 1) DESC, p.position DESC, p.id_product DESC',
|
||||
$this->adapter->getQuery()
|
||||
);
|
||||
}
|
||||
|
||||
public function getManyOperationsFilters()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'fields' => [
|
||||
'id_product',
|
||||
'out_of_stock',
|
||||
'quantity',
|
||||
'price_min',
|
||||
'price_max',
|
||||
'range_start',
|
||||
'range_end',
|
||||
],
|
||||
'operationsFilter' => [
|
||||
[
|
||||
['quantity', [0], '>='],
|
||||
['out_of_stock', [1, 3, 4], '='],
|
||||
],
|
||||
[
|
||||
['quantity', [0], '>'],
|
||||
['out_of_stock', [1], '='],
|
||||
],
|
||||
],
|
||||
'expected' => 'SELECT p.id_product, sa.out_of_stock, SUM(sa.quantity) as quantity, psi.price_min, psi.price_max, psi.range_start, psi.range_end FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) INNER JOIN ps_layered_price_index psi ON (psi.id_product = p.id_product AND psi.id_shop = 1 AND psi.id_currency = 4 AND psi.id_country = 3) LEFT JOIN ps_stock_available sa_1 ON (p.id_product = sa_1.id_product AND IFNULL(pac.id_product_attribute, 0) = sa_1.id_product_attribute) WHERE ((sa.quantity>=0 AND sa_1.out_of_stock IN (1, 3, 4)) OR (sa.quantity>0 AND sa_1.out_of_stock=1)) ORDER BY p.id_product DESC LIMIT 0, 20',
|
||||
],
|
||||
[
|
||||
'fields' => [
|
||||
'id_product',
|
||||
'quantity',
|
||||
],
|
||||
'operationsFilter' => [
|
||||
[
|
||||
['id_attribute', [2]],
|
||||
['id_attribute', [4]],
|
||||
],
|
||||
[
|
||||
['quantity', [0], '>'],
|
||||
['out_of_stock', [1], '='],
|
||||
],
|
||||
],
|
||||
'expected' => 'SELECT p.id_product, SUM(sa.quantity) as quantity FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) LEFT JOIN ps_product_attribute_combination pac_1 ON (pa.id_product_attribute = pac_1.id_product_attribute) LEFT JOIN ps_stock_available sa_1 ON (p.id_product = sa_1.id_product AND IFNULL(pac.id_product_attribute, 0) = sa_1.id_product_attribute) WHERE ((pac.id_attribute=2 AND pac_1.id_attribute=4) OR (sa.quantity>0 AND sa_1.out_of_stock=1)) ORDER BY p.id_product DESC LIMIT 0, 20',
|
||||
],
|
||||
[
|
||||
'fields' => [
|
||||
'id_product',
|
||||
'quantity',
|
||||
],
|
||||
'operationsFilter' => [
|
||||
[
|
||||
['id_attribute', [2]],
|
||||
['id_attribute', [4, 5, 6]],
|
||||
['id_attribute', [7, 8, 9]],
|
||||
],
|
||||
[
|
||||
['quantity', [0], '>'],
|
||||
['out_of_stock', [0], '='],
|
||||
],
|
||||
],
|
||||
'expected' => 'SELECT p.id_product, SUM(sa.quantity) as quantity FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) LEFT JOIN ps_product_attribute_combination pac_1 ON (pa.id_product_attribute = pac_1.id_product_attribute) LEFT JOIN ps_product_attribute_combination pac_2 ON (pa.id_product_attribute = pac_2.id_product_attribute) LEFT JOIN ps_stock_available sa_1 ON (p.id_product = sa_1.id_product AND IFNULL(pac.id_product_attribute, 0) = sa_1.id_product_attribute) WHERE ((pac.id_attribute=2 AND pac_1.id_attribute IN (4, 5, 6) AND pac_2.id_attribute IN (7, 8, 9)) OR (sa.quantity>0 AND sa_1.out_of_stock=0)) ORDER BY p.id_product DESC LIMIT 0, 20',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function oneSelectFieldDataProvider()
|
||||
{
|
||||
return [
|
||||
['id_product', 'SELECT p.id_product FROM ps_product p ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['id_product_attribute', 'SELECT pa.id_product_attribute FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['id_attribute', 'SELECT pac.id_attribute FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['id_attribute_group', 'SELECT a.id_attribute_group FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) INNER JOIN ps_attribute a ON (a.id_attribute = pac.id_attribute) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['id_feature', 'SELECT fp.id_feature FROM ps_product p INNER JOIN ps_feature_product fp ON (p.id_product = fp.id_product) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['id_shop', 'SELECT ps.id_shop FROM ps_product p INNER JOIN ps_product_shop ps ON (p.id_product = ps.id_product AND ps.id_shop = 1 AND ps.active = TRUE) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['id_feature_value', 'SELECT fp.id_feature_value FROM ps_product p LEFT JOIN ps_feature_product fp ON (p.id_product = fp.id_product) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['id_category', 'SELECT cp.id_category FROM ps_product p INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['position', 'SELECT cp.position FROM ps_product p INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['name', 'SELECT pl.name FROM ps_product p INNER JOIN ps_product_lang pl ON (p.id_product = pl.id_product AND pl.id_shop = 1 AND pl.id_lang = 2) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['nleft', 'SELECT c.nleft FROM ps_product p INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) INNER JOIN ps_category c ON (cp.id_category = c.id_category AND c.active=1) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['nright', 'SELECT c.nright FROM ps_product p INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) INNER JOIN ps_category c ON (cp.id_category = c.id_category AND c.active=1) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['level_depth', 'SELECT c.level_depth FROM ps_product p INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) INNER JOIN ps_category c ON (cp.id_category = c.id_category AND c.active=1) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['out_of_stock', 'SELECT sa.out_of_stock FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['quantity', 'SELECT SUM(sa.quantity) as quantity FROM ps_product p LEFT JOIN ps_product_attribute pa ON (p.id_product = pa.id_product) LEFT JOIN ps_product_attribute_combination pac ON (pa.id_product_attribute = pac.id_product_attribute) LEFT JOIN ps_stock_available sa ON (p.id_product = sa.id_product AND IFNULL(pac.id_product_attribute, 0) = sa.id_product_attribute) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['price_min', 'SELECT psi.price_min FROM ps_product p INNER JOIN ps_layered_price_index psi ON (psi.id_product = p.id_product AND psi.id_shop = 1 AND psi.id_currency = 4 AND psi.id_country = 3) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['price_max', 'SELECT psi.price_max FROM ps_product p INNER JOIN ps_layered_price_index psi ON (psi.id_product = p.id_product AND psi.id_shop = 1 AND psi.id_currency = 4 AND psi.id_country = 3) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['range_start', 'SELECT psi.range_start FROM ps_product p INNER JOIN ps_layered_price_index psi ON (psi.id_product = p.id_product AND psi.id_shop = 1 AND psi.id_currency = 4 AND psi.id_country = 3) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['range_end', 'SELECT psi.range_end FROM ps_product p INNER JOIN ps_layered_price_index psi ON (psi.id_product = p.id_product AND psi.id_shop = 1 AND psi.id_currency = 4 AND psi.id_country = 3) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['id_group', 'SELECT cg.id_group FROM ps_product p INNER JOIN ps_category_product cp ON (p.id_product = cp.id_product) INNER JOIN ps_category c ON (cp.id_category = c.id_category AND c.active=1) LEFT JOIN ps_category_group cg ON (cg.id_category = c.id_category) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
['manufacturer_name', 'SELECT m.name FROM ps_product p INNER JOIN ps_manufacturer m ON (p.id_manufacturer = m.id_manufacturer) ORDER BY p.id_product DESC LIMIT 0, 20'],
|
||||
];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,724 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\Module\FacetedSearch\Tests\Filters;
|
||||
|
||||
use Configuration;
|
||||
use Context;
|
||||
use Db;
|
||||
use Mockery;
|
||||
use Mockery\Adapter\Phpunit\MockeryTestCase;
|
||||
use PrestaShop\Module\FacetedSearch\Filters\Converter;
|
||||
use PrestaShop\Module\FacetedSearch\Filters\DataAccessor;
|
||||
use PrestaShop\Module\FacetedSearch\URLSerializer;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\Facet;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\Filter;
|
||||
use Shop;
|
||||
use stdClass;
|
||||
use Tools;
|
||||
|
||||
class ConverterTest extends MockeryTestCase
|
||||
{
|
||||
/** @var Context */
|
||||
private $contextMock;
|
||||
|
||||
/** @var Db */
|
||||
private $dbMock;
|
||||
|
||||
/** @var Block */
|
||||
private $converter;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$mock = Mockery::mock(Configuration::class);
|
||||
|
||||
$mock->shouldReceive('get')
|
||||
->andReturnUsing(function ($arg) {
|
||||
$valueMap = [
|
||||
'PS_HOME_CATEGORY' => 1,
|
||||
'PS_WEIGHT_UNIT' => 'kg',
|
||||
'PS_STOCK_MANAGEMENT' => '1',
|
||||
'PS_ORDER_OUT_OF_STOCK' => '0',
|
||||
'PS_UNIDENTIFIED_GROUP' => '1',
|
||||
'PS_LAYERED_FILTER_CATEGORY_DEPTH' => 3,
|
||||
];
|
||||
|
||||
return $valueMap[$arg];
|
||||
});
|
||||
|
||||
Configuration::setStaticExpectations($mock);
|
||||
|
||||
$this->contextMock = Mockery::mock(Context::class);
|
||||
$this->contextMock->shop = new stdClass();
|
||||
$this->contextMock->shop->id = 1;
|
||||
$this->contextMock->language = new stdClass();
|
||||
$this->contextMock->language->id = 2;
|
||||
|
||||
$this->dbMock = Mockery::mock(Db::class);
|
||||
$toolsMock = Mockery::mock(Tools::class);
|
||||
$toolsMock->shouldReceive('getValue')
|
||||
->andReturnUsing(function ($arg) {
|
||||
$valueMap = [
|
||||
'id_category' => 12,
|
||||
'id_category_layered' => 11,
|
||||
];
|
||||
|
||||
return $valueMap[$arg];
|
||||
});
|
||||
Tools::setStaticExpectations($toolsMock);
|
||||
|
||||
$this->shopMock = Mockery::mock(Shop::class);
|
||||
Shop::setStaticExpectations($this->shopMock);
|
||||
|
||||
$this->converter = new Converter(
|
||||
$this->contextMock,
|
||||
$this->dbMock,
|
||||
new URLSerializer(),
|
||||
new DataAccessor($this->dbMock)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetFacetsFromFilterBlocksWithEmptyArray()
|
||||
{
|
||||
$this->assertEquals(
|
||||
[],
|
||||
$this->converter->getFacetsFromFilterBlocks(
|
||||
[]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test different scenario for facets filter
|
||||
*
|
||||
* @dataProvider facetsProvider
|
||||
*/
|
||||
public function testGetFacetsFromFilterBlocks($filterBlocks, $expected)
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$this->converter->getFacetsFromFilterBlocks(
|
||||
[$filterBlocks]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function facetsProvider()
|
||||
{
|
||||
return [
|
||||
// Empty
|
||||
[
|
||||
[],
|
||||
[],
|
||||
],
|
||||
// Categories
|
||||
[
|
||||
[
|
||||
'type_lite' => 'category',
|
||||
'type' => 'category',
|
||||
'id_key' => 0,
|
||||
'name' => 'Categories',
|
||||
'values' => [
|
||||
7 => [
|
||||
'name' => 'Stationery',
|
||||
'nbr' => '3',
|
||||
],
|
||||
8 => [
|
||||
'name' => 'Home Accessories',
|
||||
'nbr' => '8',
|
||||
'checked' => true,
|
||||
],
|
||||
],
|
||||
'filter_show_limit' => '0',
|
||||
'filter_type' => Converter::WIDGET_TYPE_RADIO,
|
||||
],
|
||||
[
|
||||
Facet::__set_state(
|
||||
[
|
||||
'label' => 'Categories',
|
||||
'type' => 'category',
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'filter_show_limit' => 0,
|
||||
],
|
||||
'filters' => [
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Home Accessories',
|
||||
'type' => 'category',
|
||||
'active' => true,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 8,
|
||||
'value' => 8,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
1 => Filter::__set_state(
|
||||
[
|
||||
'label' => 'Stationery',
|
||||
'type' => 'category',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 3,
|
||||
'value' => 7,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
],
|
||||
'multipleSelectionAllowed' => false,
|
||||
'widgetType' => 'radio',
|
||||
]
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
// Attribute group
|
||||
[
|
||||
[
|
||||
'type_lite' => 'id_attribute_group',
|
||||
'type' => 'id_attribute_group',
|
||||
'id_key' => '2',
|
||||
'name' => 'Color',
|
||||
'is_color_group' => true,
|
||||
'values' => [
|
||||
8 => [
|
||||
'name' => 'White',
|
||||
'nbr' => '3',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
'color' => '#ffffff',
|
||||
],
|
||||
11 => [
|
||||
'name' => 'Black',
|
||||
'nbr' => '3',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
'color' => '#434A54',
|
||||
],
|
||||
12 => [
|
||||
'name' => 'Weird',
|
||||
'nbr' => '3',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
'color' => '',
|
||||
],
|
||||
],
|
||||
'filter_show_limit' => '0',
|
||||
'filter_type' => Converter::WIDGET_TYPE_DROPDOWN,
|
||||
],
|
||||
[
|
||||
Facet::__set_state(
|
||||
[
|
||||
'label' => 'Color',
|
||||
'type' => 'attribute_group',
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'filter_show_limit' => '0',
|
||||
'id_attribute_group' => '2',
|
||||
],
|
||||
'filters' => [
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'White',
|
||||
'type' => 'attribute_group',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'color' => '#ffffff',
|
||||
],
|
||||
'magnitude' => 3,
|
||||
'value' => 8,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Black',
|
||||
'type' => 'attribute_group',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'color' => '#434A54',
|
||||
],
|
||||
'magnitude' => 3,
|
||||
'value' => 11,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Weird',
|
||||
'type' => 'attribute_group',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'texture' => '/theme/12.jpg',
|
||||
],
|
||||
'magnitude' => 3,
|
||||
'value' => 12,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
],
|
||||
'multipleSelectionAllowed' => false,
|
||||
'widgetType' => 'dropdown',
|
||||
]
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
// Feature values
|
||||
[
|
||||
[
|
||||
'type_lite' => 'id_feature',
|
||||
'type' => 'id_feature',
|
||||
'id_key' => '2',
|
||||
'values' => [
|
||||
5 => [
|
||||
'nbr' => '2',
|
||||
'name' => '2',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
],
|
||||
6 => [
|
||||
'nbr' => '2',
|
||||
'name' => '1',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
],
|
||||
7 => [
|
||||
'nbr' => '2',
|
||||
'name' => '2.2',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
],
|
||||
8 => [
|
||||
'nbr' => '2',
|
||||
'name' => '2.1',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
],
|
||||
9 => [
|
||||
'nbr' => '3',
|
||||
'name' => 'Removable cover',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
],
|
||||
10 => [
|
||||
'nbr' => '3',
|
||||
'name' => '120 pages',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
],
|
||||
],
|
||||
'name' => 'Property',
|
||||
'url_name' => null,
|
||||
'meta_title' => null,
|
||||
'filter_show_limit' => '0',
|
||||
'filter_type' => '0',
|
||||
],
|
||||
[
|
||||
Facet::__set_state(
|
||||
[
|
||||
'label' => 'Property',
|
||||
'type' => 'feature',
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'filter_show_limit' => 0,
|
||||
'id_feature' => '2',
|
||||
],
|
||||
'filters' => [
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => '1',
|
||||
'type' => 'feature',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 2,
|
||||
'value' => 6,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => '2',
|
||||
'type' => 'feature',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 2,
|
||||
'value' => 5,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => '2.1',
|
||||
'type' => 'feature',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 2,
|
||||
'value' => 8,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => '2.2',
|
||||
'type' => 'feature',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 2,
|
||||
'value' => 7,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => '120 pages',
|
||||
'type' => 'feature',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 3,
|
||||
'value' => 10,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Removable cover',
|
||||
'type' => 'feature',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 3,
|
||||
'value' => 9,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
],
|
||||
'multipleSelectionAllowed' => true,
|
||||
'widgetType' => 'checkbox',
|
||||
]
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
// Quantity
|
||||
[
|
||||
[
|
||||
'type_lite' => 'quantity',
|
||||
'type' => 'quantity',
|
||||
'id_key' => 0, 'name' => 'Availability',
|
||||
'values' => [
|
||||
0 => [
|
||||
'name' => 'Not available',
|
||||
'nbr' => 0,
|
||||
],
|
||||
1 => [
|
||||
'name' => 'In stock',
|
||||
'nbr' => 11,
|
||||
],
|
||||
],
|
||||
'filter_show_limit' => '0',
|
||||
'filter_type' => '0',
|
||||
],
|
||||
[
|
||||
Facet::__set_state(
|
||||
[
|
||||
'label' => 'Availability',
|
||||
'type' => 'availability',
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'filter_show_limit' => 0,
|
||||
],
|
||||
'filters' => [
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'In stock',
|
||||
'type' => 'availability',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 11,
|
||||
'value' => 1,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Not available',
|
||||
'type' => 'availability',
|
||||
'active' => false,
|
||||
'displayed' => false,
|
||||
'properties' => [],
|
||||
'magnitude' => 0,
|
||||
'value' => 0,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
],
|
||||
'multipleSelectionAllowed' => true,
|
||||
'widgetType' => 'checkbox',
|
||||
]
|
||||
),
|
||||
],
|
||||
],
|
||||
// Manufacturer
|
||||
[
|
||||
[
|
||||
'type_lite' => 'manufacturer',
|
||||
'type' => 'manufacturer',
|
||||
'id_key' => 0, 'name' => 'Brand',
|
||||
'values' => [
|
||||
1 => [
|
||||
'name' => 'Studio Design',
|
||||
'nbr' => '7',
|
||||
],
|
||||
2 => [
|
||||
'name' => 'Graphic Corner',
|
||||
'nbr' => '3',
|
||||
],
|
||||
],
|
||||
'filter_show_limit' => '0',
|
||||
'filter_type' => '0',
|
||||
],
|
||||
[
|
||||
Facet::__set_state(
|
||||
[
|
||||
'label' => 'Brand',
|
||||
'type' => 'manufacturer',
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'filter_show_limit' => 0,
|
||||
],
|
||||
'filters' => [
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Graphic Corner',
|
||||
'type' => 'manufacturer',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 3,
|
||||
'value' => 2,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Studio Design',
|
||||
'type' => 'manufacturer',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 7,
|
||||
'value' => 1,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
],
|
||||
'multipleSelectionAllowed' => true,
|
||||
'widgetType' => 'checkbox',
|
||||
]
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
// Condition
|
||||
[
|
||||
[
|
||||
'type_lite' => 'condition',
|
||||
'type' => 'condition',
|
||||
'id_key' => 0, 'name' => 'Condition',
|
||||
'values' => [
|
||||
'new' => [
|
||||
'name' => 'New',
|
||||
'nbr' => '11',
|
||||
],
|
||||
'used' => [
|
||||
'name' => 'Used',
|
||||
'nbr' => 0,
|
||||
],
|
||||
'refurbished' => [
|
||||
'name' => 'Refurbished',
|
||||
'nbr' => 0,
|
||||
],
|
||||
],
|
||||
'filter_show_limit' => '0',
|
||||
'filter_type' => '0',
|
||||
],
|
||||
[
|
||||
Facet::__set_state(
|
||||
[
|
||||
'label' => 'Condition',
|
||||
'type' => 'condition',
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'filter_show_limit' => '0',
|
||||
],
|
||||
'filters' => [
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'New',
|
||||
'type' => 'condition',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 11,
|
||||
'value' => 'new',
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Refurbished',
|
||||
'type' => 'condition',
|
||||
'active' => false,
|
||||
'displayed' => false,
|
||||
'properties' => [],
|
||||
'magnitude' => 0,
|
||||
'value' => 'refurbished',
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => 'Used',
|
||||
'type' => 'condition',
|
||||
'active' => false,
|
||||
'displayed' => false,
|
||||
'properties' => [],
|
||||
'magnitude' => 0,
|
||||
'value' => 'used',
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
],
|
||||
'multipleSelectionAllowed' => true,
|
||||
'widgetType' => 'checkbox',
|
||||
]
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
// Price
|
||||
[
|
||||
[
|
||||
'type_lite' => 'price',
|
||||
'type' => 'price',
|
||||
'id_key' => 0, 'name' => 'Price',
|
||||
'max' => 35.0, 'min' => 11.0, 'unit' => '$',
|
||||
'specifications' => [
|
||||
'symbol' => [
|
||||
0 => '.',
|
||||
1 => ',',
|
||||
2 => ';',
|
||||
3 => '%',
|
||||
4 => '-',
|
||||
5 => '+',
|
||||
6 => 'E',
|
||||
7 => '×',
|
||||
8 => '‰',
|
||||
9 => '∞',
|
||||
10 => 'NaN',
|
||||
],
|
||||
'currencyCode' => 'USD',
|
||||
'currencySymbol' => '$',
|
||||
'positivePattern' => '¤#,##0.00',
|
||||
'negativePattern' => '-¤#,##0.00',
|
||||
'maxFractionDigits' => 2,
|
||||
'minFractionDigits' => 2,
|
||||
'groupingUsed' => true,
|
||||
'primaryGroupSize' => 3,
|
||||
'secondaryGroupSize' => 3,
|
||||
],
|
||||
'filter_show_limit' => 0,
|
||||
'filter_type' => 3, 'nbr' => '11',
|
||||
'value' => null,
|
||||
],
|
||||
[
|
||||
Facet::__set_state(
|
||||
[
|
||||
'label' => 'Price',
|
||||
'type' => 'price',
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'filter_show_limit' => 0,
|
||||
'min' => 11.0,
|
||||
'max' => 35.0,
|
||||
'unit' => '$',
|
||||
'specifications' => [
|
||||
'symbol' => [
|
||||
0 => '.',
|
||||
1 => ',',
|
||||
2 => ';',
|
||||
3 => '%',
|
||||
4 => '-',
|
||||
5 => '+',
|
||||
6 => 'E',
|
||||
7 => '×',
|
||||
8 => '‰',
|
||||
9 => '∞',
|
||||
10 => 'NaN',
|
||||
],
|
||||
'currencyCode' => 'USD',
|
||||
'currencySymbol' => '$',
|
||||
'positivePattern' => '¤#,##0.00',
|
||||
'negativePattern' => '-¤#,##0.00',
|
||||
'maxFractionDigits' => 2,
|
||||
'minFractionDigits' => 2,
|
||||
'groupingUsed' => true,
|
||||
'primaryGroupSize' => 3,
|
||||
'secondaryGroupSize' => 3,
|
||||
],
|
||||
'range' => true,
|
||||
],
|
||||
'filters' => [
|
||||
Filter::__set_state(
|
||||
[
|
||||
'label' => '',
|
||||
'type' => 'price',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [
|
||||
'symbol' => '$',
|
||||
],
|
||||
'magnitude' => 11,
|
||||
'value' => null,
|
||||
'nextEncodedFacets' => [],
|
||||
]
|
||||
),
|
||||
],
|
||||
'multipleSelectionAllowed' => false,
|
||||
'widgetType' => 'slider',
|
||||
]
|
||||
),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\Module\FacetedSearch\Tests;
|
||||
|
||||
use Context;
|
||||
use Db;
|
||||
use Mockery;
|
||||
use Mockery\Adapter\Phpunit\MockeryTestCase;
|
||||
use PrestaShop\Module\FacetedSearch\HookDispatcher;
|
||||
use Ps_Facetedsearch;
|
||||
|
||||
class HookDispatcherTest extends MockeryTestCase
|
||||
{
|
||||
private $module;
|
||||
private $dispatcher;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->module = Mockery::mock(Ps_Facetedsearch::class);
|
||||
$contextMock = Mockery::mock(Context::class);
|
||||
$dbMock = Mockery::mock(Db::class);
|
||||
$this->module->shouldReceive('getDatabase')
|
||||
->andReturn($dbMock);
|
||||
$this->module->shouldReceive('getContext')
|
||||
->andReturn($contextMock);
|
||||
|
||||
$this->dispatcher = new HookDispatcher($this->module);
|
||||
}
|
||||
|
||||
public function testGetAvailableHooks()
|
||||
{
|
||||
$this->assertCount(27, $this->dispatcher->getAvailableHooks());
|
||||
$this->assertEquals(
|
||||
[
|
||||
'actionAttributeGroupDelete',
|
||||
'actionAttributeSave',
|
||||
'displayAttributeForm',
|
||||
'actionAttributePostProcess',
|
||||
'actionAttributeGroupDelete',
|
||||
'actionAttributeGroupSave',
|
||||
'displayAttributeGroupForm',
|
||||
'displayAttributeGroupPostProcess',
|
||||
'actionCategoryAdd',
|
||||
'actionCategoryDelete',
|
||||
'actionCategoryUpdate',
|
||||
'displayLeftColumn',
|
||||
'actionFeatureSave',
|
||||
'actionFeatureDelete',
|
||||
'displayFeatureForm',
|
||||
'displayFeaturePostProcess',
|
||||
'actionFeatureFormBuilderModifier',
|
||||
'actionAfterCreateFeatureFormHandler',
|
||||
'actionAfterUpdateFeatureFormHandler',
|
||||
'actionFeatureValueSave',
|
||||
'actionFeatureValueDelete',
|
||||
'displayFeatureValueForm',
|
||||
'displayFeatureValuePostProcess',
|
||||
'actionProductSave',
|
||||
'productSearchProvider',
|
||||
'actionObjectSpecificPriceRuleUpdateBefore',
|
||||
'actionAdminSpecificPriceRuleControllerSaveAfter',
|
||||
],
|
||||
$this->dispatcher->getAvailableHooks()
|
||||
);
|
||||
}
|
||||
|
||||
public function testDispatchUnfoundHook()
|
||||
{
|
||||
$this->module->shouldReceive('renderWidget')
|
||||
->once()
|
||||
->with('ThisHookDoesNotExists', [])
|
||||
->andReturn('');
|
||||
$this->assertEquals('', $this->dispatcher->dispatch('ThisHookDoesNotExists'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\PrestaShop\Core\Product\Search;
|
||||
|
||||
interface FacetsRendererInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\PrestaShop\Core\Product\Search;
|
||||
|
||||
interface ProductSearchProviderInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\PrestaShop\Core\Module;
|
||||
|
||||
interface WidgetInterface
|
||||
{
|
||||
}
|
||||
254
modules/ps_facetedsearch/tests/php/FacetedSearch/Mock/Facet.php
Normal file
254
modules/ps_facetedsearch/tests/php/FacetedSearch/Mock/Facet.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\PrestaShop\Core\Product\Search;
|
||||
|
||||
/**
|
||||
* We call a facet a set of filters combined with logical operators.
|
||||
*/
|
||||
class Facet
|
||||
{
|
||||
/**
|
||||
* @var string the facet label
|
||||
*/
|
||||
private $label = '';
|
||||
|
||||
/**
|
||||
* @var string the facet type
|
||||
*/
|
||||
private $type = '';
|
||||
|
||||
/**
|
||||
* @var bool if true, the facet is displayed
|
||||
*/
|
||||
private $displayed = true;
|
||||
|
||||
/**
|
||||
* @var array the facet properties
|
||||
*/
|
||||
private $properties = [];
|
||||
|
||||
/**
|
||||
* @var array the facet filters
|
||||
*/
|
||||
private $filters = [];
|
||||
|
||||
/**
|
||||
* @var bool if true, allows the multiple selection
|
||||
*/
|
||||
private $multipleSelectionAllowed = true;
|
||||
|
||||
/**
|
||||
* @var string the widget type
|
||||
*/
|
||||
private $widgetType = 'radio';
|
||||
|
||||
/**
|
||||
* @return array an array representation of the facet
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'label' => $this->label,
|
||||
'displayed' => $this->displayed,
|
||||
'type' => $this->type,
|
||||
'properties' => $this->properties,
|
||||
'filters' => array_map(function (Filter $filter) {
|
||||
return $filter->toArray();
|
||||
}, $this->filters),
|
||||
'multipleSelectionAllowed' => $this->multipleSelectionAllowed,
|
||||
'widgetType' => $this->widgetType,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label the facet label
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the facet label
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type the facet type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the facet type
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name the facet property name
|
||||
* @param mixed $value the facet property value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProperty($name, $value)
|
||||
{
|
||||
$this->properties[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name the facet property name
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getProperty($name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->properties)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->properties[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Filter $filter the facet filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addFilter(Filter $filter)
|
||||
{
|
||||
$this->filters[] = $filter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array the list of facet filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isAllowed allows/disallows the multiple selection
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMultipleSelectionAllowed($isAllowed = true)
|
||||
{
|
||||
$this->multipleSelectionAllowed = $isAllowed;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool returns true if multiple selection is allowed
|
||||
*/
|
||||
public function isMultipleSelectionAllowed()
|
||||
{
|
||||
return $this->multipleSelectionAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $displayed sets the display of the facet
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDisplayed($displayed = true)
|
||||
{
|
||||
$this->displayed = $displayed;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool returns true if the facet is displayed
|
||||
*/
|
||||
public function isDisplayed()
|
||||
{
|
||||
return $this->displayed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $widgetType sets the widget type of the facet
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWidgetType($widgetType)
|
||||
{
|
||||
$this->widgetType = $widgetType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string returns the facet widget type
|
||||
*/
|
||||
public function getWidgetType()
|
||||
{
|
||||
return $this->widgetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions created for testing
|
||||
*/
|
||||
public function setProperties(array $data)
|
||||
{
|
||||
$this->properties = $data;
|
||||
}
|
||||
|
||||
public function setFilters(array $data)
|
||||
{
|
||||
$this->filters = $data;
|
||||
}
|
||||
|
||||
public static function __set_state($data)
|
||||
{
|
||||
$obj = new self();
|
||||
$obj->setLabel($data['label']);
|
||||
$obj->setDisplayed($data['displayed']);
|
||||
$obj->setType($data['type']);
|
||||
$obj->setProperties($data['properties']);
|
||||
$obj->setFilters($data['filters']);
|
||||
$obj->setMultipleSelectionAllowed($data['multipleSelectionAllowed']);
|
||||
$obj->setWidgetType($data['widgetType']);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
271
modules/ps_facetedsearch/tests/php/FacetedSearch/Mock/Filter.php
Normal file
271
modules/ps_facetedsearch/tests/php/FacetedSearch/Mock/Filter.php
Normal file
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\PrestaShop\Core\Product\Search;
|
||||
|
||||
class Filter
|
||||
{
|
||||
/**
|
||||
* @var string the filter label
|
||||
*/
|
||||
private $label = '';
|
||||
|
||||
/**
|
||||
* @var string internal type, used by query logic
|
||||
*/
|
||||
private $type = '';
|
||||
|
||||
/**
|
||||
* @var bool whether or not the filter is used in the query
|
||||
*/
|
||||
private $active = false;
|
||||
|
||||
/**
|
||||
* @var bool whether or not the filter is displayed
|
||||
*/
|
||||
private $displayed = true;
|
||||
|
||||
/**
|
||||
* @var array the filter properties
|
||||
*/
|
||||
private $properties = [];
|
||||
|
||||
/**
|
||||
* @var int the filter magnitude
|
||||
*/
|
||||
private $magnitude = 0;
|
||||
|
||||
/**
|
||||
* @var mixed the filter value
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @var array the filter next encoded facets
|
||||
*/
|
||||
private $nextEncodedFacets = [];
|
||||
|
||||
/**
|
||||
* @return array an array representation of the filter
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'label' => $this->label,
|
||||
'type' => $this->type,
|
||||
'active' => $this->active,
|
||||
'displayed' => $this->displayed,
|
||||
'properties' => $this->properties,
|
||||
'magnitude' => $this->magnitude,
|
||||
'value' => $this->value,
|
||||
'nextEncodedFacets' => $this->nextEncodedFacets,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label the filter label
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the filter label
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type the filter type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the filter type
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name the filter property name
|
||||
* @param mixed $value the filter property value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProperty($name, $value)
|
||||
{
|
||||
$this->properties[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name the filter property name
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getProperty($name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->properties)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->properties[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $magnitude the filter magnitude
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMagnitude($magnitude)
|
||||
{
|
||||
$this->magnitude = (int) $magnitude;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int the filter magnitude
|
||||
*/
|
||||
public function getMagnitude()
|
||||
{
|
||||
return $this->magnitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $active sets the activation of the filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setActive($active = true)
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool returns true if the filter is active
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $displayed sets the display of the filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDisplayed($displayed = true)
|
||||
{
|
||||
$this->displayed = $displayed;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool returns true if the filter is displayed
|
||||
*/
|
||||
public function isDisplayed()
|
||||
{
|
||||
return $this->displayed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $nextEncodedFacets
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNextEncodedFacets($nextEncodedFacets)
|
||||
{
|
||||
$this->nextEncodedFacets = $nextEncodedFacets;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getNextEncodedFacets()
|
||||
{
|
||||
return $this->nextEncodedFacets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions created for testing
|
||||
*/
|
||||
public function setProperties(array $data)
|
||||
{
|
||||
$this->properties = $data;
|
||||
}
|
||||
|
||||
public static function __set_state($data)
|
||||
{
|
||||
$obj = new self();
|
||||
$obj->setLabel($data['label']);
|
||||
$obj->setDisplayed($data['displayed']);
|
||||
$obj->setType($data['type']);
|
||||
$obj->setProperties($data['properties']);
|
||||
$obj->setActive($data['active']);
|
||||
$obj->setMagnitude($data['magnitude']);
|
||||
$obj->setValue($data['value']);
|
||||
$obj->setNextEncodedFacets($data['nextEncodedFacets']);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
129
modules/ps_facetedsearch/tests/php/FacetedSearch/MockProxy.php
Normal file
129
modules/ps_facetedsearch/tests/php/FacetedSearch/MockProxy.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
class MockProxy
|
||||
{
|
||||
protected static $mock;
|
||||
|
||||
/**
|
||||
* Set static expectations
|
||||
*
|
||||
* @param mixed $mock
|
||||
*/
|
||||
public static function setStaticExpectations($mock)
|
||||
{
|
||||
static::$mock = $mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any static calls we get are passed along to self::$mock. public static
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $args
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic($name, $args)
|
||||
{
|
||||
return call_user_func_array(
|
||||
[static::$mock, $name],
|
||||
$args
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StockAvailable extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Context extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Db extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Configuration extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Tools extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Category extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
|
||||
public $id = null;
|
||||
}
|
||||
|
||||
class Group extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Manufacturer extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Combination extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Shop extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Feature extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class FeatureValue extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
|
||||
class Module extends MockProxy
|
||||
{
|
||||
// Redeclare to use this instead MockProxy::mock
|
||||
protected static $mock;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\Module\FacetedSearch\Tests\Product;
|
||||
|
||||
use Configuration;
|
||||
use Context;
|
||||
use Db;
|
||||
use Mockery;
|
||||
use Mockery\Adapter\Phpunit\MockeryTestCase;
|
||||
use PrestaShop\Module\FacetedSearch\Filters\Converter;
|
||||
use PrestaShop\Module\FacetedSearch\Filters\DataAccessor;
|
||||
use PrestaShop\Module\FacetedSearch\Product\SearchProvider;
|
||||
use PrestaShop\Module\FacetedSearch\URLSerializer;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\Facet;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\FacetCollection;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\Filter;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchContext;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchResult;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
|
||||
use Ps_Facetedsearch;
|
||||
use Smarty;
|
||||
use Tools;
|
||||
|
||||
class SearchProviderTest extends MockeryTestCase
|
||||
{
|
||||
/**
|
||||
* @var Search
|
||||
*/
|
||||
private $provider;
|
||||
|
||||
/**
|
||||
* @var Db
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* @var Context
|
||||
*/
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* @var URLSerializer
|
||||
*/
|
||||
private $serializer;
|
||||
|
||||
/**
|
||||
* @var FacetCollection
|
||||
*/
|
||||
private $facetCollection;
|
||||
|
||||
/**
|
||||
* @var Ps_Facetedsearch
|
||||
*/
|
||||
private $module;
|
||||
|
||||
private function mockFacet($label, $data = ['filters' => []], $widgetType = 'checkbox')
|
||||
{
|
||||
$facet = Mockery::mock(Facet::class);
|
||||
$facet->shouldReceive('getLabel')
|
||||
->andReturn($label);
|
||||
|
||||
$facet->shouldReceive('toArray')
|
||||
->andReturn($data);
|
||||
|
||||
$facet->shouldReceive('getWidgetType')
|
||||
->andReturn($widgetType);
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
private function mockFilter($label, $active = false, $value = null, $properties = [])
|
||||
{
|
||||
$filter = Mockery::mock(Filter::class);
|
||||
$filter->shouldReceive('getLabel')
|
||||
->andReturn($label);
|
||||
$filter->shouldReceive('isActive')
|
||||
->andReturn($active);
|
||||
|
||||
if ($value !== null) {
|
||||
$filter->shouldReceive('getValue')
|
||||
->andReturn($value);
|
||||
}
|
||||
|
||||
$filter->shouldReceive('getProperty')
|
||||
->andReturnUsing(
|
||||
function ($arg) use ($properties) {
|
||||
return $properties[$arg];
|
||||
}
|
||||
);
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->database = Mockery::mock(Db::class);
|
||||
$this->context = Mockery::mock(Context::class);
|
||||
$this->converter = Mockery::mock(Converter::class);
|
||||
$this->serializer = Mockery::mock(URLSerializer::class);
|
||||
$this->facetCollection = Mockery::mock(FacetCollection::class);
|
||||
|
||||
$this->module = Mockery::mock(Ps_Facetedsearch::class);
|
||||
$this->module->shouldReceive('getDatabase')
|
||||
->andReturn($this->database);
|
||||
$this->module->shouldReceive('getContext')
|
||||
->andReturn($this->context);
|
||||
$this->module->shouldReceive('isAjax')
|
||||
->andReturn(true);
|
||||
|
||||
$mock = Mockery::mock(Configuration::class);
|
||||
|
||||
$mock->shouldReceive('get')
|
||||
->andReturnUsing(function ($arg) {
|
||||
$valueMap = [
|
||||
'PS_LAYERED_SHOW_QTIES' => true,
|
||||
];
|
||||
|
||||
return $valueMap[$arg];
|
||||
});
|
||||
|
||||
Configuration::setStaticExpectations($mock);
|
||||
|
||||
$toolsMock = Mockery::mock(Tools::class);
|
||||
$toolsMock->shouldReceive('getCurrentUrlProtocolPrefix')
|
||||
->andReturn('http://');
|
||||
Tools::setStaticExpectations($toolsMock);
|
||||
|
||||
$this->provider = new SearchProvider(
|
||||
$this->module,
|
||||
$this->converter,
|
||||
$this->serializer,
|
||||
new DataAccessor($this->database)
|
||||
);
|
||||
}
|
||||
|
||||
public function testRenderFacetsWithoutFacetsCollection()
|
||||
{
|
||||
$productContext = Mockery::mock(ProductSearchContext::class);
|
||||
$productSearchResult = Mockery::mock(ProductSearchResult::class);
|
||||
$productSearchResult->shouldReceive('getFacetCollection')
|
||||
->once()
|
||||
->andReturn(null);
|
||||
|
||||
$this->assertEquals(
|
||||
'',
|
||||
$this->provider->renderFacets(
|
||||
$productContext,
|
||||
$productSearchResult
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testRenderFacetsWithFacetsCollection()
|
||||
{
|
||||
$productContext = Mockery::mock(ProductSearchContext::class);
|
||||
$smarty = Mockery::mock(Smarty::class);
|
||||
$smarty->shouldReceive('assign')
|
||||
->once()
|
||||
->with(
|
||||
[
|
||||
'show_quantities' => true,
|
||||
'facets' => [
|
||||
[
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'js_enabled' => true,
|
||||
'displayedFacets' => [],
|
||||
'activeFilters' => [],
|
||||
'sort_order' => 'product.position.asc',
|
||||
'clear_all_link' => 'http://shop.prestashop.com/catalog?from=scratch',
|
||||
]
|
||||
);
|
||||
$this->context->smarty = $smarty;
|
||||
$sortOrder = Mockery::mock(SortOrder::class);
|
||||
$sortOrder->shouldReceive('toString')
|
||||
->once()
|
||||
->andReturn('product.position.asc');
|
||||
$productSearchResult = Mockery::mock(ProductSearchResult::class);
|
||||
$productSearchResult->shouldReceive('getFacetCollection')
|
||||
->once()
|
||||
->andReturn($this->facetCollection);
|
||||
$productSearchResult->shouldReceive('getCurrentSortOrder')
|
||||
->once()
|
||||
->andReturn($sortOrder);
|
||||
|
||||
$facet = $this->mockFacet('Test');
|
||||
$this->facetCollection->shouldReceive('getFacets')
|
||||
->once()
|
||||
->andReturn(
|
||||
[
|
||||
$facet,
|
||||
]
|
||||
);
|
||||
|
||||
$this->module->shouldReceive('fetch')
|
||||
->once()
|
||||
->with(
|
||||
'module:ps_facetedsearch/views/templates/front/catalog/facets.tpl'
|
||||
)
|
||||
->andReturn('');
|
||||
|
||||
$this->assertEquals(
|
||||
'',
|
||||
$this->provider->renderFacets(
|
||||
$productContext,
|
||||
$productSearchResult
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testRenderFacetsWithFacetsCollectionAndFilters()
|
||||
{
|
||||
$productContext = Mockery::mock(ProductSearchContext::class);
|
||||
$smarty = Mockery::mock(Smarty::class);
|
||||
$smarty->shouldReceive('assign')
|
||||
->once()
|
||||
->with(
|
||||
[
|
||||
'show_quantities' => true,
|
||||
'facets' => [
|
||||
[
|
||||
'displayed' => true,
|
||||
'filters' => [
|
||||
[
|
||||
'label' => 'Men',
|
||||
'type' => 'category',
|
||||
'nextEncodedFacets' => 'Categories-Men',
|
||||
'active' => false,
|
||||
'facetLabel' => 'Test',
|
||||
'nextEncodedFacetsURL' => 'http://shop.prestashop.com/catalog?from=scratch&q=Categories-Men',
|
||||
],
|
||||
[
|
||||
'label' => 'Women',
|
||||
'type' => 'category',
|
||||
'nextEncodedFacets' => '',
|
||||
'active' => true,
|
||||
'facetLabel' => 'Test',
|
||||
'nextEncodedFacetsURL' => 'http://shop.prestashop.com/catalog?from=scratch&page=1',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'displayed' => true,
|
||||
'filters' => [
|
||||
[
|
||||
'label' => '£22.00 - £35.00',
|
||||
'type' => 'price',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 2,
|
||||
'value' => 0,
|
||||
'nextEncodedFacets' => '',
|
||||
'facetLabel' => 'Price',
|
||||
'nextEncodedFacetsURL' => 'http://shop.prestashop.com/catalog?from=scratch',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'js_enabled' => true,
|
||||
'displayedFacets' => [
|
||||
[
|
||||
'displayed' => true,
|
||||
'filters' => [
|
||||
[
|
||||
'label' => 'Men',
|
||||
'type' => 'category',
|
||||
'nextEncodedFacets' => 'Categories-Men',
|
||||
'active' => false,
|
||||
'facetLabel' => 'Test',
|
||||
'nextEncodedFacetsURL' => 'http://shop.prestashop.com/catalog?from=scratch&q=Categories-Men',
|
||||
],
|
||||
[
|
||||
'label' => 'Women',
|
||||
'type' => 'category',
|
||||
'nextEncodedFacets' => '',
|
||||
'active' => true,
|
||||
'facetLabel' => 'Test',
|
||||
'nextEncodedFacetsURL' => 'http://shop.prestashop.com/catalog?from=scratch&page=1',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'displayed' => true,
|
||||
'filters' => [
|
||||
[
|
||||
'label' => '£22.00 - £35.00',
|
||||
'type' => 'price',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 2,
|
||||
'value' => 0,
|
||||
'nextEncodedFacets' => '',
|
||||
'facetLabel' => 'Price',
|
||||
'nextEncodedFacetsURL' => 'http://shop.prestashop.com/catalog?from=scratch',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'activeFilters' => [
|
||||
[
|
||||
'label' => 'Women',
|
||||
'type' => 'category',
|
||||
'nextEncodedFacets' => '',
|
||||
'active' => true,
|
||||
'facetLabel' => 'Test',
|
||||
'nextEncodedFacetsURL' => 'http://shop.prestashop.com/catalog?from=scratch&page=1',
|
||||
],
|
||||
],
|
||||
'sort_order' => 'product.position.asc',
|
||||
'clear_all_link' => 'http://shop.prestashop.com/catalog?from=scratch',
|
||||
]
|
||||
);
|
||||
$this->context->smarty = $smarty;
|
||||
$sortOrder = Mockery::mock(SortOrder::class);
|
||||
$sortOrder->shouldReceive('toString')
|
||||
->once()
|
||||
->andReturn('product.position.asc');
|
||||
$productSearchResult = Mockery::mock(ProductSearchResult::class);
|
||||
$productSearchResult->shouldReceive('getFacetCollection')
|
||||
->once()
|
||||
->andReturn($this->facetCollection);
|
||||
$productSearchResult->shouldReceive('getCurrentSortOrder')
|
||||
->once()
|
||||
->andReturn($sortOrder);
|
||||
|
||||
$facet = $this->mockFacet(
|
||||
'Test',
|
||||
[
|
||||
'displayed' => true,
|
||||
'filters' => [
|
||||
[
|
||||
'label' => 'Men',
|
||||
'type' => 'category',
|
||||
'nextEncodedFacets' => 'Categories-Men',
|
||||
'active' => false,
|
||||
],
|
||||
[
|
||||
'label' => 'Women',
|
||||
'type' => 'category',
|
||||
'nextEncodedFacets' => '',
|
||||
'active' => true,
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
$facetSlider = $this->mockFacet(
|
||||
'Price',
|
||||
[
|
||||
'displayed' => true,
|
||||
'filters' => [
|
||||
[
|
||||
'label' => '£22.00 - £35.00',
|
||||
'type' => 'price',
|
||||
'active' => false,
|
||||
'displayed' => true,
|
||||
'properties' => [],
|
||||
'magnitude' => 2,
|
||||
'value' => 0,
|
||||
'nextEncodedFacets' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
'slider'
|
||||
);
|
||||
$this->facetCollection->shouldReceive('getFacets')
|
||||
->once()
|
||||
->andReturn(
|
||||
[
|
||||
$facet,
|
||||
$facetSlider,
|
||||
]
|
||||
);
|
||||
|
||||
$this->module->shouldReceive('fetch')
|
||||
->once()
|
||||
->with(
|
||||
'module:ps_facetedsearch/views/templates/front/catalog/facets.tpl'
|
||||
)
|
||||
->andReturn('');
|
||||
|
||||
$this->assertEquals(
|
||||
'',
|
||||
$this->provider->renderFacets(
|
||||
$productContext,
|
||||
$productSearchResult
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\Module\FacetedSearch\Tests\Product;
|
||||
|
||||
use Configuration;
|
||||
use Context;
|
||||
use Mockery;
|
||||
use Mockery\Adapter\Phpunit\MockeryTestCase;
|
||||
use PrestaShop\Module\FacetedSearch\Adapter\MySQL;
|
||||
use PrestaShop\Module\FacetedSearch\Product\Search;
|
||||
use stdClass;
|
||||
use Tools;
|
||||
|
||||
class SearchTest extends MockeryTestCase
|
||||
{
|
||||
/**
|
||||
* @var Search
|
||||
*/
|
||||
private $search;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$mock = Mockery::mock(Configuration::class);
|
||||
$mock->shouldReceive('get')
|
||||
->andReturnUsing(function ($arg) {
|
||||
$valueMap = [
|
||||
'PS_STOCK_MANAGEMENT' => true,
|
||||
'PS_ORDER_OUT_OF_STOCK' => true,
|
||||
'PS_HOME_CATEGORY' => true,
|
||||
'PS_LAYERED_FULL_TREE' => false,
|
||||
'PS_LAYERED_FILTER_BY_DEFAULT_CATEGORY' => true,
|
||||
];
|
||||
|
||||
return $valueMap[$arg];
|
||||
});
|
||||
|
||||
Configuration::setStaticExpectations($mock);
|
||||
|
||||
$contextMock = Mockery::mock(Context::class);
|
||||
$contextMock->shop = new stdClass();
|
||||
$contextMock->shop->id = 1;
|
||||
|
||||
Context::setStaticExpectations($contextMock);
|
||||
|
||||
$this->search = new Search($contextMock);
|
||||
}
|
||||
|
||||
public function testGetFacetedSearchTypeAdapter()
|
||||
{
|
||||
$this->assertInstanceOf(
|
||||
MySQL::class,
|
||||
$this->search->getSearchAdapter()
|
||||
);
|
||||
}
|
||||
|
||||
public function testInitSearchWithEmptyFilters()
|
||||
{
|
||||
$toolsMock = Mockery::mock(Tools::class);
|
||||
$toolsMock->shouldReceive('getValue')
|
||||
->andReturnUsing(function ($arg) {
|
||||
$valueMap = [
|
||||
'id_category' => 12,
|
||||
'id_category_layered' => 11,
|
||||
];
|
||||
|
||||
return $valueMap[$arg];
|
||||
});
|
||||
|
||||
Tools::setStaticExpectations($toolsMock);
|
||||
|
||||
$this->search->initSearch([]);
|
||||
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getFilters()->toArray());
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getOperationsFilters()->toArray());
|
||||
$this->assertEquals(
|
||||
[
|
||||
'id_category_default' => [
|
||||
'=' => [
|
||||
[
|
||||
null,
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_category' => [
|
||||
'=' => [
|
||||
[
|
||||
null,
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_shop' => [
|
||||
'=' => [
|
||||
[
|
||||
1,
|
||||
],
|
||||
],
|
||||
],
|
||||
'visibility' => [
|
||||
'=' => [
|
||||
[
|
||||
'both',
|
||||
'catalog',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->search->getSearchAdapter()->getInitialPopulation()->getFilters()->toArray()
|
||||
);
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getInitialPopulation()->getOperationsFilters()->toArray());
|
||||
}
|
||||
|
||||
public function testInitSearchWithAllFilters()
|
||||
{
|
||||
$toolsMock = Mockery::mock(Tools::class);
|
||||
$toolsMock->shouldReceive('getValue')
|
||||
->andReturnUsing(function ($arg) {
|
||||
$valueMap = [
|
||||
'id_category' => 12,
|
||||
'id_category_layered' => 11,
|
||||
];
|
||||
|
||||
return $valueMap[$arg];
|
||||
});
|
||||
|
||||
Tools::setStaticExpectations($toolsMock);
|
||||
|
||||
$this->search->initSearch(
|
||||
[
|
||||
'id_feature' => [
|
||||
[1, 2],
|
||||
],
|
||||
'id_attribute_group' => [
|
||||
[4, 5],
|
||||
],
|
||||
'category' => [
|
||||
[6],
|
||||
],
|
||||
'quantity' => [
|
||||
0,
|
||||
],
|
||||
'weight' => [
|
||||
'10',
|
||||
'40',
|
||||
],
|
||||
'price' => [
|
||||
'50',
|
||||
'200',
|
||||
],
|
||||
'manufacturer' => [
|
||||
'10',
|
||||
],
|
||||
'condition' => [
|
||||
'1',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getFilters()->toArray());
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getOperationsFilters()->toArray());
|
||||
$this->assertEquals(
|
||||
[
|
||||
'weight' => [
|
||||
'>=' => [
|
||||
[
|
||||
10.0,
|
||||
],
|
||||
],
|
||||
'<=' => [
|
||||
[
|
||||
40.0,
|
||||
],
|
||||
],
|
||||
],
|
||||
'price_min' => [
|
||||
'<=' => [
|
||||
[
|
||||
200.0,
|
||||
],
|
||||
],
|
||||
],
|
||||
'price_max' => [
|
||||
'>=' => [
|
||||
[
|
||||
50.0,
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_manufacturer' => [
|
||||
'=' => [
|
||||
[
|
||||
'10',
|
||||
],
|
||||
],
|
||||
],
|
||||
'condition' => [
|
||||
'=' => [
|
||||
[
|
||||
'1',
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_shop' => [
|
||||
'=' => [
|
||||
[
|
||||
1,
|
||||
],
|
||||
],
|
||||
],
|
||||
'visibility' => [
|
||||
'=' => [
|
||||
[
|
||||
'both',
|
||||
'catalog',
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_category' => [
|
||||
'=' => [
|
||||
[
|
||||
null,
|
||||
],
|
||||
[
|
||||
6,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->search->getSearchAdapter()->getInitialPopulation()->getFilters()->toArray()
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'with_stock_management' => [
|
||||
[
|
||||
[
|
||||
'quantity',
|
||||
[
|
||||
0,
|
||||
],
|
||||
'<=',
|
||||
],
|
||||
[
|
||||
'out_of_stock',
|
||||
[
|
||||
0,
|
||||
],
|
||||
'=',
|
||||
],
|
||||
],
|
||||
],
|
||||
'with_attributes_0' => [
|
||||
[
|
||||
[
|
||||
'id_attribute',
|
||||
[
|
||||
4,
|
||||
5,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'with_features_0' => [
|
||||
[
|
||||
[
|
||||
'id_feature_value',
|
||||
[
|
||||
1,
|
||||
2,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->search->getSearchAdapter()->getInitialPopulation()->getOperationsFilters()->toArray()
|
||||
);
|
||||
}
|
||||
|
||||
public function testInitSearchWithManyFeatures()
|
||||
{
|
||||
$toolsMock = Mockery::mock(Tools::class);
|
||||
$toolsMock->shouldReceive('getValue')
|
||||
->andReturnUsing(function ($arg) {
|
||||
$valueMap = [
|
||||
'id_category' => 12,
|
||||
'id_category_layered' => 11,
|
||||
];
|
||||
|
||||
return $valueMap[$arg];
|
||||
});
|
||||
|
||||
Tools::setStaticExpectations($toolsMock);
|
||||
|
||||
$this->search->initSearch(
|
||||
[
|
||||
'id_feature' => [
|
||||
[1],
|
||||
[2, 3, 4],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getFilters()->toArray());
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getOperationsFilters()->toArray());
|
||||
$this->assertEquals(
|
||||
[
|
||||
'id_shop' => [
|
||||
'=' => [
|
||||
[
|
||||
1,
|
||||
],
|
||||
],
|
||||
],
|
||||
'visibility' => [
|
||||
'=' => [
|
||||
[
|
||||
'both',
|
||||
'catalog',
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_category_default' => [
|
||||
'=' => [
|
||||
[
|
||||
null,
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_category' => [
|
||||
'=' => [
|
||||
[
|
||||
null,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->search->getSearchAdapter()->getInitialPopulation()->getFilters()->toArray()
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'with_features_0' => [
|
||||
[
|
||||
[
|
||||
'id_feature_value',
|
||||
[
|
||||
1,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'with_features_1' => [
|
||||
[
|
||||
[
|
||||
'id_feature_value',
|
||||
[
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->search->getSearchAdapter()->getInitialPopulation()->getOperationsFilters()->toArray()
|
||||
);
|
||||
}
|
||||
|
||||
public function testInitSearchWithManyAttributes()
|
||||
{
|
||||
$toolsMock = Mockery::mock(Tools::class);
|
||||
$toolsMock->shouldReceive('getValue')
|
||||
->andReturnUsing(function ($arg) {
|
||||
$valueMap = [
|
||||
'id_category' => 12,
|
||||
'id_category_layered' => 11,
|
||||
];
|
||||
|
||||
return $valueMap[$arg];
|
||||
});
|
||||
|
||||
Tools::setStaticExpectations($toolsMock);
|
||||
|
||||
$this->search->initSearch(
|
||||
[
|
||||
'id_attribute_group' => [
|
||||
[1],
|
||||
[2, 3, 4],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getFilters()->toArray());
|
||||
$this->assertEquals([], $this->search->getSearchAdapter()->getOperationsFilters()->toArray());
|
||||
$this->assertEquals(
|
||||
[
|
||||
'id_shop' => [
|
||||
'=' => [
|
||||
[
|
||||
1,
|
||||
],
|
||||
],
|
||||
],
|
||||
'visibility' => [
|
||||
'=' => [
|
||||
[
|
||||
'both',
|
||||
'catalog',
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_category_default' => [
|
||||
'=' => [
|
||||
[
|
||||
null,
|
||||
],
|
||||
],
|
||||
],
|
||||
'id_category' => [
|
||||
'=' => [
|
||||
[
|
||||
null,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->search->getSearchAdapter()->getInitialPopulation()->getFilters()->toArray()
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'with_attributes_0' => [
|
||||
[
|
||||
[
|
||||
'id_attribute',
|
||||
[
|
||||
1,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'with_attributes_1' => [
|
||||
[
|
||||
[
|
||||
'id_attribute',
|
||||
[
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->search->getSearchAdapter()->getInitialPopulation()->getOperationsFilters()->toArray()
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddFilter()
|
||||
{
|
||||
$this->search->addFilter('weight', [10, 20]);
|
||||
$this->search->addFilter('id_feature', [[10, 20]]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/AFL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
*/
|
||||
|
||||
namespace PrestaShop\Module\FacetedSearch\Tests;
|
||||
|
||||
use Mockery;
|
||||
use Mockery\Adapter\Phpunit\MockeryTestCase;
|
||||
use PrestaShop\Module\FacetedSearch\URLSerializer;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\Facet;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\Filter;
|
||||
|
||||
class URLSerializerTest extends MockeryTestCase
|
||||
{
|
||||
private $serializer;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->serializer = new URLSerializer();
|
||||
}
|
||||
|
||||
private function mockFacet($label, $properties = [])
|
||||
{
|
||||
$facet = Mockery::mock(Facet::class);
|
||||
$facet->shouldReceive('getLabel')
|
||||
->andReturn($label);
|
||||
|
||||
$facet->shouldReceive('getProperty')
|
||||
->andReturnUsing(
|
||||
function ($arg) use ($properties) {
|
||||
return isset($properties[$arg]) ? $properties[$arg] : null;
|
||||
}
|
||||
);
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
private function mockFilter($label, $active = false, $value = null, $properties = [])
|
||||
{
|
||||
$filter = Mockery::mock(Filter::class);
|
||||
$filter->shouldReceive('getLabel')
|
||||
->andReturn($label);
|
||||
$filter->shouldReceive('isActive')
|
||||
->andReturn($active);
|
||||
|
||||
if ($value !== null) {
|
||||
$filter->shouldReceive('getValue')
|
||||
->andReturn($value);
|
||||
}
|
||||
|
||||
$filter->shouldReceive('getProperty')
|
||||
->andReturnUsing(
|
||||
function ($arg) use ($properties) {
|
||||
return isset($properties[$arg]) ? $properties[$arg] : null;
|
||||
}
|
||||
);
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
public function testGetActiveFilters()
|
||||
{
|
||||
$first = $this->mockFilter('Tops', true);
|
||||
$second = $this->mockFilter('Robes', false);
|
||||
|
||||
$facet = $this->mockFacet('Categories', ['range' => false]);
|
||||
$facet->shouldReceive('getFilters')
|
||||
->andReturn([$first, $second]);
|
||||
|
||||
$this->assertEquals(
|
||||
['Categories' => ['Tops' => 'Tops']],
|
||||
$this->serializer->getActiveFacetFiltersFromFacets([$facet])
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetActiveFiltersWithRange()
|
||||
{
|
||||
$filter = $this->mockFilter('filter', true, [0, 100], ['symbol' => '$']);
|
||||
$facet = $this->mockFacet('Price', ['range' => true]);
|
||||
$facet->shouldReceive('getFilters')
|
||||
->andReturn([$filter]);
|
||||
|
||||
$this->assertEquals(
|
||||
['Price' => ['$', 0, 100]],
|
||||
$this->serializer->getActiveFacetFiltersFromFacets([$facet])
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAndRemoveFiltersWithoutRange()
|
||||
{
|
||||
$filter = $this->mockFilter('Tops');
|
||||
$facet = $this->mockFacet('Categories', ['range' => false]);
|
||||
$facetsFilters = $this->serializer->addFilterToFacetFilters(
|
||||
[],
|
||||
$filter,
|
||||
$facet
|
||||
);
|
||||
$this->assertEquals(
|
||||
['Categories' => ['Tops' => 'Tops']],
|
||||
$facetsFilters
|
||||
);
|
||||
$facetsFilters = $this->serializer->removeFilterFromFacetFilters(
|
||||
$facetsFilters,
|
||||
$filter,
|
||||
$facet
|
||||
);
|
||||
$this->assertEquals(
|
||||
[],
|
||||
$facetsFilters
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAndRemoveFiltersWithRangeAndMinMax()
|
||||
{
|
||||
$filter = $this->mockFilter(
|
||||
'filter',
|
||||
true,
|
||||
[0, 100],
|
||||
['symbol' => '$']
|
||||
);
|
||||
$facet = $this->mockFacet(
|
||||
'Price',
|
||||
[
|
||||
'range' => true,
|
||||
'values' => [],
|
||||
'min' => 0,
|
||||
'max' => 200,
|
||||
]
|
||||
);
|
||||
$facetsFilters = $this->serializer->addFilterToFacetFilters(
|
||||
[],
|
||||
$filter,
|
||||
$facet
|
||||
);
|
||||
$this->assertEquals(
|
||||
['Price' => ['$', 0, 200]],
|
||||
$facetsFilters
|
||||
);
|
||||
$facetsFilters = $this->serializer->removeFilterFromFacetFilters(
|
||||
$facetsFilters,
|
||||
$filter,
|
||||
$facet
|
||||
);
|
||||
$this->assertEquals(
|
||||
[],
|
||||
$facetsFilters
|
||||
);
|
||||
}
|
||||
|
||||
public function testAddAndRemoveFiltersWithRange()
|
||||
{
|
||||
$filter = $this->mockFilter(
|
||||
'filter',
|
||||
true,
|
||||
[0, 100],
|
||||
['symbol' => '$']
|
||||
);
|
||||
$facet = $this->mockFacet(
|
||||
'Price',
|
||||
[
|
||||
'range' => true,
|
||||
'values' => [10, 100],
|
||||
]
|
||||
);
|
||||
$facetsFilters = $this->serializer->addFilterToFacetFilters(
|
||||
[],
|
||||
$filter,
|
||||
$facet
|
||||
);
|
||||
$this->assertEquals(
|
||||
['Price' => ['$', 10, 100]],
|
||||
$facetsFilters
|
||||
);
|
||||
$facetsFilters = $this->serializer->removeFilterFromFacetFilters(
|
||||
$facetsFilters,
|
||||
$filter,
|
||||
$facet
|
||||
);
|
||||
$this->assertEquals(
|
||||
[],
|
||||
$facetsFilters
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getSerializerData
|
||||
*/
|
||||
public function testSerializeUnserialize($expected, array $fragment)
|
||||
{
|
||||
$this->assertEquals($expected, $this->serializer->serialize($fragment));
|
||||
$this->assertEquals($fragment, $this->serializer->unserialize($expected));
|
||||
}
|
||||
|
||||
public function getSerializerData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'a-b',
|
||||
['a' => ['b']],
|
||||
],
|
||||
[
|
||||
'a-b-c',
|
||||
['a' => ['b', 'c']],
|
||||
],
|
||||
[
|
||||
'a-b-c/x-y-z',
|
||||
['a' => ['b', 'c'], 'x' => ['y', 'z']],
|
||||
],
|
||||
[
|
||||
'a-b\-c',
|
||||
['a' => ['b-c']],
|
||||
],
|
||||
[
|
||||
'Category-Men \/ Women \ \-Children-Clothes/Size-\-1-2',
|
||||
['Category' => ['Men / Women \ -Children', 'Clothes'], 'Size' => ['-1', '2']],
|
||||
],
|
||||
[
|
||||
'Category-\-\-\--\-2/\-Size\--\-1-\-2\-',
|
||||
['Category' => ['---', '-2'], '-Size-' => ['-1', '-2-']],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
19
modules/ps_facetedsearch/tests/php/bootstrap.php
Normal file
19
modules/ps_facetedsearch/tests/php/bootstrap.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
// FacetedSearch autoloader
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
require_once __DIR__ . '/FacetedSearch/MockProxy.php';
|
||||
require_once __DIR__ . '/FacetedSearch/Mock/Filter.php';
|
||||
require_once __DIR__ . '/FacetedSearch/Mock/Facet.php';
|
||||
require_once __DIR__ . '/FacetedSearch/Interface/WidgetInterface.php';
|
||||
require_once __DIR__ . '/FacetedSearch/Interface/FacetsRendererInterface.php';
|
||||
require_once __DIR__ . '/FacetedSearch/Interface/ProductSearchProviderInterface.php';
|
||||
|
||||
define('_PS_COL_IMG_DIR_', __DIR__ . '/files/');
|
||||
|
||||
// Fake pSQL function
|
||||
function pSQL($string, $htmlOK = false)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
0
modules/ps_facetedsearch/tests/php/files/12.jpg
Normal file
0
modules/ps_facetedsearch/tests/php/files/12.jpg
Normal file
28
modules/ps_facetedsearch/tests/php/phpstan.sh
Normal file
28
modules/ps_facetedsearch/tests/php/phpstan.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
PS_VERSION=$1
|
||||
|
||||
set -e
|
||||
|
||||
# Docker images prestashop/prestashop may be used, even if the shop remains uninstalled
|
||||
echo "Pull PrestaShop files (Tag ${PS_VERSION})"
|
||||
|
||||
docker rm -f temp-ps || true
|
||||
docker volume rm -f ps-volume || true
|
||||
|
||||
docker run -tid --rm -v ps-volume:/var/www/html --name temp-ps prestashop/prestashop:$PS_VERSION
|
||||
|
||||
# Clear previous instance of the module in the PrestaShop volume
|
||||
echo "Clear previous module"
|
||||
|
||||
docker exec -t temp-ps rm -rf /var/www/html/modules/ps_facetedsearch
|
||||
|
||||
# Run a container for PHPStan, having access to the module content and PrestaShop sources.
|
||||
# This tool is outside the composer.json because of the compatibility with PHP 5.6
|
||||
echo "Run PHPStan using phpstan-${PS_VERSION}.neon file"
|
||||
|
||||
docker run --rm --volumes-from temp-ps \
|
||||
-v $PWD:/var/www/html/modules/ps_facetedsearch \
|
||||
-e _PS_ROOT_DIR_=/var/www/html \
|
||||
--workdir=/var/www/html/modules/ps_facetedsearch phpstan/phpstan:0.12 \
|
||||
analyse \
|
||||
--configuration=/var/www/html/modules/ps_facetedsearch/tests/php/phpstan/phpstan-$PS_VERSION.neon
|
||||
@@ -0,0 +1,22 @@
|
||||
includes:
|
||||
- %currentWorkingDirectory%/tests/php/phpstan/phpstan.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# module specific
|
||||
- '~^Parameter #1 \$hook_name of method ModuleCore::registerHook\(\) expects string, array<string> given\.$~'
|
||||
- '~^Parameter #2 \$active of static method CurrencyCore::getCurrencies\(\) expects bool, int given\.$~'
|
||||
- '~^Parameter #3 \$groupBy of static method CurrencyCore::getCurrencies\(\) expects bool, Shop given\.$~'
|
||||
- '~^Parameter #\d+ \$\w+ of static method ProductCore::priceCalculation\(\) expects \w+, \w+ given\.$~'
|
||||
- '~^Parameter #1 \$master of static method DbCore::getInstance\(\) expects bool, int given\.$~'
|
||||
- '~^Parameter #1 \$string of method PrestaShop\\PrestaShop\\Core\\Product\\Search\\URLFragmentSerializer::unserialize\(\) expects string, array given\.$~'
|
||||
- '~^Parameter #\d+ \$(.+?) of class Category constructor expects null, int given\.$~'
|
||||
|
||||
- '~^Access to an undefined property Cookie::\$id_lang\.$~'
|
||||
- '~^Access to constant NUMBERING_SYSTEM_LATIN on an unknown class PrestaShop\\PrestaShop\\Core\\Localization\\Locale.$~'
|
||||
|
||||
- '~^Constant _PS_JS_DIR_ not found\.$~'
|
||||
|
||||
- '~^Class PrestaShopBundle\\Form\\Admin\\Type\\(TranslatableType|SwitchType) not found\.$~'
|
||||
|
||||
- '~^Call to an undefined method PrestaShop\\PrestaShop\\Adapter\\Tools::linkRewrite\(\).$~'
|
||||
@@ -0,0 +1,20 @@
|
||||
includes:
|
||||
- %currentWorkingDirectory%/tests/php/phpstan/phpstan.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# module specific
|
||||
- '~^Parameter #1 \$hook_name of method ModuleCore::registerHook\(\) expects string, array<string> given\.$~'
|
||||
- '~^Parameter #2 \$active of static method CurrencyCore::getCurrencies\(\) expects bool, int given\.$~'
|
||||
- '~^Parameter #3 \$groupBy of static method CurrencyCore::getCurrencies\(\) expects bool, Shop given\.$~'
|
||||
- '~^Parameter #\d+ \$\w+ of static method ProductCore::priceCalculation\(\) expects \w+, \w+ given\.$~'
|
||||
- '~^Parameter #\d+ \$(.+?) of class Category constructor expects null, int given\.$~'
|
||||
|
||||
- '~^Access to an undefined property Cookie::\$id_lang\.$~'
|
||||
- '~^Access to constant NUMBERING_SYSTEM_LATIN on an unknown class PrestaShop\\PrestaShop\\Core\\Localization\\Locale.$~'
|
||||
|
||||
- '~^Constant _PS_JS_DIR_ not found\.$~'
|
||||
|
||||
- '~^Class PrestaShopBundle\\Form\\Admin\\Type\\(TranslatableType|SwitchType) not found\.$~'
|
||||
|
||||
- '~^Call to an undefined method PrestaShop\\PrestaShop\\Adapter\\Tools::linkRewrite\(\).$~'
|
||||
@@ -0,0 +1,18 @@
|
||||
includes:
|
||||
- %currentWorkingDirectory%/tests/php/phpstan/phpstan.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# module specific
|
||||
- '~^Parameter #1 \$hook_name of method ModuleCore::registerHook\(\) expects string, array<string> given\.$~'
|
||||
- '~^Parameter #2 \$active of static method CurrencyCore::getCurrencies\(\) expects bool, int given\.$~'
|
||||
- '~^Parameter #3 \$groupBy of static method CurrencyCore::getCurrencies\(\) expects bool, Shop given\.$~'
|
||||
- '~^Parameter #\d+ \$\w+ of static method ProductCore::priceCalculation\(\) expects \w+, \w+ given\.$~'
|
||||
- '~^Parameter #\d+ \$(.+?) of class Category constructor expects null, int given\.$~'
|
||||
|
||||
- '~^Access to an undefined property Cookie::\$id_lang\.$~'
|
||||
- '~^Access to constant NUMBERING_SYSTEM_LATIN on an unknown class PrestaShop\\PrestaShop\\Core\\Localization\\Locale.$~'
|
||||
|
||||
- '~^Constant _PS_JS_DIR_ not found\.$~'
|
||||
|
||||
- '~^Class PrestaShopBundle\\Form\\Admin\\Type\\(TranslatableType|SwitchType) not found\.$~'
|
||||
@@ -0,0 +1,17 @@
|
||||
includes:
|
||||
- %currentWorkingDirectory%/tests/php/phpstan/phpstan.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# module specific
|
||||
- '~^Parameter #1 \$hook_name of method ModuleCore::registerHook\(\) expects string, array<string> given\.$~'
|
||||
- '~^Parameter #2 \$active of static method CurrencyCore::getCurrencies\(\) expects bool, int given\.$~'
|
||||
- '~^Parameter #3 \$groupBy of static method CurrencyCore::getCurrencies\(\) expects bool, Shop given\.$~'
|
||||
- '~^Parameter #\d+ \$\w+ of static method ProductCore::priceCalculation\(\) expects \w+, \w+ given\.$~'
|
||||
- '~^Parameter #\d+ \$(.+?) of class Category constructor expects null, int given\.$~'
|
||||
|
||||
- '~^Access to an undefined property Cookie::\$id_lang\.$~'
|
||||
|
||||
- '~^Constant _PS_JS_DIR_ not found\.$~'
|
||||
|
||||
- '~^Class PrestaShopBundle\\Form\\Admin\\Type\\(TranslatableType|SwitchType) not found\.$~'
|
||||
@@ -0,0 +1,17 @@
|
||||
includes:
|
||||
- %currentWorkingDirectory%/tests/php/phpstan/phpstan.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# module specific
|
||||
- '~^Parameter #1 \$hook_name of method ModuleCore::registerHook\(\) expects string, array<string> given\.$~'
|
||||
- '~^Parameter #2 \$active of static method CurrencyCore::getCurrencies\(\) expects bool, int given\.$~'
|
||||
- '~^Parameter #3 \$groupBy of static method CurrencyCore::getCurrencies\(\) expects bool, Shop given\.$~'
|
||||
- '~^Parameter #\d+ \$\w+ of static method ProductCore::priceCalculation\(\) expects \w+, \w+ given\.$~'
|
||||
- '~^Parameter #\d+ \$(.+?) of class Category constructor expects null, int given\.$~'
|
||||
|
||||
- '~^Access to an undefined property Cookie::\$id_lang\.$~'
|
||||
|
||||
- '~^Constant _PS_JS_DIR_ not found\.$~'
|
||||
|
||||
- '~^Class PrestaShopBundle\\Form\\Admin\\Type\\(TranslatableType|SwitchType) not found\.$~'
|
||||
@@ -0,0 +1,17 @@
|
||||
includes:
|
||||
- %currentWorkingDirectory%/tests/php/phpstan/phpstan.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# module specific
|
||||
- '~^Parameter #1 \$hook_name of method ModuleCore::registerHook\(\) expects string, array<string> given\.$~'
|
||||
- '~^Parameter #2 \$active of static method CurrencyCore::getCurrencies\(\) expects bool, int given\.$~'
|
||||
- '~^Parameter #3 \$groupBy of static method CurrencyCore::getCurrencies\(\) expects bool, Shop given\.$~'
|
||||
- '~^Parameter #\d+ \$\w+ of static method ProductCore::priceCalculation\(\) expects \w+, \w+ given\.$~'
|
||||
- '~^Parameter #\d+ \$(.+?) of class Category constructor expects null, int given\.$~'
|
||||
|
||||
- '~^Access to an undefined property Cookie::\$id_lang\.$~'
|
||||
|
||||
- '~^Constant _PS_JS_DIR_ not found\.$~'
|
||||
|
||||
- '~^Parameter #1 \$string of method PrestaShop\\Module\\FacetedSearch\\URLSerializer::unserialize\(\) expects string, array given\.$~'
|
||||
@@ -0,0 +1,15 @@
|
||||
includes:
|
||||
- %currentWorkingDirectory%/tests/php/phpstan/phpstan.neon
|
||||
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
# module specific
|
||||
- '~^Parameter #2 \$active of static method CurrencyCore::getCurrencies\(\) expects bool, int given\.$~'
|
||||
- '~^Parameter #3 \$groupBy of static method CurrencyCore::getCurrencies\(\) expects bool, Shop given\.$~'
|
||||
- '~^Parameter #\d+ \$\w+ of static method ProductCore::priceCalculation\(\) expects \w+, \w+ given\.$~'
|
||||
|
||||
- '~^Access to an undefined property Cookie::\$id_lang\.$~'
|
||||
|
||||
- '~^Constant _PS_JS_DIR_ not found\.$~'
|
||||
|
||||
- '~^Parameter #1 \$string of method PrestaShop\\Module\\FacetedSearch\\URLSerializer::unserialize\(\) expects string, array given\.$~'
|
||||
13
modules/ps_facetedsearch/tests/php/phpstan/phpstan.neon
Normal file
13
modules/ps_facetedsearch/tests/php/phpstan/phpstan.neon
Normal file
@@ -0,0 +1,13 @@
|
||||
includes:
|
||||
- %currentWorkingDirectory%/vendor/prestashop/php-dev-tools/phpstan/ps-module-extension.neon
|
||||
|
||||
parameters:
|
||||
paths:
|
||||
# From PHPStan 0.12, paths to check are relative to the neon file
|
||||
- ../../../ps_facetedsearch.php
|
||||
- ../../../ps_facetedsearch-attribute-indexer.php
|
||||
- ../../../ps_facetedsearch-clear-cache.php
|
||||
- ../../../ps_facetedsearch-price-indexer.php
|
||||
- ../../../src/
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
level: 5
|
||||
23
modules/ps_facetedsearch/tests/php/phpunit.xml
Normal file
23
modules/ps_facetedsearch/tests/php/phpunit.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<phpunit
|
||||
bootstrap="bootstrap.php"
|
||||
>
|
||||
<php>
|
||||
<const name="_DB_PREFIX_" value="ps_"/>
|
||||
<const name="_PS_VERSION_" value="FacetedSearchVersion"/>
|
||||
<const name="_THEME_COL_DIR_" value="/theme/"/>
|
||||
<server name="REQUEST_URI" value="/catalog?from=scratch&page=1&something"/>
|
||||
<server name="HTTP_HOST" value="shop.prestashop.com"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="FacetedSearch">
|
||||
<directory>.</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../../src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
Reference in New Issue
Block a user