Skip to content

Commit 8151ba3

Browse files
committed
Merge branch '2.4-develop' of https://github.com/magento-gl/magento2ce into ACQE-8258
2 parents 6f03548 + 21390c6 commit 8151ba3

File tree

74 files changed

+2081
-323
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+2081
-323
lines changed

.github/app-projects-boards-automation.config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Copyright 2025 Adobe
2+
# All Rights Reserved.
3+
14
automations:
25

36
############################################################################################################
@@ -401,3 +404,10 @@ automations:
401404
]
402405
actions:
403406
- addLabelsToRelated: ['${modifiedLabel.name}']
407+
408+
- trigger: issues.closed
409+
actions:
410+
- removeFromProject: [18]
411+
- removeFromProject: [21]
412+
- removeFromProject: [20]
413+
- removeFromProject: [19]

app/code/Magento/AwsS3/Driver/AwsS3.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ private function createDirectoryRecursively(string $path): bool
188188
$parentDir = dirname($path);
189189

190190
while (!$this->isDirectory($parentDir)) {
191-
$this->createDirectoryRecursively($parentDir);
191+
if (!$this->createDirectoryRecursively($parentDir)) {
192+
return false;
193+
}
192194
}
193195

194196
if (!$this->isDirectory($path)) {

app/code/Magento/AwsS3/Test/Unit/Driver/AwsS3Test.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
/**
2020
* @see AwsS3
21+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2122
*/
2223
class AwsS3Test extends TestCase
2324
{
@@ -497,6 +498,23 @@ public function testCreateDirectory(): void
497498
self::assertTrue($this->driver->createDirectory(self::URL . 'test/test2/'));
498499
}
499500

501+
/**
502+
* This test ensures that the method does not loop infinitely in case of an exception
503+
*
504+
* @return void
505+
* @throws FileSystemException
506+
* @throws \PHPUnit\Framework\MockObject\Exception
507+
*/
508+
public function testShouldFailSafelyIfUnableToCreateDirectory(): void
509+
{
510+
$this->adapterMock->expects(self::once())
511+
->method('createDirectory')
512+
->willThrowException($this->createMock(\League\Flysystem\FilesystemException::class))
513+
->with('test');
514+
515+
self::assertFalse($this->driver->createDirectory(self::URL . 'test/test2/'));
516+
}
517+
500518
public function testRename(): void
501519
{
502520
$this->adapterMock->expects(self::once())

app/code/Magento/Backend/Block/Store/Switcher.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,15 @@ public function getCurrentSelectionName()
457457
if ($this->getCurrentWebsiteName() !== '') {
458458
return $this->getCurrentWebsiteName();
459459
}
460+
461+
if (!$this->hasDefaultOption()) {
462+
$websites = $this->getWebsites();
463+
if (!empty($websites)) {
464+
$websiteArray = array_values($websites);
465+
return $websiteArray[0]->getName();
466+
}
467+
}
468+
460469
return $this->getDefaultSelectionName();
461470
}
462471

app/code/Magento/Bundle/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Bundle.php

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
use Magento\Bundle\Api\Data\OptionInterfaceFactory as OptionFactory;
99
use Magento\Bundle\Api\Data\LinkInterfaceFactory as LinkFactory;
1010
use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory;
11+
use Magento\Catalog\Api\Data\ProductInterface;
1112
use Magento\Catalog\Api\ProductRepositoryInterface as ProductRepository;
1213
use Magento\Store\Model\StoreManagerInterface as StoreManager;
1314
use Magento\Framework\App\RequestInterface;
1415

1516
/**
16-
* Class Bundle
17+
* Plugin class to initialize Bundle product
18+
*
1719
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1820
*/
1921
class Bundle
@@ -106,12 +108,14 @@ public function afterInitialize(
106108
$product->setBundleOptionsData($result['bundle_options']);
107109
}
108110

111+
if (!$result['bundle_selections']) {
112+
$this->resetBundleProductOptions($product);
113+
}
114+
109115
$this->processBundleOptionsData($product);
110116
$this->processDynamicOptionsData($product);
111117
} elseif (!$compositeReadonly) {
112-
$extension = $product->getExtensionAttributes();
113-
$extension->setBundleProductOptions([]);
114-
$product->setExtensionAttributes($extension);
118+
$this->resetBundleProductOptions($product);
115119
}
116120

117121
$affectProductSelections = (bool)$this->request->getPost('affect_bundle_product_selections');
@@ -120,6 +124,8 @@ public function afterInitialize(
120124
}
121125

