354 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			354 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
<?php
 | 
						|
/**
 | 
						|
 * Copyright © Magento, Inc. All rights reserved.
 | 
						|
 * See COPYING.txt for license details.
 | 
						|
 */
 | 
						|
declare(strict_types=1);
 | 
						|
 | 
						|
namespace Magento\Checkout\Block\Cart;
 | 
						|
 | 
						|
use Magento\Catalog\Block\Product\ProductList\AbstractLinksTest;
 | 
						|
use Magento\Catalog\ViewModel\Product\Listing\PreparePostData;
 | 
						|
use Magento\Checkout\Model\Session;
 | 
						|
use Magento\TestFramework\Helper\Xpath;
 | 
						|
use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId;
 | 
						|
use Magento\TestFramework\Store\ExecuteInStoreContext;
 | 
						|
 | 
						|
/**
 | 
						|
 * Check the correct behavior of cross-sell products in the shopping cart
 | 
						|
 *
 | 
						|
 * @see \Magento\Checkout\Block\Cart\Crosssell
 | 
						|
 * @magentoDbIsolation disabled
 | 
						|
 * @magentoAppIsolation enabled
 | 
						|
 * @magentoAppArea frontend
 | 
						|
 */
 | 
						|
class CrosssellTest extends AbstractLinksTest
 | 
						|
{
 | 
						|
    private const MAX_ITEM_COUNT = 4;
 | 
						|
 | 
						|
    /** @var Session */
 | 
						|
    private $checkoutSession;
 | 
						|
 | 
						|
    /** @var string */
 | 
						|
    private $addToCartButtonXpath = "//div[contains(@class, 'actions-primary')]/button[@type='button']";
 | 
						|
 | 
						|
    /** @var string */
 | 
						|
    private $addToCartSubmitXpath = "//div[contains(@class, 'actions-primary')]"
 | 
						|
    . "/form[@data-product-sku='%s']/button[@type='submit']";
 | 
						|
 | 
						|
    /** @var string */
 | 
						|
    private $addToLinksXpath = "//div[contains(@class, 'actions-secondary')]";
 | 
						|
 | 
						|
    /** @var ExecuteInStoreContext */
 | 
						|
    private $executeInStoreContext;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @inheritdoc
 | 
						|
     */
 | 
						|
    protected function setUp(): void
 | 
						|
    {
 | 
						|
        parent::setUp();
 | 
						|
 | 
						|
        $this->block = $this->layout->createBlock(Crosssell::class);
 | 
						|
        $this->linkType = 'crosssell';
 | 
						|
        $this->titleName = (string)__('More Choices:');
 | 
						|
        $this->checkoutSession = $this->objectManager->get(Session::class);
 | 
						|
        $this->executeInStoreContext = $this->objectManager->get(ExecuteInStoreContext::class);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Checks for a simple cross-sell product when block code is generated
 | 
						|
     *
 | 
						|
     * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function testSimpleCrosssellProduct(): void
 | 
						|
    {
 | 
						|
        $relatedProduct = $this->productRepository->get('simple-1');
 | 
						|
        $this->linkProducts('simple', ['simple-1' => ['position' => 2]]);
 | 
						|
        $this->setCheckoutSessionQuote('test_order_with_simple_product_without_address');
 | 
						|
        $this->prepareBlock();
 | 
						|
        $html = $this->block->toHtml();
 | 
						|
 | 
						|
        $this->assertNotEmpty($html);
 | 
						|
        $this->assertEquals(
 | 
						|
            1,
 | 
						|
            Xpath::getElementsCountForXpath(sprintf($this->titleXpath, $this->linkType, $this->titleName), $html),
 | 
						|
            'Expected title is incorrect or missing!'
 | 
						|
        );
 | 
						|
        $this->assertEquals(
 | 
						|
            1,
 | 
						|
            Xpath::getElementsCountForXpath(sprintf($this->addToCartSubmitXpath, $relatedProduct->getSku()), $html),
 | 
						|
            'Expected add to cart button is incorrect or missing!'
 | 
						|
        );
 | 
						|
        $this->assertEquals(
 | 
						|
            1,
 | 
						|
            Xpath::getElementsCountForXpath($this->addToLinksXpath, $html),
 | 
						|
            'Expected add to links is incorrect or missing!'
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Checks for a cross-sell product with required option when block code is generated
 | 
						|
     *
 | 
						|
     * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/product_virtual_with_options.php
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function testCrosssellProductWithRequiredOption(): void
 | 
						|
    {
 | 
						|
        $this->linkProducts('simple', ['virtual' => ['position' => 1]]);
 | 
						|
        $this->setCheckoutSessionQuote('test_order_with_simple_product_without_address');
 | 
						|
        $this->prepareBlock();
 | 
						|
        $html = $this->block->toHtml();
 | 
						|
 | 
						|
        $this->assertEquals(
 | 
						|
            1,
 | 
						|
            Xpath::getElementsCountForXpath($this->addToCartButtonXpath, $html),
 | 
						|
            'Expected add to cart button is incorrect or missing!'
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Test the display of cross-sell products in the block
 | 
						|
     *
 | 
						|
     * @dataProvider displayLinkedProductsProvider
 | 
						|
     * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/products_list.php
 | 
						|
     * @param array $data
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function testDisplayCrosssellProducts(array $data): void
 | 
						|
    {
 | 
						|
        $this->updateProducts($data['updateProducts']);
 | 
						|
        $this->linkProducts('simple', $this->existingProducts);
 | 
						|
        $items = $this->getBlockItems('test_order_with_simple_product_without_address');
 | 
						|
 | 
						|
        $this->assertEquals(
 | 
						|
            $data['expectedProductLinks'],
 | 
						|
            $this->getActualLinks($items),
 | 
						|
            'Expected cross-sell products do not match actual cross-sell products!'
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Test the position and max count of cross-sell products in the block
 | 
						|
     *
 | 
						|
     * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/products_list.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function testPositionCrosssellProducts(): void
 | 
						|
    {
 | 
						|
        $positionData = array_merge_recursive(
 | 
						|
            $this->getPositionData(),
 | 
						|
            [
 | 
						|
                'productLinks' => [
 | 
						|
                    'simple-1' => ['position' => 5],
 | 
						|
                    'simple2' => ['position' => 4],
 | 
						|
                ],
 | 
						|
                'expectedProductLinks' => [
 | 
						|
                    'simple2',
 | 
						|
                ],
 | 
						|
            ]
 | 
						|
        );
 | 
						|
        $this->linkProducts('simple', $positionData['productLinks']);
 | 
						|
        $items = $this->getBlockItems('test_order_with_simple_product_without_address');
 | 
						|
 | 
						|
        $this->assertCount(
 | 
						|
            self::MAX_ITEM_COUNT,
 | 
						|
            $items,
 | 
						|
            'Expected quantity of cross-sell products do not match the actual quantity!'
 | 
						|
        );
 | 
						|
        $this->assertEquals(
 | 
						|
            $positionData['expectedProductLinks'],
 | 
						|
            $this->getActualLinks($items),
 | 
						|
            'Expected cross-sell products do not match actual cross-sell products!'
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Test the position and max count of cross-sell products in the block
 | 
						|
     * when set last added product in checkout session
 | 
						|
     *
 | 
						|
     * @dataProvider positionWithLastAddedProductProvider
 | 
						|
     * @magentoDataFixture Magento/Sales/_files/quote_with_multiple_products.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/products_list.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
 | 
						|
     * @param array $positionData
 | 
						|
     * @param array $expectedProductLinks
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function testPositionCrosssellProductsWithLastAddedProduct(
 | 
						|
        array $positionData,
 | 
						|
        array $expectedProductLinks
 | 
						|
    ): void {
 | 
						|
        foreach ($positionData as $sku => $productLinks) {
 | 
						|
            $this->linkProducts($sku, $productLinks);
 | 
						|
        }
 | 
						|
        $this->checkoutSession->setLastAddedProductId($this->productRepository->get('simple-tableRate-1')->getId());
 | 
						|
        $items = $this->getBlockItems('tableRate');
 | 
						|
 | 
						|
        $this->assertCount(
 | 
						|
            self::MAX_ITEM_COUNT,
 | 
						|
            $items,
 | 
						|
            'Expected quantity of cross-sell products do not match the actual quantity!'
 | 
						|
        );
 | 
						|
        $this->assertEquals(
 | 
						|
            $expectedProductLinks,
 | 
						|
            $this->getActualLinks($items),
 | 
						|
            'Expected cross-sell products do not match actual cross-sell products!'
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Provide test data to verify the position of linked products of the last added product.
 | 
						|
     *
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    public function positionWithLastAddedProductProvider(): array
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            'less_four_linked_products_to_last_added_product' => [
 | 
						|
                'positionData' => [
 | 
						|
                    'simple-tableRate-1' => [
 | 
						|
                        'simple-249' => ['position' => 2],
 | 
						|
                        'simple-156' => ['position' => 1],
 | 
						|
                    ],
 | 
						|
                    'simple-tableRate-2' => [
 | 
						|
                        'simple-1' => ['position' => 2],
 | 
						|
                        'simple2' => ['position' => 1],
 | 
						|
                        'wrong-simple' => ['position' => 3],
 | 
						|
                    ],
 | 
						|
                ],
 | 
						|
                'expectedProductLinks' => [
 | 
						|
                    'simple-156',
 | 
						|
                    'simple-249',
 | 
						|
                    'simple2',
 | 
						|
                    'simple-1',
 | 
						|
                ],
 | 
						|
            ],
 | 
						|
            'four_linked_products_to_last_added_product' => [
 | 
						|
                'positionData' => [
 | 
						|
                    'simple-tableRate-1' => [
 | 
						|
                        'wrong-simple' => ['position' => 3],
 | 
						|
                        'simple-249' => ['position' => 1],
 | 
						|
                        'simple-156' => ['position' => 2],
 | 
						|
                        'simple2' => ['position' => 4],
 | 
						|
                    ],
 | 
						|
                    'simple-tableRate-2' => [
 | 
						|
                        'simple-1' => ['position' => 1],
 | 
						|
                    ],
 | 
						|
                ],
 | 
						|
                'expectedProductLinks' => [
 | 
						|
                    'simple-249',
 | 
						|
                    'simple-156',
 | 
						|
                    'wrong-simple',
 | 
						|
                    'simple2',
 | 
						|
                ],
 | 
						|
            ],
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Test the display of cross-sell products in the block on different websites
 | 
						|
     *
 | 
						|
     * @dataProvider multipleWebsitesLinkedProductsProvider
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/products_with_websites_and_stores.php
 | 
						|
     * @magentoDataFixture Magento/Sales/_files/quote_with_multiple_products.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/products_list.php
 | 
						|
     * @param array $data
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function testMultipleWebsitesCrosssellProducts(array $data): void
 | 
						|
    {
 | 
						|
        $this->updateProducts($this->prepareProductsWebsiteIds());
 | 
						|
        $productLinks = array_merge($this->existingProducts, $data['productLinks']);
 | 
						|
        $this->linkProducts('simple-tableRate-1', $productLinks);
 | 
						|
        $items = $this->executeInStoreContext->execute($data['storeCode'], [$this, 'getBlockItems'], 'tableRate');
 | 
						|
 | 
						|
        $this->assertEquals(
 | 
						|
            $data['expectedProductLinks'],
 | 
						|
            $this->getActualLinks($items),
 | 
						|
            'Expected cross-sell products do not match actual cross-sell products!'
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Test the invisibility of cross-sell products in the block which added to cart
 | 
						|
     *
 | 
						|
     * @magentoDataFixture Magento/Sales/_files/quote_with_multiple_products.php
 | 
						|
     * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function testInvisibilityCrosssellProductAddedToCart(): void
 | 
						|
    {
 | 
						|
        $productLinks = [
 | 
						|
            'simple-1' => ['position' => 1],
 | 
						|
            'simple-tableRate-2' => ['position' => 2],
 | 
						|
        ];
 | 
						|
        $this->linkProducts('simple-tableRate-1', $productLinks);
 | 
						|
        $items = $this->getBlockItems('tableRate');
 | 
						|
 | 
						|
        $this->assertEquals(
 | 
						|
            ['simple-1'],
 | 
						|
            $this->getActualLinks($items),
 | 
						|
            'Expected cross-sell products do not match actual cross-sell products!'
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get products of block when quote in checkout session
 | 
						|
     *
 | 
						|
     * @param string $reservedOrderId
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    public function getBlockItems(string $reservedOrderId): array
 | 
						|
    {
 | 
						|
        $this->setCheckoutSessionQuote($reservedOrderId);
 | 
						|
 | 
						|
        return $this->block->getItems();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @inheritdoc
 | 
						|
     */
 | 
						|
    protected function prepareBlock(): void
 | 
						|
    {
 | 
						|
        parent::prepareBlock();
 | 
						|
 | 
						|
        $this->block->setViewModel($this->objectManager->get(PreparePostData::class));
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @inheritdoc
 | 
						|
     */
 | 
						|
    protected function prepareProductsWebsiteIds(): array
 | 
						|
    {
 | 
						|
        $productsWebsiteIds = parent::prepareProductsWebsiteIds();
 | 
						|
        $simple = $productsWebsiteIds['simple-1'];
 | 
						|
        unset($productsWebsiteIds['simple-1']);
 | 
						|
 | 
						|
        return array_merge($productsWebsiteIds, ['simple-tableRate-1' => $simple]);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set quoteId in checkoutSession object.
 | 
						|
     *
 | 
						|
     * @param string $reservedOrderId
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    private function setCheckoutSessionQuote(string $reservedOrderId): void
 | 
						|
    {
 | 
						|
        $this->checkoutSession->clearQuote();
 | 
						|
        $quote = $this->objectManager->get(GetQuoteByReservedOrderId::class)->execute($reservedOrderId);
 | 
						|
        if ($quote !== null) {
 | 
						|
            $this->checkoutSession->setQuoteId($quote->getId());
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |