CartTest.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. /**
  7. * Test class for \Magento\Checkout\Controller\Cart
  8. */
  9. namespace Magento\Checkout\Controller;
  10. use Magento\Catalog\Api\ProductRepositoryInterface;
  11. use Magento\Checkout\Model\Session;
  12. use Magento\Checkout\Model\Session as CheckoutSession;
  13. use Magento\Customer\Model\ResourceModel\CustomerRepository;
  14. use Magento\Framework\Data\Form\FormKey;
  15. use Magento\Framework\Api\SearchCriteriaBuilder;
  16. use Magento\Quote\Model\Quote;
  17. use Magento\Quote\Api\CartRepositoryInterface;
  18. use Magento\TestFramework\Helper\Bootstrap;
  19. use Magento\TestFramework\Request;
  20. use Magento\Customer\Model\Session as CustomerSession;
  21. use Magento\Sales\Model\ResourceModel\Order\Collection as OrderCollection;
  22. use Magento\Sales\Model\ResourceModel\Order\Item\Collection as OrderItemCollection;
  23. use Magento\Framework\App\Request\Http as HttpRequest;
  24. /**
  25. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  26. * @magentoDbIsolation enabled
  27. */
  28. class CartTest extends \Magento\TestFramework\TestCase\AbstractController
  29. {
  30. /** @var CheckoutSession */
  31. private $checkoutSession;
  32. /**
  33. * @inheritdoc
  34. */
  35. protected function setUp()
  36. {
  37. parent::setUp();
  38. $this->checkoutSession = $this->_objectManager->get(CheckoutSession::class);
  39. $this->_objectManager->addSharedInstance($this->checkoutSession, CheckoutSession::class);
  40. }
  41. /**
  42. * @inheritdoc
  43. */
  44. protected function tearDown()
  45. {
  46. $this->_objectManager->removeSharedInstance(CheckoutSession::class);
  47. parent::tearDown();
  48. }
  49. /**
  50. * Test for \Magento\Checkout\Controller\Cart::configureAction() with simple product
  51. *
  52. * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product.php
  53. */
  54. public function testConfigureActionWithSimpleProduct()
  55. {
  56. /** @var $session CheckoutSession */
  57. $session = $this->_objectManager->create(CheckoutSession::class);
  58. /** @var ProductRepositoryInterface $productRepository */
  59. $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class);
  60. /** @var $product \Magento\Catalog\Model\Product */
  61. $product = $productRepository->get('simple');
  62. $quoteItem = $this->_getQuoteItemIdByProductId($session->getQuote(), $product->getId());
  63. $this->assertNotNull($quoteItem, 'Cannot get quote item for simple product');
  64. $this->dispatch(
  65. 'checkout/cart/configure/id/' . $quoteItem->getId() . '/product_id/' . $quoteItem->getProduct()->getId()
  66. );
  67. $response = $this->getResponse();
  68. $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR);
  69. $this->assertEquals(
  70. 1,
  71. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  72. '//button[@type="submit" and @title="Update Cart"]',
  73. $response->getBody()
  74. ),
  75. 'Response for simple product doesn\'t contain "Update Cart" button'
  76. );
  77. }
  78. /**
  79. * Test for \Magento\Checkout\Controller\Cart::configureAction() with simple product and custom option
  80. *
  81. * @magentoDataFixture Magento/Checkout/_files/cart_with_simple_product_and_custom_options.php
  82. */
  83. public function testConfigureActionWithSimpleProductAndCustomOption()
  84. {
  85. /** @var Quote $quote */
  86. $quote = $this->getQuote('test_order_item_with_custom_options');
  87. $this->checkoutSession->setQuoteId($quote->getId());
  88. /** @var ProductRepositoryInterface $productRepository */
  89. $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class);
  90. /** @var $product \Magento\Catalog\Model\Product */
  91. $product = $productRepository->get('simple_with_custom_options');
  92. $quoteItem = $this->_getQuoteItemIdByProductId($quote, $product->getId());
  93. $this->assertNotNull($quoteItem, 'Cannot get quote item for simple product with custom option');
  94. $this->dispatch(
  95. 'checkout/cart/configure/id/' . $quoteItem->getId() . '/product_id/' . $quoteItem->getProduct()->getId()
  96. );
  97. $response = $this->getResponse();
  98. $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR);
  99. $this->assertEquals(
  100. 1,
  101. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  102. '//button[@type="submit" and @title="Update Cart"]',
  103. $response->getBody()
  104. ),
  105. 'Response for simple product with custom option doesn\'t contain "Update Cart" button'
  106. );
  107. $this->assertEquals(
  108. 1,
  109. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  110. '//input[contains(@class,"product-custom-option") and @type="text"]',
  111. $response->getBody()
  112. ),
  113. 'Response for simple product with custom option doesn\'t contain custom option input field'
  114. );
  115. }
  116. /**
  117. * Test for \Magento\Checkout\Controller\Cart::configureAction() with bundle product
  118. *
  119. * @magentoDataFixture Magento/Checkout/_files/quote_with_bundle_product.php
  120. * @magentoDbIsolation disabled
  121. */
  122. public function testConfigureActionWithBundleProduct()
  123. {
  124. /** @var $session CheckoutSession */
  125. $session = $this->_objectManager->create(CheckoutSession::class);
  126. /** @var ProductRepositoryInterface $productRepository */
  127. $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class);
  128. /** @var $product \Magento\Catalog\Model\Product */
  129. $product = $productRepository->get('bundle-product');
  130. $quoteItem = $this->_getQuoteItemIdByProductId($session->getQuote(), $product->getId());
  131. $this->assertNotNull($quoteItem, 'Cannot get quote item for bundle product');
  132. $this->dispatch(
  133. 'checkout/cart/configure/id/' . $quoteItem->getId() . '/product_id/' . $quoteItem->getProduct()->getId()
  134. );
  135. $response = $this->getResponse();
  136. $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR);
  137. $this->assertEquals(
  138. 1,
  139. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  140. '//button[@type="submit" and @title="Update Cart"]',
  141. $response->getBody()
  142. ),
  143. 'Response for bundle product doesn\'t contain "Update Cart" button'
  144. );
  145. }
  146. /**
  147. * Test for \Magento\Checkout\Controller\Cart::configureAction() with downloadable product
  148. *
  149. * @magentoDataFixture Magento/Checkout/_files/quote_with_downloadable_product.php
  150. */
  151. public function testConfigureActionWithDownloadableProduct()
  152. {
  153. /** @var $session CheckoutSession */
  154. $session = $this->_objectManager->create(CheckoutSession::class);
  155. /** @var ProductRepositoryInterface $productRepository */
  156. $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class);
  157. /** @var $product \Magento\Catalog\Model\Product */
  158. $product = $productRepository->get('downloadable-product');
  159. $quoteItem = $this->_getQuoteItemIdByProductId($session->getQuote(), $product->getId());
  160. $this->assertNotNull($quoteItem, 'Cannot get quote item for downloadable product');
  161. $this->dispatch(
  162. 'checkout/cart/configure/id/' . $quoteItem->getId() . '/product_id/' . $quoteItem->getProduct()->getId()
  163. );
  164. $response = $this->getResponse();
  165. $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR);
  166. $this->assertEquals(
  167. 1,
  168. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  169. '//button[@type="submit" and @title="Update Cart"]',
  170. $response->getBody()
  171. ),
  172. 'Response for downloadable product doesn\'t contain "Update Cart" button'
  173. );
  174. $this->assertEquals(
  175. 1,
  176. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  177. '//*[@id="downloadable-links-list"]',
  178. $response->getBody()
  179. ),
  180. 'Response for downloadable product doesn\'t contain links for download'
  181. );
  182. }
  183. /**
  184. * @magentoDataFixture Magento/Customer/_files/customer.php
  185. * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product.php
  186. * @magentoAppIsolation enabled
  187. */
  188. public function testUpdatePostAction()
  189. {
  190. /** @var ProductRepositoryInterface $productRepository */
  191. $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class);
  192. /** @var $product \Magento\Catalog\Model\Product */
  193. $product = $productRepository->get('simple');
  194. /** Preconditions */
  195. $customerFromFixture = 1;
  196. $productId = $product->getId();
  197. $originalQuantity = 1;
  198. $updatedQuantity = 2;
  199. /** @var $checkoutSession CheckoutSession */
  200. $checkoutSession = $this->_objectManager->create(CheckoutSession::class);
  201. $quoteItem = $this->_getQuoteItemIdByProductId($checkoutSession->getQuote(), $productId);
  202. /** @var FormKey $formKey */
  203. $formKey = $this->_objectManager->get(FormKey::class);
  204. $postData = [
  205. 'cart' => [$quoteItem->getId() => ['qty' => $updatedQuantity]],
  206. 'update_cart_action' => 'update_qty',
  207. 'form_key' => $formKey->getFormKey(),
  208. ];
  209. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  210. $this->getRequest()->setPostValue($postData);
  211. /** @var $customerSession \Magento\Customer\Model\Session */
  212. $customerSession = $this->_objectManager->create(\Magento\Customer\Model\Session::class);
  213. $customerSession->setCustomerId($customerFromFixture);
  214. $this->assertNotNull($quoteItem, 'Cannot get quote item for simple product');
  215. $this->assertEquals(
  216. $originalQuantity,
  217. $quoteItem->getQty(),
  218. "Precondition failed: invalid quote item quantity"
  219. );
  220. /** Execute SUT */
  221. $this->dispatch('checkout/cart/updatePost');
  222. /** Check results */
  223. /** @var \Magento\Quote\Model\Quote $quote */
  224. $quote = $this->_objectManager->create(\Magento\Quote\Model\Quote::class);
  225. $quote->load($checkoutSession->getQuote()->getId());
  226. $quoteItem = $this->_getQuoteItemIdByProductId($quote, $product->getId());
  227. $this->assertEquals($updatedQuantity, $quoteItem->getQty(), "Invalid quote item quantity");
  228. }
  229. /**
  230. * Gets quote by reserved order id.
  231. *
  232. * @param string $reservedOrderId
  233. * @return Quote
  234. */
  235. private function getQuote($reservedOrderId)
  236. {
  237. /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
  238. $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class);
  239. $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
  240. ->create();
  241. /** @var CartRepositoryInterface $quoteRepository */
  242. $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
  243. $items = $quoteRepository->getList($searchCriteria)->getItems();
  244. return array_pop($items);
  245. }
  246. /**
  247. * Gets \Magento\Quote\Model\Quote\Item from \Magento\Quote\Model\Quote by product id
  248. *
  249. * @param \Magento\Quote\Model\Quote $quote
  250. * @param string|int $productId
  251. *
  252. * @return \Magento\Quote\Model\Quote\Item|null
  253. */
  254. private function _getQuoteItemIdByProductId($quote, $productId)
  255. {
  256. /** @var $quoteItems \Magento\Quote\Model\Quote\Item[] */
  257. $quoteItems = $quote->getAllItems();
  258. foreach ($quoteItems as $quoteItem) {
  259. if ($productId == $quoteItem->getProductId()) {
  260. return $quoteItem;
  261. }
  262. }
  263. return null;
  264. }
  265. /**
  266. * Test for \Magento\Checkout\Controller\Cart::execute() with simple product
  267. *
  268. * @param string $area
  269. * @param string $expectedPrice
  270. * @magentoDataFixture Magento/Catalog/_files/products.php
  271. * @magentoAppIsolation enabled
  272. * @dataProvider addAddProductDataProvider
  273. */
  274. public function testAddToCartSimpleProduct($area, $expectedPrice)
  275. {
  276. $formKey = $this->_objectManager->get(FormKey::class);
  277. $postData = [
  278. 'qty' => '1',
  279. 'product' => '1',
  280. 'custom_price' => 1,
  281. 'form_key' => $formKey->getFormKey(),
  282. 'isAjax' => 1
  283. ];
  284. \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea($area);
  285. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  286. $this->getRequest()->setPostValue($postData);
  287. $quote = $this->_objectManager->create(\Magento\Checkout\Model\Cart::class);
  288. /** @var \Magento\Checkout\Controller\Cart\Add $controller */
  289. $controller = $this->_objectManager->create(\Magento\Checkout\Controller\Cart\Add::class, [$quote]);
  290. $controller->execute();
  291. $this->assertContains(json_encode([]), $this->getResponse()->getBody());
  292. $items = $quote->getItems()->getItems();
  293. $this->assertTrue(is_array($items), 'Quote doesn\'t have any items');
  294. $this->assertCount(1, $items, 'Expected quote items not equal to 1');
  295. $item = reset($items);
  296. $this->assertEquals(1, $item->getProductId(), 'Quote has more than one product');
  297. $this->assertEquals($expectedPrice, $item->getPrice(), 'Expected product price failed');
  298. }
  299. /**
  300. * Data provider for testAddToCartSimpleProduct
  301. */
  302. public function addAddProductDataProvider()
  303. {
  304. return [
  305. 'frontend' => ['frontend', 'expected_price' => 10],
  306. 'adminhtml' => ['adminhtml', 'expected_price' => 1]
  307. ];
  308. }
  309. /**
  310. * Test for \Magento\Checkout\Controller\Cart\Add::execute() with simple product and activated redirect to cart
  311. *
  312. * @magentoDataFixture Magento/Catalog/_files/products.php
  313. * @magentoConfigFixture current_store checkout/cart/redirect_to_cart 1
  314. * @magentoAppIsolation enabled
  315. */
  316. public function testMessageAtAddToCartWithRedirect()
  317. {
  318. $formKey = $this->_objectManager->get(FormKey::class);
  319. $postData = [
  320. 'qty' => '1',
  321. 'product' => '1',
  322. 'custom_price' => 1,
  323. 'form_key' => $formKey->getFormKey(),
  324. 'isAjax' => 1
  325. ];
  326. \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend');
  327. $this->getRequest()->setPostValue($postData);
  328. $this->getRequest()->setMethod('POST');
  329. $this->dispatch('checkout/cart/add');
  330. $this->assertEquals(
  331. '{"backUrl":"http:\/\/localhost\/index.php\/checkout\/cart\/"}',
  332. $this->getResponse()->getBody()
  333. );
  334. $this->assertSessionMessages(
  335. $this->contains(
  336. 'You added Simple Product to your shopping cart.'
  337. ),
  338. \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
  339. );
  340. }
  341. /**
  342. * Test for \Magento\Checkout\Controller\Cart\Add::execute() with simple product and deactivated redirect to cart
  343. *
  344. * @magentoDataFixture Magento/Catalog/_files/products.php
  345. * @magentoConfigFixture current_store checkout/cart/redirect_to_cart 0
  346. * @magentoAppIsolation enabled
  347. */
  348. public function testMessageAtAddToCartWithoutRedirect()
  349. {
  350. $formKey = $this->_objectManager->get(FormKey::class);
  351. $postData = [
  352. 'qty' => '1',
  353. 'product' => '1',
  354. 'custom_price' => 1,
  355. 'form_key' => $formKey->getFormKey(),
  356. 'isAjax' => 1
  357. ];
  358. \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend');
  359. $this->getRequest()->setPostValue($postData);
  360. $this->getRequest()->setMethod('POST');
  361. $this->dispatch('checkout/cart/add');
  362. $this->assertFalse($this->getResponse()->isRedirect());
  363. $this->assertEquals('[]', $this->getResponse()->getBody());
  364. $this->assertSessionMessages(
  365. $this->contains(
  366. "\n" . 'You added Simple Product to your ' .
  367. '<a href="http://localhost/index.php/checkout/cart/">shopping cart</a>.'
  368. ),
  369. \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
  370. );
  371. }
  372. /**
  373. * @covers \Magento\Checkout\Controller\Cart\Addgroup::execute()
  374. *
  375. * Test customer can add items to cart only if they belong to him.
  376. *
  377. * @param bool $loggedIn
  378. * @param string $request
  379. * @magentoAppArea frontend
  380. * @magentoDataFixture Magento/Checkout/_files/order_items.php
  381. * @dataProvider reorderItemsDataProvider
  382. * @return void
  383. */
  384. public function testReorderItems(bool $loggedIn, string $request)
  385. {
  386. // Make sure test starts without logged in customer.
  387. $customerSession = $this->_objectManager->get(CustomerSession::class);
  388. $customerSession->logout();
  389. $checkoutSession = Bootstrap::getObjectManager()->get(Session::class);
  390. $expected = [];
  391. if ($loggedIn && $request == Request::METHOD_POST) {
  392. $customer = $this->_objectManager->create(CustomerRepository::class)->get('customer2@example.com');
  393. $customerSession->setCustomerDataObject($customer);
  394. $orderCollection = $this->_objectManager->create(OrderCollection::class);
  395. $orderCollection->addFieldToFilter('customer_id', $customer->getId());
  396. $orderItemCollection = $this->_objectManager->create(OrderItemCollection::class);
  397. $orderItemCollection->addFieldToFilter('order_id', ['in' => $orderCollection->getAllIds()]);
  398. $expected = $orderItemCollection->getColumnValues('product_id');
  399. }
  400. $this->prepareRequest($request);
  401. $this->dispatch('checkout/cart/addGroup');
  402. $this->assertEquals(
  403. $expected,
  404. $checkoutSession->getQuote()->getItemsCollection()->getColumnValues('product_id')
  405. );
  406. // Make sure test doesn't left logged in customer after execution.
  407. $customerSession->logout();
  408. }
  409. /**
  410. * Data provider for testReorderItems.
  411. *
  412. * @return array
  413. */
  414. public function reorderItemsDataProvider()
  415. {
  416. return [
  417. [
  418. 'logged_in' => false,
  419. 'request_type' => Request::METHOD_POST,
  420. ],
  421. [
  422. 'logged_in' => false,
  423. 'request_type' => Request::METHOD_GET,
  424. ],
  425. [
  426. 'logged_in' => true,
  427. 'request_type' => Request::METHOD_POST,
  428. ],
  429. [
  430. 'logged_in' => true,
  431. 'request_type' => Request::METHOD_GET,
  432. ],
  433. ];
  434. }
  435. /**
  436. * Prepare request for testReorderItems.
  437. *
  438. * @param string $method
  439. * @return void
  440. */
  441. private function prepareRequest(string $method)
  442. {
  443. /** @var OrderItemCollection $orderItems */
  444. $orderItems = $this->_objectManager->create(OrderItemCollection::class);
  445. /** @var FormKey $key */
  446. $key = $this->_objectManager->get(FormKey::class);
  447. $data = [
  448. 'form_key' => $key->getFormKey(),
  449. 'order_items' => $orderItems->getAllIds(),
  450. ];
  451. $this->getRequest()->setMethod($method);
  452. switch ($method) {
  453. case Request::METHOD_POST:
  454. $this->getRequest()->setPostValue($data);
  455. break;
  456. case Request::METHOD_GET:
  457. default:
  458. $this->getRequest()->setParams($data);
  459. break;
  460. }
  461. }
  462. }