Skip to content

Commit 81815e7

Browse files
committed
MC-22032: Storefront: Product URL Management
1 parent d5104d5 commit 81815e7

File tree

3 files changed

+222
-0
lines changed

3 files changed

+222
-0
lines changed
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Controller\Product;
9+
10+
use Magento\Catalog\Api\CategoryRepositoryInterface;
11+
use Magento\Catalog\Api\Data\ProductInterface;
12+
use Magento\Catalog\Api\ProductRepositoryInterface;
13+
use Magento\Catalog\Model\AbstractModel;
14+
use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator;
15+
use Magento\Framework\App\Config\ScopeConfigInterface;
16+
use Magento\Framework\Registry;
17+
use Magento\Store\Model\ScopeInterface;
18+
use Magento\Store\Model\StoreManagerInterface;
19+
use Magento\TestFramework\Request;
20+
use Magento\TestFramework\Response;
21+
use Magento\TestFramework\TestCase\AbstractController;
22+
23+
/**
24+
* Checks product availability on storefront by url rewrite
25+
*
26+
* @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1
27+
* @magentoDbIsolation enabled
28+
*/
29+
class ProductUrlRewriteTest extends AbstractController
30+
{
31+
/** @var ScopeConfigInterface */
32+
private $config;
33+
34+
/** @var ProductRepositoryInterface */
35+
private $productRepository;
36+
37+
/** @var Registry */
38+
private $registry;
39+
40+
/** @var CategoryRepositoryInterface */
41+
private $categoryRepository;
42+
43+
/** @var StoreManagerInterface */
44+
private $storeManager;
45+
46+
/** @var string */
47+
private $urlSuffix;
48+
49+
/**
50+
* @inheritdoc
51+
*/
52+
protected function setUp()
53+
{
54+
parent::setUp();
55+
56+
$this->config = $this->_objectManager->get(ScopeConfigInterface::class);
57+
$this->productRepository = $this->_objectManager->create(ProductRepositoryInterface::class);
58+
$this->registry = $this->_objectManager->get(Registry::class);
59+
$this->categoryRepository = $this->_objectManager->create(CategoryRepositoryInterface::class);
60+
$this->storeManager = $this->_objectManager->get(StoreManagerInterface::class);
61+
$this->urlSuffix = $this->config->getValue(
62+
CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX,
63+
ScopeInterface::SCOPE_STORE
64+
);
65+
}
66+
67+
/**
68+
* @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
69+
* @return void
70+
*/
71+
public function testProductUrlRewrite(): void
72+
{
73+
$product = $this->productRepository->get('simple2');
74+
$url = $this->prepareUrl($product);
75+
$this->dispatch($url);
76+
77+
$this->assertProductIsVisible($product);
78+
}
79+
80+
/**
81+
* @magentoDataFixture Magento/Catalog/_files/category_product.php
82+
* @return void
83+
*/
84+
public function testCategoryProductUrlRewrite(): void
85+
{
86+
$category = $this->categoryRepository->get(333);
87+
$product = $this->productRepository->get('simple333');
88+
$url = $this->prepareUrl($category, false) . $this->prepareUrl($product);
89+
$this->dispatch($url);
90+
91+
$this->assertProductIsVisible($product);
92+
}
93+
94+
/**
95+
* @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
96+
* @return void
97+
*/
98+
public function testProductRedirect(): void
99+
{
100+
$product = $this->productRepository->get('simple2');
101+
$oldUrl = $this->prepareUrl($product);
102+
$data = [
103+
'url_key' => 'new-url-key',
104+
'url_key_create_redirect' => $product->getUrlKey(),
105+
'save_rewrites_history' => true,
106+
];
107+
$this->updateProduct($product, $data);
108+
$this->dispatch($oldUrl);
109+
110+
$this->assertRedirect($this->stringContains('new-url-key' . $this->urlSuffix));
111+
}
112+
113+
/**
114+
* @magentoDbIsolation disabled
115+
* @magentoDataFixture Magento/Catalog/_files/product_with_two_stores.php
116+
* @return void
117+
*/
118+
public function testMultistoreProductUrlRewrite(): void
119+
{
120+
$currentStore = $this->storeManager->getStore();
121+
$product = $this->productRepository->get('simple2');
122+
$firstStoreUrl = $this->prepareUrl($product);
123+
$secondStoreId = $this->storeManager->getStore('fixturestore')->getId();
124+
$this->storeManager->setCurrentStore($secondStoreId);
125+
126+
try {
127+
$product = $this->updateProduct($product, ['url_key' => 'second-store-url-key']);
128+
$this->assertEquals('second-store-url-key', $product->getUrlKey());
129+
$secondStoreUrl = $this->prepareUrl($product);
130+
131+
$this->dispatch($secondStoreUrl);
132+
$this->assertProductIsVisible($product);
133+
$this->cleanUpCachedObjects();
134+
} finally {
135+
$this->storeManager->setCurrentStore($currentStore);
136+
}
137+
138+
$this->dispatch($firstStoreUrl);
139+
$this->assertProductIsVisible($product);
140+
}
141+
142+
/**
143+
* Update product
144+
*
145+
* @param ProductInterface $product
146+
* @param array $data
147+
* @return ProductInterface
148+
*/
149+
private function updateProduct(ProductInterface $product, array $data): ProductInterface
150+
{
151+
$product->addData($data);
152+
153+
return $this->productRepository->save($product);
154+
}
155+
156+
/**
157+
* Clear request object.
158+
*
159+
* @return void
160+
*/
161+
private function cleanUpCachedObjects(): void
162+
{
163+
$this->registry->unregister('current_product');
164+
$this->registry->unregister('product');
165+
$this->_objectManager->removeSharedInstance(Request::class);
166+
$this->_objectManager->removeSharedInstance(Response::class);
167+
$this->_response = null;
168+
$this->_request = null;
169+
}
170+
171+
/**
172+
* Prepare url to dispatch
173+
*
174+
* @param AbstractModel $object
175+
* @param bool $addSuffix
176+
* @return string
177+
*/
178+
private function prepareUrl(AbstractModel $object, bool $addSuffix = true): string
179+
{
180+
$url = $addSuffix ? '/' . $object->getUrlKey() . $this->urlSuffix : '/' . $object->getUrlKey();
181+
182+
return $url;
183+
}
184+
185+
/**
186+
* Assert that product is available in storefront
187+
*
188+
* @param ProductInterface $product
189+
* @return void
190+
*/
191+
private function assertProductIsVisible(ProductInterface $product): void
192+
{
193+
$this->assertEquals(
194+
Response::STATUS_CODE_200,
195+
$this->getResponse()->getHttpResponseCode(),
196+
'Wrong response code is returned'
197+
);
198+
$this->assertEquals(
199+
$product->getSku(),
200+
$this->registry->registry('current_product')->getSku(),
201+
'Wrong product is registered'
202+
);
203+
}
204+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
require __DIR__ . '/../../Store/_files/core_fixturestore.php';
9+
require __DIR__ . '/../../Catalog/_files/second_product_simple.php';
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
require __DIR__ . '/../../Catalog/_files/second_product_simple_rollback.php';
9+
require __DIR__ . '/../../Store/_files/core_fixturestore_rollback.php';

0 commit comments

Comments
 (0)