objectManager = Bootstrap::getObjectManager(); $this->model = $this->objectManager->create(Product::class); $this->model->setTypeId(Type::TYPE_BUNDLE); } /** * Tests Retrieve ans set type instance of the product * * @see \Magento\Catalog\Model\Product::getTypeInstance * @see \Magento\Catalog\Model\Product::setTypeInstance * @return void */ public function testGetSetTypeInstance() { // model getter $typeInstance = $this->model->getTypeInstance(); $this->assertInstanceOf(BundleType::class, $typeInstance); $this->assertSame($typeInstance, $this->model->getTypeInstance()); // singleton getter $otherProduct = $this->objectManager->create(Product::class); $otherProduct->setTypeId(Type::TYPE_BUNDLE); $this->assertSame($typeInstance, $otherProduct->getTypeInstance()); // model setter $customTypeInstance = $this->objectManager->create(BundleType::class); $this->model->setTypeInstance($customTypeInstance); $this->assertSame($customTypeInstance, $this->model->getTypeInstance()); } /** * @magentoDbIsolation enabled * @magentoAppIsolation enabled * @magentoAppArea adminhtml */ public function testCRUD() { $this->model->setTypeId(Type::TYPE_BUNDLE) ->setAttributeSetId(4) ->setName('Bundle Product') ->setSku(uniqid()) ->setPrice(10) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') ->setVisibility(Visibility::VISIBILITY_BOTH) ->setStatus(Status::STATUS_ENABLED); $crud = new Entity($this->model, ['sku' => uniqid()]); $crud->testCrud(); } /** * Tests Get product price model * * @see \Magento\Catalog\Model\Product::getPriceModel * @return void */ public function testGetPriceModel() { $this->model->setTypeId(Type::TYPE_BUNDLE); $type = $this->model->getPriceModel(); $this->assertInstanceOf(Price::class, $type); $this->assertSame($type, $this->model->getPriceModel()); } /** * Tests Check is product composite * * @see \Magento\Catalog\Model\Product::isComposite * @return void */ public function testIsComposite() { $this->assertTrue($this->model->isComposite()); } /** * Checks a case when bundle product is should be available per multiple stores. * * @magentoDataFixture Magento/Bundle/_files/product_with_multiple_options.php * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php * @magentoDbIsolation disabled */ public function testMultipleStores() { /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $bundle = $productRepository->get('bundle-product'); /** @var StoreRepositoryInterface $storeRepository */ $storeRepository = $this->objectManager->get(StoreRepositoryInterface::class); $store = $storeRepository->get('fixture_second_store'); self::assertNotEquals($store->getId(), $bundle->getStoreId()); /** @var StoreManagerInterface $storeManager */ $storeManager = $this->objectManager->get(StoreManagerInterface::class); $storeManager->setCurrentStore($store->getId()); $bundle->setStoreId($store->getId()) ->setCopyFromView(true); $updatedBundle = $productRepository->save($bundle); self::assertEquals($store->getId(), $updatedBundle->getStoreId()); } /** * @param float $selectionQty * @param float $qty * @param int $isInStock * @param bool $manageStock * @param int $backorders * @param bool $isSalable * @magentoAppIsolation enabled * @magentoDataFixture Magento/Bundle/_files/product.php * @dataProvider stockConfigDataProvider * @covers \Magento\Catalog\Model\Product::isSalable */ public function testIsSalable( float $selectionQty, float $qty, int $isInStock, bool $manageStock, int $backorders, bool $isSalable ) { $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $child = $productRepository->get('simple'); $childStockItem = $child->getExtensionAttributes()->getStockItem(); $childStockItem->setQty($qty); $childStockItem->setIsInStock($isInStock); $childStockItem->setUseConfigManageStock(false); $childStockItem->setManageStock($manageStock); $childStockItem->setUseConfigBackorders(false); $childStockItem->setBackorders($backorders); $productRepository->save($child); /** @var \Magento\Catalog\Model\Product $bundle */ $bundle = $productRepository->get('bundle-product'); foreach ($bundle->getExtensionAttributes()->getBundleProductOptions() as $productOption) { foreach ($productOption->getProductLinks() as $productLink) { $productLink->setCanChangeQuantity(0); $productLink->setQty($selectionQty); } } $productRepository->save($bundle); $this->assertEquals($isSalable, $bundle->isSalable()); } /** * @return array */ public function stockConfigDataProvider(): array { $qtyVars = [0, 10]; $isInStockVars = [ Stock::STOCK_OUT_OF_STOCK, Stock::STOCK_IN_STOCK, ]; $manageStockVars = [false, true]; $backordersVars = [ Stock::BACKORDERS_NO, Stock::BACKORDERS_YES_NONOTIFY, Stock::BACKORDERS_YES_NOTIFY, ]; $selectionQtyVars = [5, 10, 15]; $variations = []; foreach ($qtyVars as $qty) { foreach ($isInStockVars as $isInStock) { foreach ($manageStockVars as $manageStock) { foreach ($backordersVars as $backorders) { foreach ($selectionQtyVars as $selectionQty) { $variationName = "selectionQty: {$selectionQty}" . " qty: {$qty}" . " isInStock: {$isInStock}" . " manageStock: {$manageStock}" . " backorders: {$backorders}"; $isSalable = $this->checkIsSalable( $selectionQty, $qty, $isInStock, $manageStock, $backorders ); $variations[$variationName] = [ $selectionQty, $qty, $isInStock, $manageStock, $backorders, $isSalable ]; } } } } } return $variations; } /** * @param float $selectionQty * @param float $qty * @param int $isInStock * @param bool $manageStock * @param int $backorders * @return bool * @see \Magento\Bundle\Model\ResourceModel\Selection\Collection::addQuantityFilter */ private function checkIsSalable( float $selectionQty, float $qty, int $isInStock, bool $manageStock, int $backorders ): bool { return !$manageStock || ($isInStock && ($backorders || $selectionQty <= $qty)); } }