Skip to content

Commit c54f108

Browse files
ENGCOM-7543: Fix #13401 - Multi-Store: 'Store View' sort order values are not reflected in front-end store-switcher #28163
- Merge Pull Request #28163 from Bartlomiejsz/magento2:feature/fix_13401_store_view_sort_order_FE - Merged commits: 1. 85b6d05 2. 47bc47b 3. e87199e 4. 06a5ac4 5. a78ddde
2 parents 1ce8bcb + a78ddde commit c54f108

File tree

5 files changed

+211
-38
lines changed

5 files changed

+211
-38
lines changed

app/code/Magento/Catalog/Test/Mftf/Section/StorefrontFooterSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
1010
<section name="StorefrontFooterSection">
1111
<element name="switchStoreButton" type="button" selector="#switcher-store-trigger"/>
12+
<element name="storeViewOptionNumber" type="button" selector="//div[@class='actions dropdown options switcher-options active']//ul//li[{{var1}}]//a" parameterized="true"/>
1213
<element name="storeLink" type="button" selector="//ul[@class='dropdown switcher-dropdown']//a[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/>
1314
</section>
1415
</sections>

app/code/Magento/Store/Block/Switcher.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,15 @@ public function getGroups()
170170

171171
if ($store) {
172172
$group->setHomeUrl($store->getHomeUrl());
173+
$group->setSortOrder($store->getSortOrder());
173174
$groups[] = $group;
174175
}
175176
}
177+
178+
usort($groups, static function ($itemA, $itemB) {
179+
return (int)$itemA->getSortOrder() <=> (int)$itemB->getSortOrder();
180+
});
181+
176182
$this->setData('groups', $groups);
177183
}
178184
return $this->getData('groups');
@@ -193,7 +199,12 @@ public function getStores()
193199
$stores = [];
194200
} else {
195201
$stores = $rawStores[$groupId];
202+
203+
uasort($stores, static function ($itemA, $itemB) {
204+
return (int)$itemA->getSortOrder() <=> (int)$itemB->getSortOrder();
205+
});
196206
}
207+
197208
$this->setData('stores', $stores);
198209
}
199210
return $this->getData('stores');
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="AdminCreateStoreViewFillSortOrderActionGroup" extends="AdminCreateStoreViewActionGroup">
12+
<annotations>
13+
<description>Fill 'Sort Order' field</description>
14+
</annotations>
15+
<arguments>
16+
<argument name="sortOrder" type="string" defaultValue="0"/>
17+
</arguments>
18+
19+
<fillField selector="{{AdminNewStoreSection.sortOrderTextField}}" userInput="{{sortOrder}}" stepKey="fillSortOrder" after="enterStoreViewCode"/>
20+
</actionGroup>
21+
</actionGroups>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
11+
<test name="StorefrontCheckSortOrderStoreView">
12+
<annotations>
13+
<features value="Backend"/>
14+
<stories value="Github issue: #13401 'Store View' sort order values are not reflected"/>
15+
<title value="Check 'Store view' sort order values"/>
16+
<description value="Check 'Store View' sort order values no frontend store-switcher"/>
17+
<severity value="MINOR"/>
18+
<group value="store"/>
19+
</annotations>
20+
<before>
21+
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
22+
<actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createFirstStore">
23+
<argument name="website" value="{{_defaultWebsite.name}}"/>
24+
<argument name="storeGroupName" value="{{customStoreGroup.name}}"/>
25+
<argument name="storeGroupCode" value="{{customStoreGroup.code}}"/>
26+
</actionGroup>
27+
<actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createSecondStore">
28+
<argument name="website" value="{{_defaultWebsite.name}}"/>
29+
<argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/>
30+
<argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/>
31+
</actionGroup>
32+
</before>
33+
<after>
34+
<actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCustomStore">
35+
<argument name="storeGroupName" value="customStoreGroup.name"/>
36+
</actionGroup>
37+
<actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteSecondStore">
38+
<argument name="storeGroupName" value="SecondStoreGroupUnique.name"/>
39+
</actionGroup>
40+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
41+
<actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
42+
<argument name="indices" value=""/>
43+
</actionGroup>
44+
<actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache">
45+
<argument name="tags" value=""/>
46+
</actionGroup>
47+
</after>
48+
<actionGroup ref="AdminCreateStoreViewFillSortOrderActionGroup" stepKey="createFirstStoreView">
49+
<argument name="StoreGroup" value="customStoreGroup"/>
50+
<argument name="customStore" value="customStoreGroup"/>
51+
<argument name="sortOrder" value="30"/>
52+
</actionGroup>
53+
<actionGroup ref="AdminCreateStoreViewFillSortOrderActionGroup" stepKey="createSecondStoreView">
54+
<argument name="StoreGroup" value="SecondStoreGroupUnique"/>
55+
<argument name="customStore" value="SecondStoreGroupUnique"/>
56+
<argument name="sortOrder" value="20"/>
57+
</actionGroup>
58+
59+
<actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/>
60+
<click stepKey="selectStoreSwitcher" selector="{{StorefrontFooterSection.switchStoreButton}}"/>
61+
<grabTextFrom selector="{{StorefrontFooterSection.storeViewOptionNumber('1')}}" stepKey="grabSwatchFirstOption"/>
62+
<grabTextFrom selector="{{StorefrontFooterSection.storeViewOptionNumber('2')}}" stepKey="grabSwatchSecondOption"/>
63+
<assertStringContainsString stepKey="checkingSwatchFirstOption">
64+
<expectedResult type="string">{{SecondStoreGroupUnique.name}}</expectedResult>
65+
<actualResult type="variable">$grabSwatchFirstOption</actualResult>
66+
</assertStringContainsString>
67+
<assertStringContainsString stepKey="checkingSwatchSecondOption">
68+
<expectedResult type="string">{{customStoreGroup.name}}</expectedResult>
69+
<actualResult type="variable">$grabSwatchSecondOption</actualResult>
70+
</assertStringContainsString>
71+
</test>
72+
</tests>

