1414use Magento \Catalog \Model \ProductRepository ;
1515use Magento \Catalog \Model \ResourceModel \Eav \Attribute ;
1616use Magento \Eav \Model \Config ;
17+ use Magento \Framework \App \Filesystem \DirectoryList ;
1718use Magento \Framework \DataObject ;
1819use Magento \Framework \Event \ManagerInterface ;
1920use Magento \Framework \Exception \LocalizedException ;
21+ use Magento \Framework \File \Uploader ;
22+ use Magento \Framework \File \UploaderFactory ;
2023use Magento \Framework \Filesystem ;
24+ use Magento \Framework \HTTP \Adapter \FileTransferFactory ;
2125use Magento \Framework \Registry ;
2226use Magento \Framework \Serialize \Serializer \Json ;
2327use Magento \MediaStorage \Helper \File \Storage \Database ;
2428use Magento \TestFramework \Helper \Bootstrap ;
29+ use Magento \TestFramework \ObjectManager ;
2530use PHPUnit \Framework \TestCase ;
2631use Psr \Log \LoggerInterface ;
32+ use ReflectionMethod ;
33+ use StdClass ;
2734
2835/**
2936 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -35,35 +42,41 @@ class AbstractTypeTest extends TestCase
3542 */
3643 protected $ _model ;
3744
45+ /** @var ObjectManager */
46+ private $ objectManager ;
47+
48+ /** @var ProductRepositoryInterface */
49+ private $ productRepository ;
50+
51+ /**
52+ * @inheritdoc
53+ */
3854 protected function setUp (): void
3955 {
40- $ productRepository = Bootstrap::getObjectManager ()->get (
41- ProductRepositoryInterface::class
42- );
43- $ catalogProductOption = Bootstrap::getObjectManager ()->get (
44- Option::class
45- );
56+ $ this ->objectManager = Bootstrap::getObjectManager ();
57+ $ this ->productRepository = $ this ->objectManager ->get (ProductRepositoryInterface::class);
58+ $ catalogProductOption = $ this ->objectManager ->get (Option::class);
4659 $ catalogProductType = $ this ->createMock (Type::class);
4760 $ eventManager = $ this ->createPartialMock (ManagerInterface::class, ['dispatch ' ]);
4861 $ fileStorageDb = $ this ->createMock (Database::class);
4962 $ filesystem = $ this ->createMock (Filesystem::class);
5063 $ registry = $ this ->createMock (Registry::class);
5164 $ logger = $ this ->createMock (LoggerInterface::class);
52- $ serializer = Bootstrap:: getObjectManager () ->get (
65+ $ serializer = $ this -> objectManager ->get (
5366 Json::class
5467 );
5568 $ this ->_model = $ this ->getMockForAbstractClass (
5669 AbstractType::class,
5770 [
5871 $ catalogProductOption ,
59- Bootstrap:: getObjectManager () ->get (Config::class),
72+ $ this -> objectManager ->get (Config::class),
6073 $ catalogProductType ,
6174 $ eventManager ,
6275 $ fileStorageDb ,
6376 $ filesystem ,
6477 $ registry ,
6578 $ logger ,
66- $ productRepository ,
79+ $ this -> productRepository ,
6780 $ serializer
6881 ]
6982 );
@@ -384,7 +397,7 @@ public function testGetSetStoreFilter()
384397 {
385398 $ product = new DataObject ();
386399 $ this ->assertNull ($ this ->_model ->getStoreFilter ($ product ));
387- $ store = new \ StdClass ();
400+ $ store = new StdClass ();
388401 $ this ->_model ->setStoreFilter ($ store , $ product );
389402 $ this ->assertSame ($ store , $ this ->_model ->getStoreFilter ($ product ));
390403 }
@@ -473,7 +486,7 @@ public function testGetSearchableData()
473486
474487 public function testGetProductsToPurchaseByReqGroups ()
475488 {
476- $ product = new \ StdClass ();
489+ $ product = new StdClass ();
477490 $ this ->assertSame ([[$ product ]], $ this ->_model ->getProductsToPurchaseByReqGroups ($ product ));
478491 $ this ->_model ->setConfig (['composite ' => 1 ]);
479492 $ this ->assertEquals ([], $ this ->_model ->getProductsToPurchaseByReqGroups ($ product ));
@@ -509,7 +522,7 @@ public function testPrepareOptions(): void
509522 );
510523 $ product ->load (1 );
511524 $ buyRequest = new DataObject (['product ' => 1 ]);
512- $ method = new \ ReflectionMethod (
525+ $ method = new ReflectionMethod (
513526 AbstractType::class,
514527 '_prepareOptions '
515528 );
@@ -523,4 +536,150 @@ public function testPrepareOptions(): void
523536 }
524537 $ this ->assertTrue ($ exceptionIsThrown );
525538 }
539+
540+ /**
541+ * Test if product can be prepared for cart with more than one custom file option
542+ *
543+ * @magentoAppIsolation enabled
544+ * @magentoDataFixture Magento/Catalog/_files/product_simple_with_two_custom_file_options.php
545+ * @return void
546+ */
547+ public function testPrepareForCartAdvancedWithMultipleCustomFileOptions (): void
548+ {
549+ /** @var $product Product */
550+ $ product = $ this ->productRepository ->get ('simple_with_custom_file_option ' );
551+ $ optionIds = array_reduce ($ product ->getOptions (), function ($ result , $ item ) {
552+ $ result [] = $ item ->getOptionId ();
553+ return $ result ;
554+ }, []);
555+ $ this ->prepareEnv ($ optionIds );
556+
557+ $ buyRequest = new DataObject (
558+ [
559+ 'qty ' => 5 ,
560+ 'options ' => ['files_prefix ' => 'item_simple_with_custom_file_option_ ' ],
561+ ]
562+ );
563+ $ model = $ this ->objectManager ->create (Simple::class);
564+ $ product ->setTypeInstance ($ model );
565+ $ result = $ model ->prepareForCartAdvanced ($ buyRequest , $ product );
566+
567+ //result is exception string value in case if exception occurs
568+ self ::assertTrue (is_array ($ result ));
569+ $ product = reset ($ result );
570+ $ options = $ product ->getOptions ();
571+ self ::assertCount (2 , $ options );
572+ }
573+
574+ /**
575+ * Test if exception occurs in case if file is not uploaded
576+ *
577+ * @magentoAppIsolation enabled
578+ * @magentoDataFixture Magento/Catalog/_files/product_simple_with_two_custom_file_options.php
579+ * @return void
580+ */
581+ public function testPrepareForCartAdvancedFileOptionFailedToUpload (): void
582+ {
583+ /** @var $product Product */
584+ $ product = $ this ->productRepository ->get ('simple_with_custom_file_option ' );
585+ $ optionIds = array_reduce ($ product ->getOptions (), function ($ result , $ item ) {
586+ $ result [] = $ item ->getOptionId ();
587+ return $ result ;
588+ }, []);
589+ $ this ->prepareEnv ($ optionIds );
590+
591+ $ uploaderFactory = $ this ->createPartialMock (UploaderFactory::class, ['create ' ]);
592+ $ uploader = $ this ->createPartialMock (Uploader::class, ['save ' ]);
593+ $ uploaderFactory ->method ('create ' )->willReturn ($ uploader );
594+ $ this ->objectManager ->addSharedInstance ($ uploaderFactory , UploaderFactory::class);
595+
596+ $ buyRequest = new DataObject (
597+ [
598+ 'qty ' => 5 ,
599+ 'options ' => ['files_prefix ' => 'item_simple_with_custom_file_option_ ' ],
600+ ]
601+ );
602+ $ model = $ this ->objectManager ->create (Simple::class);
603+ $ product ->setTypeInstance ($ model );
604+ $ this ->expectException (LocalizedException::class);
605+ $ model ->prepareForCartAdvanced ($ buyRequest , $ product );
606+ }
607+
608+ /**
609+ * Prepare file upload environment
610+ *
611+ * @param array $optionIds
612+ * @return void
613+ */
614+ private function prepareEnv (array $ optionIds ): void
615+ {
616+ $ file = 'magento_thumbnail.jpg ' ;
617+ $ fixtureDir = realpath (__DIR__ . '/../../../_files/ ' );
618+
619+ /** @var Filesystem $filesystem */
620+ $ filesystem = $ this ->objectManager ->get (Filesystem::class);
621+ $ tmpDirectory = $ filesystem ->getDirectoryWrite (DirectoryList::SYS_TMP );
622+ $ filePath = $ tmpDirectory ->getAbsolutePath ($ file );
623+ copy ($ fixtureDir . DIRECTORY_SEPARATOR . $ file , $ filePath );
624+ copy ($ fixtureDir . DIRECTORY_SEPARATOR . $ file , $ filePath . '_1 ' );
625+
626+ $ _FILES ["item_simple_with_custom_file_option_options_ $ optionIds [0 ]_file " ] = [
627+ 'name ' => 'test.jpg ' ,
628+ 'type ' => 'image/jpeg ' ,
629+ 'tmp_name ' => $ filePath ,
630+ 'error ' => 0 ,
631+ 'size ' => '3046 ' ,
632+ ];
633+ $ _FILES ["item_simple_with_custom_file_option_options_ $ optionIds [1 ]_file " ] = [
634+ 'name ' => 'test.jpg ' ,
635+ 'type ' => 'image/jpeg ' ,
636+ 'tmp_name ' => $ filePath . '_1 ' ,
637+ 'error ' => 0 ,
638+ 'size ' => '3046 ' ,
639+ ];
640+ $ this ->prepareUploaderFactoryMock ();
641+ }
642+
643+ /**
644+ * Prepare file upload validator mock
645+ *
646+ * @return void
647+ */
648+ private function prepareUploaderFactoryMock (): void
649+ {
650+ $ uploaderMock = $ this ->getPreparedUploader ();
651+ /** @var FileTransferFactory $httpFactory */
652+ $ httpFactoryMock = $ this ->createPartialMock (FileTransferFactory::class, ['create ' ]);
653+ $ httpFactoryMock ->expects ($ this ->at (0 ))
654+ ->method ('create ' )
655+ ->willReturn ($ uploaderMock );
656+ $ httpFactoryMock ->expects ($ this ->at (1 ))
657+ ->method ('create ' )
658+ ->willReturn (clone $ uploaderMock );
659+ $ this ->objectManager ->addSharedInstance ($ httpFactoryMock , FileTransferFactory::class);
660+ }
661+
662+ /**
663+ * Create prepared uploader instance for test
664+ *
665+ * @return \Zend_File_Transfer_Adapter_Http
666+ * @SuppressWarnings(PHPMD.UnusedLocalVariable)
667+ */
668+ private function getPreparedUploader (): \Zend_File_Transfer_Adapter_Http
669+ {
670+ $ uploader = new \Zend_File_Transfer_Adapter_Http ();
671+ $ refObject = new \ReflectionObject ($ uploader );
672+ $ validators = $ refObject ->getProperty ('_validators ' );
673+ $ validators ->setAccessible (true );
674+ $ validators ->setValue ($ uploader , []);
675+ $ files = $ refObject ->getProperty ('_files ' );
676+ $ files ->setAccessible (true );
677+ $ filesValues = $ files ->getValue ($ uploader );
678+ foreach (array_keys ($ filesValues ) as $ value ) {
679+ $ filesValues [$ value ]['validators ' ] = [];
680+ }
681+ $ files ->setValue ($ uploader , $ filesValues );
682+
683+ return $ uploader ;
684+ }
526685}
0 commit comments