122126
/**
127+
* Process Bundle Options Data
128+
*
123129
* @param \Magento\Catalog\Model\Product $product
124130
* @return void
125131
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -161,10 +167,11 @@ protected function processBundleOptionsData(\Magento\Catalog\Model\Product $prod
161167
$extension = $product->getExtensionAttributes();
162168
$extension->setBundleProductOptions($options);
163169
$product->setExtensionAttributes($extension);
164-
return;
165170
}
166171

167172
/**
173+
* Process Dynamic Options Data
174+
*
168175
* @param \Magento\Catalog\Model\Product $product
169176
* @return void
170177
*/
@@ -198,9 +205,10 @@ protected function processDynamicOptionsData(\Magento\Catalog\Model\Product $pro
198205
}
199206

200207
/**
208+
* Build product link
209+
*
201210
* @param \Magento\Catalog\Model\Product $product
202211
* @param array $linkData
203-
*
204212
* @return \Magento\Bundle\Api\Data\LinkInterface
205213
*/
206214
private function buildLink(
@@ -228,4 +236,18 @@ private function buildLink(
228236

229237
return $link;
230238
}
239+
240+
/**
241+
* Resets bundle product options inside product extension attributes
242+
*
243+
* @param ProductInterface $product
244+
* @return void
245+
*/
246+
private function resetBundleProductOptions(ProductInterface $product) : void
247+
{
248+
$extension = $product->getExtensionAttributes();
249+
$extension->setBundleProductOptions([]);
250+
$product->setExtensionAttributes($extension);
251+
$product->setDropOptions(true);
252+
}
231253
}