app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php

Lines changed: 106 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,91 +7,159 @@
77

88
namespace Magento\Store\Test\Unit\Block;
99

10+
use Magento\Directory\Helper\Data;
11+
use Magento\Framework\App\Config\ScopeConfigInterface;
1012
use Magento\Framework\Data\Helper\PostHelper;
1113
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
1214
use Magento\Framework\UrlInterface;
1315
use Magento\Framework\View\Element\Template\Context;
1416
use Magento\Store\Api\Data\StoreInterface;
1517
use Magento\Store\Block\Switcher;
18+
use Magento\Store\Model\ScopeInterface;
1619
use Magento\Store\Model\Store;
1720
use Magento\Store\Model\StoreManagerInterface;
21+
use Magento\Store\Model\Website;
1822
use PHPUnit\Framework\MockObject\MockObject;
1923
use PHPUnit\Framework\TestCase;
2024

25+
/**
26+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
27+
*/
2128
class SwitcherTest extends TestCase
2229
{
23-
/** @var Switcher */
24-
protected $switcher;
25-
26-
/** @var Context|MockObject */
27-
protected $context;
30+
/**
31+
* @var Switcher
32+
*/
33+
private $switcher;
2834

29-
/** @var PostHelper|MockObject */
30-
protected $corePostDataHelper;
35+
/**
36+
* @var PostHelper|MockObject
37+
*/
38+
private $corePostDataHelperMock;
3139

32-
/** @var StoreManagerInterface|MockObject */
33-
protected $storeManager;
40+
/**
41+
* @var StoreManagerInterface|MockObject
42+
*/
43+
private $storeManagerMock;
3444

35-
/** @var UrlInterface|MockObject */
36-
protected $urlBuilder;
45+
/**
46+
* @var UrlInterface|MockObject
47+
*/
48+
private $urlBuilderMock;
3749

38-
/** @var StoreInterface|MockObject */
39-
private $store;
50+
/**
51+
* @var ScopeConfigInterface|MockObject
52+
*/
53+
private $scopeConfigMock;
4054

4155
/**
4256
* @return void
4357
*/
4458
protected function setUp(): void
4559
{
46-
$this->storeManager = $this->getMockBuilder(StoreManagerInterface::class)
47-
->getMock();
48-
$this->urlBuilder = $this->getMockForAbstractClass(UrlInterface::class);
49-
$this->context = $this->createMock(Context::class);
50-
$this->context->expects($this->any())->method('getStoreManager')->willReturn($this->storeManager);
51-
$this->context->expects($this->any())->method('getUrlBuilder')->willReturn($this->urlBuilder);
52-
$this->corePostDataHelper = $this->createMock(PostHelper::class);
53-
$this->store = $this->getMockBuilder(StoreInterface::class)
54-
->disableOriginalConstructor()
55-
->getMockForAbstractClass();
60+
$this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class)->getMock();
61+
$this->urlBuilderMock = $this->createMock(UrlInterface::class);
62+
$this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
63+
$contextMock = $this->createMock(Context::class);
64+
$contextMock->method('getStoreManager')->willReturn($this->storeManagerMock);
65+
$contextMock->method('getUrlBuilder')->willReturn($this->urlBuilderMock);
66+
$contextMock->method('getScopeConfig')->willReturn($this->scopeConfigMock);
67+
$this->corePostDataHelperMock = $this->createMock(PostHelper::class);
5668
$this->switcher = (new ObjectManager($this))->getObject(
5769
Switcher::class,
5870
[
59-
'context' => $this->context,
60-
'postDataHelper' => $this->corePostDataHelper,
71+
'context' => $contextMock,
72+
'postDataHelper' => $this->corePostDataHelperMock,
6173
]
6274
);
6375
}
6476

