Skip to content

Commit cf79589

Browse files
committed
MC-36374: Checkout with simple product as a guest/customer
1 parent 42311fa commit cf79589

12 files changed

+1437
-672
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
use Magento\Catalog\Api\ProductRepositoryInterface;
9+
use Magento\Catalog\Api\Data\ProductInterfaceFactory;
10+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
11+
use Magento\Catalog\Model\Product\Type;
12+
use Magento\Catalog\Model\Product\Visibility;
13+
use Magento\TestFramework\Helper\Bootstrap;
14+
15+
$objectManager = Bootstrap::getObjectManager();
16+
/** @var ProductInterfaceFactory ProductInterfaceFactory */
17+
$productFactory = $objectManager->get(ProductInterfaceFactory::class);
18+
/** @var ProductRepositoryInterface $productRepositoryFactory */
19+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
20+
$productRepository->cleanCache();
21+
22+
$product = $productFactory->create();
23+
$product->setTypeId(Type::TYPE_SIMPLE)
24+
->setStatus(Status::STATUS_ENABLED)
25+
->setAttributeSetId($product->getDefaultAttributeSetId())
26+
->setWebsiteIds([1])
27+
->setName('Simple Product min and max sale qty')
28+
->setSku('simple_product_min_max_sale_qty')
29+
->setPrice(10)
30+
->setWeight(1)
31+
->setVisibility(Visibility::VISIBILITY_BOTH)
32+
->setStockData(
33+
[
34+
'use_config_manage_stock' => 1,
35+
'qty' => 100,
36+
'is_qty_decimal' => 0,
37+
'is_in_stock' => 1,
38+
'min_sale_qty' => 5,
39+
'max_sale_qty' => 20,
40+
]
41+
)
42+
->setCanSaveCustomOptions(true)
43+
->setHasOptions(true);
44+
$productRepository->save($product);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
use Magento\Catalog\Api\ProductRepositoryInterface;
9+
use Magento\Framework\Exception\NoSuchEntityException;
10+
use Magento\Framework\Registry;
11+
use Magento\TestFramework\Helper\Bootstrap;
12+
13+
$objectManager = Bootstrap::getObjectManager();
14+
/** @var Registry $registry */
15+
$registry = $objectManager->get(Registry::class);
16+
/** @var ProductRepositoryInterface $productRepository */
17+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
18+
19+
$registry->unregister('isSecureArea');
20+
$registry->register('isSecureArea', true);
21+
22+
try {
23+
$productRepository->deleteById('simple_product_min_max_sale_qty');
24+
} catch (NoSuchEntityException $e) {
25+
//product already deleted
26+
}
27+
28+
$registry->unregister('isSecureArea');
29+
$registry->register('isSecureArea', false);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
use Magento\Catalog\Api\ProductRepositoryInterface;
9+
use Magento\Catalog\Api\Data\ProductInterfaceFactory;
10+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
11+
use Magento\Catalog\Model\Product\Type;
12+
use Magento\Catalog\Model\Product\Visibility;
13+
use Magento\TestFramework\Helper\Bootstrap;
14+
15+
$objectManager = Bootstrap::getObjectManager();
16+
/** @var ProductInterfaceFactory ProductInterfaceFactory */
17+
$productFactory = $objectManager->get(ProductInterfaceFactory::class);
18+
/** @var ProductRepositoryInterface $productRepositoryFactory */
19+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
20+
$productRepository->cleanCache();
21+
22+
$product = $productFactory->create();
23+
$product->setTypeId(Type::TYPE_SIMPLE)
24+
->setStatus(Status::STATUS_ENABLED)
25+
->setAttributeSetId($product->getDefaultAttributeSetId())
26+
->setWebsiteIds([1])
27+
->setName('Simple Product with qty increments')
28+
->setSku('simple_product_with_qty_increments')
29+
->setPrice(10)
30+
->setWeight(1)
31+
->setVisibility(Visibility::VISIBILITY_BOTH)
32+
->setStockData(
33+
[
34+
'use_config_manage_stock' => 1,
35+
'qty' => 100,
36+
'is_qty_decimal' => 0,
37+
'is_in_stock' => 1,
38+
'enable_qty_increments' => 1,
39+
'qty_increments' => 3,
40+
]
41+
)
42+
->setCanSaveCustomOptions(true)
43+
->setHasOptions(true);
44+
$productRepository->save($product);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
use Magento\Catalog\Api\ProductRepositoryInterface;
9+
use Magento\Framework\Exception\NoSuchEntityException;
10+
use Magento\Framework\Registry;
11+
use Magento\TestFramework\Helper\Bootstrap;
12+
13+
$objectManager = Bootstrap::getObjectManager();
14+
/** @var Registry $registry */
15+
$registry = $objectManager->get(Registry::class);
16+
/** @var ProductRepositoryInterface $productRepository */
17+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
18+
19+
$registry->unregister('isSecureArea');
20+
$registry->register('isSecureArea', true);
21+
22+
try {
23+
$productRepository->deleteById('simple_product_with_qty_increments');
24+
} catch (NoSuchEntityException $e) {
25+
//product already deleted
26+
}
27+
28+
$registry->unregister('isSecureArea');
29+
$registry->register('isSecureArea', false);
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
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\Checkout\Controller\Cart;
9+
10+
use Laminas\Stdlib\Parameters;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\Checkout\Model\SessionFactory as CheckoutSessionFactory;
13+
use Magento\Framework\App\Request\Http as HttpRequest;
14+
use Magento\Framework\Escaper;
15+
use Magento\Framework\Message\MessageInterface;
16+
use Magento\Framework\Serialize\SerializerInterface;
17+
use Magento\TestFramework\Store\ExecuteInStoreContext;
18+
use Magento\TestFramework\TestCase\AbstractController;
19+
20+
/**
21+
* Class add product to cart controller.
22+
*
23+
* @see \Magento\Checkout\Controller\Cart\Add
24+
* @magentoAppArea frontend
25+
* @magentoDbIsolation enabled
26+
*/
27+
class AddTest extends AbstractController
28+
{
29+
/** @var SerializerInterface */
30+
private $json;
31+
32+
/** @var CheckoutSessionFactory */
33+
private $checkoutSessionFactory;
34+
35+
/** @var ProductRepositoryInterface */
36+
private $productRepository;
37+
38+
/** @var ExecuteInStoreContext */
39+
private $executeInStoreContext;
40+
41+
/** @var Escaper */
42+
private $escaper;
43+
44+
/**
45+
* @inheritdoc
46+
*/
47+
protected function setUp(): void
48+
{
49+
parent::setUp();
50+
51+
$this->json = $this->_objectManager->get(SerializerInterface::class);
52+
$this->checkoutSessionFactory = $this->_objectManager->get(CheckoutSessionFactory::class);
53+
$this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class);
54+
$this->productRepository->cleanCache();
55+
$this->executeInStoreContext = $this->_objectManager->get(ExecuteInStoreContext::class);
56+
$this->escaper = $this->_objectManager->get(Escaper::class);
57+
}
58+
59+
/**
60+
* Test with simple product and activated redirect to cart
61+
*
62+
* @magentoDataFixture Magento/Catalog/_files/products.php
63+
* @magentoConfigFixture current_store checkout/cart/redirect_to_cart 1
64+
*
65+
* @return void
66+
*/
67+
public function testMessageAtAddToCartWithRedirect(): void
68+
{
69+
$this->prepareReferer();
70+
$checkoutSession = $this->checkoutSessionFactory->create();
71+
$postData = [
72+
'qty' => '1',
73+
'product' => '1',
74+
'custom_price' => 1,
75+
'isAjax' => 1,
76+
];
77+
$this->dispatchAddToCartRequest($postData);
78+
$this->assertEquals(
79+
$this->json->serialize(['backUrl' => 'http://localhost/checkout/cart/']),
80+
$this->getResponse()->getBody()
81+
);
82+
$this->assertSessionMessages(
83+
$this->containsEqual((string)__('You added %1 to your shopping cart.', 'Simple Product')),
84+
MessageInterface::TYPE_SUCCESS
85+
);
86+
$this->assertCount(1, $checkoutSession->getQuote()->getItemsCollection());
87+
}
88+
89+
/**
90+
* Test with simple product and deactivated redirect to cart
91+
*
92+
* @magentoDataFixture Magento/Catalog/_files/products.php
93+
* @magentoConfigFixture current_store checkout/cart/redirect_to_cart 0
94+
*
95+
* @return void
96+
*/
97+
public function testMessageAtAddToCartWithoutRedirect(): void
98+
{
99+
$this->prepareReferer();
100+
$checkoutSession = $this->checkoutSessionFactory->create();
101+
$postData = [
102+
'qty' => '1',
103+
'product' => '1',
104+
'custom_price' => 1,
105+
'isAjax' => 1,
106+
];
107+
$this->dispatchAddToCartRequest($postData);
108+
$this->assertFalse($this->getResponse()->isRedirect());
109+
$this->assertEquals('[]', $this->getResponse()->getBody());
110+
$message = (string)__(
111+
'You added %1 to your <a href="%2">shopping cart</a>.',
112+
'Simple Product',
113+
'http://localhost/checkout/cart/'
114+
);
115+
$this->assertSessionMessages(
116+
$this->containsEqual("\n" . $message),
117+
MessageInterface::TYPE_SUCCESS
118+
);
119+
$this->assertCount(1, $checkoutSession->getQuote()->getItemsCollection());
120+
}
121+
122+
/**
123+
* @dataProvider wrongParamsDataProvider
124+
*
125+
* @param array $params
126+
* @return void
127+
*/
128+
public function testWithWrongParams(array $params): void
129+
{
130+
$this->prepareReferer();
131+
$this->dispatchAddToCartRequest($params);
132+
$this->assertRedirect($this->stringContains('http://localhost/test'));
133+
}
134+
135+
/**
136+
* @return array
137+
*/
138+
public function wrongParamsDataProvider(): array
139+
{
140+
return [
141+
'empty_params' => ['params' => []],
142+
'with_not_existing_product_id' => ['params' => ['product' => 989]],
143+
];
144+
}
145+
146+
/**
147+
* @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
148+
* @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
149+
*
150+
* @return void
151+
*/
152+
public function testAddProductFromUnavailableWebsite(): void
153+
{
154+
$this->prepareReferer();
155+
$product = $this->productRepository->get('simple-1');
156+
$postData = ['product' => $product->getId()];
157+
$this->executeInStoreContext->execute('fixture_second_store', [$this, 'dispatchAddToCartRequest'], $postData);
158+
$this->assertRedirect($this->stringContains('http://localhost/test'));
159+
$message = $this->escaper->escapeHtml(
160+
(string)__('The product wasn\'t found. Verify the product and try again.')
161+
);
162+
$this->assertSessionMessages($this->containsEqual($message), MessageInterface::TYPE_ERROR);
163+
}
164+
165+
/**
166+
* @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
167+
*
168+
* @return void
169+
*/
170+
public function testAddProductWithUnavailableQty(): void
171+
{
172+
$product = $this->productRepository->get('simple-1');
173+
$postData = ['product' => $product->getId(), 'qty' => '1000'];
174+
$this->dispatchAddToCartRequest($postData);
175+
$message = (string)__('The requested qty is not available');
176+
$this->assertSessionMessages($this->containsEqual($message), MessageInterface::TYPE_ERROR);
177+
$this->assertRedirect($this->stringContains($product->getProductUrl()));
178+
}
179+
180+
/**
181+
* @magentoDataFixture Magento/Catalog/_files/products_related_multiple.php
182+
*
183+
* @return void
184+
*/
185+
public function testAddProductWithRelated(): void
186+
{
187+
$this->prepareReferer();
188+
$checkoutSession = $this->checkoutSessionFactory->create();
189+
$product = $this->productRepository->get('simple_with_cross');
190+
$params = [
191+
'product' => $product->getId(),
192+
'related_product' => implode(',', $product->getRelatedProductIds()),
193+
];
194+
$this->dispatchAddToCartRequest($params);
195+
$this->assertCount(3, $checkoutSession->getQuote()->getItemsCollection());
196+
$message = (string)__(
197+
'You added %1 to your <a href="%2">shopping cart</a>.',
198+
$product->getName(),
199+
'http://localhost/checkout/cart/'
200+
);
201+
$this->assertSessionMessages(
202+
$this->containsEqual("\n" . $message),
203+
MessageInterface::TYPE_SUCCESS
204+
);
205+
}
206+
207+
/**
208+
* Dispatch add product to cart request.
209+
*
210+
* @param array $postData
211+
* @return void
212+
*/
213+
public function dispatchAddToCartRequest(array $postData = []): void
214+
{
215+
$this->getRequest()->setPostValue($postData);
216+
$this->getRequest()->setMethod(HttpRequest::METHOD_POST);
217+
$this->dispatch('checkout/cart/add');
218+
}
219+
220+
/**
221+
* Prepare referer to test.
222+
*
223+
* @return void
224+
*/
225+
private function prepareReferer(): void
226+
{
227+
$parameters = $this->_objectManager->create(Parameters::class);
228+
$parameters->set('HTTP_REFERER', 'http://localhost/test');
229+
$this->getRequest()->setServer($parameters);
230+
}
231+
}

0 commit comments

Comments
 (0)