app/code/Magento/Bundle/Model/Product/SaveHandler.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ public function execute($entity, $arguments = [])
9696
/** @var OptionInterface[] $bundleProductOptions */
9797
$bundleProductOptions = $entity->getExtensionAttributes()->getBundleProductOptions() ?: [];
9898
//Only processing bundle products.
99-
if ($entity->getTypeId() !== Type::TYPE_CODE || empty($bundleProductOptions)) {
99+
if ($entity->getTypeId() !== Type::TYPE_CODE
100+
|| (empty($bundleProductOptions) && !$entity->getDropOptions())
101+
) {
100102
return $entity;
101103
}
102104

app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,7 @@
5252
<element name="radioButtonQuantityValidation" type="input" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//div[@class='field qty qty-holder']//input/following-sibling::div[@class='mage-error']" parameterized="true"/>
5353
<element name="dropDrownOptionQuantity" type="input" selector="//span[contains(text(), '{{productName}}')]/../..//input/following-sibling::div//div//div//input" parameterized="true"/>
5454
<element name="selectOptionError" type="text" selector="//div[contains(@class, 'field')]//div[contains(@class, 'mage-error')]"/>
55+
<element name="radioButton" type="select" selector="//label//span[contains(text(), '{{productName}}')]/ancestor::div//div[@class='control']//div[@class='field choice'][{{productNumber}}]/input" parameterized="true"/>
56+
<element name="radioOptionQty" type="input" selector="//div[@class='control']//div[@class='field qty qty-holder']//input"/>
5557
</section>
5658
</sections>
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright 2025 Adobe
5+
* All Rights Reserved.
6+
*/
7+
-->
8+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
9+
<test name="StorefrontEditBundleProductUserDefinedQtyFromCartTest">
10+
<annotations>
11+
<features value="Bundle Product"/>
12+
<stories value="Editing bundle product from cart with user-defined quantities"/>
13+
<title value="Edit bundle product from cart with user-defined quantities"/>
14+
<description value="Test verifies that user-defined are retained when editing a bundle product from the shopping cart"/>
15+
<severity value="MAJOR"/>
16+
<testCaseId value="AC-4271"/>
17+
<group value="catalog"/>
18+
</annotations>
19+
<before>
20+
<!-- Precondition1: Create two simple product with category -->
21+
<createData entity="SimpleSubCategory" stepKey="createSubCategory"/>
22+
<createData entity="SimpleProduct2" stepKey="createSimpleProduct1"/>
23+
<createData entity="SimpleProduct2" stepKey="createSimpleProduct2"/>
24+
<!-- Precondition2: Create bundle product with radio option -->
25+
<createData entity="ApiBundleProduct" stepKey="createBundleProduct">
26+
<requiredEntity createDataKey="createSubCategory"/>
27+
</createData>
28+
<createData entity="RadioButtonsOption" stepKey="bundleOption">
29+
<requiredEntity createDataKey="createBundleProduct"/>
30+
<field key="required">true</field>
31+
</createData>
32+
<createData entity="ApiBundleLink" stepKey="LinkOptionToFirstProduct">
33+
<requiredEntity createDataKey="createBundleProduct"/>
34+
<requiredEntity createDataKey="bundleOption"/>
35+
<requiredEntity createDataKey="createSimpleProduct1"/>
36+
<field key="qty">3</field>
37+
</createData>
38+
<createData entity="ApiBundleLink" stepKey="LinkOptionToSecondProduct">
39+
<requiredEntity createDataKey="createBundleProduct"/>
40+
<requiredEntity createDataKey="bundleOption"/>
41+
<requiredEntity createDataKey="createSimpleProduct2"/>
42+
<field key="qty">3</field>
43+
</createData>
44+
<!-- Login as admin -->
45+
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
46+
<actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openBundleEditPage">
47+
<argument name="productId" value="$$createBundleProduct.id$$"/>
48+
</actionGroup>
49+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '0')}}" stepKey="userDefinedQuantityOption0Product0"/>
50+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '1')}}" stepKey="userDefinedQuantityOption0Product1"/>
51+
<!-- Save product -->
52+
<actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
53+
</before>
54+
<after>
55+
<deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/>
56+
<deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/>
57+
<deleteData createDataKey="createSubCategory" stepKey="deleteSubCategory"/>
58+
<deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/>
59+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
60+
</after>
61+
<!-- Step1: Open Bundled product on storefront -->
62+
<actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="navigateToBundleProductDetailsPage">
63+
<argument name="product" value="$createBundleProduct$"/>
64+
</actionGroup>
65+
<!-- Step2: Click on Customize and Add to cart-->
66+
<actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickButtonToCustomize"/>
67+
<checkOption selector="{{StorefrontBundledSection.radioButton('$$createSimpleProduct1.name$$', '1')}}" stepKey="selectOption1"/>
68+
<waitForElementVisible selector="{{StorefrontBundledSection.radioOptionQty}}" stepKey="waitForUpdateQty"/>
69+
<!-- Step3: Update qty and add product to cart -->
70+
<fillField selector="{{StorefrontBundledSection.radioOptionQty}}" userInput="10" stepKey="updateQty"/>
71+
<actionGroup ref="StorefrontAddToTheCartButtonActionGroup" stepKey="addToTheCartBundleProduct"/>
72+
<!-- Step4: Open Mini Cart and click on edit item icon and verify qty -->
73+
<actionGroup ref="StorefrontOpenMiniCartActionGroup" stepKey="openMiniCart"/>
74+
<click selector="{{StorefrontMinicartSection.editMiniCartItem}}" stepKey="clickEditCartItem"/>
75+
<waitForPageLoad stepKey="waitForPageToLoad"/>
76+
<seeInField selector="{{StorefrontBundledSection.radioOptionQty}}" userInput="010" stepKey="VerifyQty"/>
77+
</test>
78+
</tests>

app/code/Magento/Bundle/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/BundleTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ public function testAfterInitializeIfBundleSelectionsAndCustomOptionsExist()
173173
->method('setBundleOptionsData')
174174
->with($this->bundleOptionsCleaned);
175175
$this->productMock->expects($this->never())->method('setBundleSelectionsData');
176+
$extensionAttribute = $this->getMockBuilder(ProductExtensionInterface::class)
177+
->disableOriginalConstructor()
178+
->addMethods(['setBundleProductOptions'])
179+
->getMockForAbstractClass();
180+
$extensionAttribute->expects($this->once())->method('setBundleProductOptions')->with([]);
181+
$this->productMock->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttribute);
182+
$this->productMock->expects($this->once())->method('setExtensionAttributes')->with($extensionAttribute);
176183
$this->productMock->expects($this->once())->method('getPriceType')->willReturn(2);
177184
$this->productMock->expects($this->any())->method('getOptionsReadonly')->willReturn(true);
178185
$this->productMock->expects($this->once())->method('setCanSaveBundleSelections')->with(false);

app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ public function execute()
143143
$this->validateProductAttributes($attributesData);
144144
$this->publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds);
145145
$this->messageManager->addSuccessMessage(__('Message is added to queue'));
146+
$this->attributeHelper->setProductIds([]);
146147
} catch (LocalizedException $e) {
147148
$this->messageManager->addErrorMessage($e->getMessage());
148149
} catch (\Exception $e) {

0 commit comments

Comments
 (0)