77+
public function testGetStoresSortOrder()
78+
{
79+
$groupId = 1;
80+
$storesSortOrder = [
81+
1 => 2,
82+
2 => 4,
83+
3 => 1,
84+
4 => 3
85+
];
86+
87+
$currentStoreMock = $this->getMockBuilder(Store::class)
88+
->disableOriginalConstructor()
89+
->getMock();
90+
$currentStoreMock->method('getGroupId')->willReturn($groupId);
91+
$currentStoreMock->method('isUseStoreInUrl')->willReturn(false);
92+
$this->storeManagerMock->method('getStore')
93+
->willReturn($currentStoreMock);
94+
95+
$currentWebsiteMock = $this->getMockBuilder(Website::class)
96+
->disableOriginalConstructor()
97+
->getMock();
98+
$this->storeManagerMock->method('getWebsite')
99+
->willReturn($currentWebsiteMock);
100+
101+
$stores = [];
102+
foreach ($storesSortOrder as $storeId => $sortOrder) {
103+
$storeMock = $this->getMockBuilder(Store::class)
104+
->disableOriginalConstructor()
105+
->setMethods(['getId', 'getGroupId', 'getSortOrder', 'isActive', 'getUrl'])
106+
->getMock();
107+
$storeMock->method('getId')->willReturn($storeId);
108+
$storeMock->method('getGroupId')->willReturn($groupId);
109+
$storeMock->method('getSortOrder')->willReturn($sortOrder);
110+
$storeMock->method('isActive')->willReturn(true);
111+
$storeMock->method('getUrl')->willReturn('https://example.org');
112+
$stores[] = $storeMock;
113+
}
114+
115+
$scopeConfigMap = array_map(static function ($item) {
116+
return [
117+
Data::XML_PATH_DEFAULT_LOCALE,
118+
ScopeInterface::SCOPE_STORE,
119+
$item,
120+
'en_US'
121+
];
122+
}, $stores);
123+
$this->scopeConfigMock->method('getValue')
124+
->willReturnMap($scopeConfigMap);
125+
126+
$currentWebsiteMock->method('getStores')
127+
->willReturn($stores);
128+
129+
$this->assertEquals([3, 1, 4, 2], array_keys($this->switcher->getStores()));
130+
}
131+
65132
/**
66133
* @return void
67134
*/
68135
public function testGetTargetStorePostData()
69136
{
70-
$store = $this->getMockBuilder(Store::class)
137+
$storeMock = $this->getMockBuilder(Store::class)
71138
->disableOriginalConstructor()
72139
->getMock();
73-
$store->expects($this->any())
74-
->method('getCode')
140+
$oldStoreMock = $this->getMockBuilder(StoreInterface::class)
141+
->disableOriginalConstructor()
142+
->getMockForAbstractClass();
143+
$storeMock->method('getCode')
75144
->willReturn('new-store');
76145
$storeSwitchUrl = 'http://domain.com/stores/store/redirect';
77-
$store->expects($this->atLeastOnce())
146+
$storeMock->expects($this->atLeastOnce())
78147
->method('getCurrentUrl')
79148
->with(false)
80149
->willReturn($storeSwitchUrl);
81-
$this->storeManager->expects($this->once())
150+
$this->storeManagerMock->expects($this->once())
82151
->method('getStore')
83-
->willReturn($this->store);
84-
$this->store->expects($this->once())
152+
->willReturn($oldStoreMock);
153+
$oldStoreMock->expects($this->once())
85154
->method('getCode')
86155
->willReturn('old-store');
87-
$this->urlBuilder->expects($this->once())
156+
$this->urlBuilderMock->expects($this->once())
88157
->method('getUrl')
89158
->willReturn($storeSwitchUrl);
90-
$this->corePostDataHelper->expects($this->any())
91-
->method('getPostData')
159+
$this->corePostDataHelperMock->method('getPostData')
92160
->with($storeSwitchUrl, ['___store' => 'new-store', 'uenc' => null, '___from_store' => 'old-store']);
93161

94-
$this->switcher->getTargetStorePostData($store);
162+
$this->switcher->getTargetStorePostData($storeMock);
95163
}
96164

97165
/**
@@ -104,7 +172,7 @@ public function testIsStoreInUrl($isUseStoreInUrl)
104172

105173
$storeMock->expects($this->once())->method('isUseStoreInUrl')->willReturn($isUseStoreInUrl);
106174

107-
$this->storeManager->expects($this->any())->method('getStore')->willReturn($storeMock);
175+
$this->storeManagerMock->method('getStore')->willReturn($storeMock);
108176
$this->assertEquals($this->switcher->isStoreInUrl(), $isUseStoreInUrl);
109177
// check value is cached
110178
$this->assertEquals($this->switcher->isStoreInUrl(), $isUseStoreInUrl);
@@ -114,7 +182,7 @@ public function testIsStoreInUrl($isUseStoreInUrl)
114182
* @see self::testIsStoreInUrlDataProvider()
115183
* @return array
116184
*/
117-
public function isStoreInUrlDataProvider()
185+
public function isStoreInUrlDataProvider(): array
118186
{
119187
return [[true], [false]];
120188
}

0 commit comments

Comments
 (0)