Skip to content

Commit 49e375f

Browse files
committed
Merge branch '2.4-develop' of github.com:magento-engcom/magento2ce into region-updater-magento2-32996
2 parents 0a1c487 + 9728fe3 commit 49e375f

File tree

333 files changed

+26257
-4269
lines changed

Some content is hidden

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

333 files changed

+26257
-4269
lines changed

.github/CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ This will allow you to collaborate with the Magento 2 development team, fork the
3333
1. Search current [listed issues](https://github.com/magento/magento2/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution.
3434
2. Review the [Contributor License Agreement](https://opensource.adobe.com/cla.html) if this is your first time contributing.
3535
3. Create and test your work.
36-
4. Fork the Magento 2 repository according to the [Fork A Repository instructions](https://devdocs.magento.com/guides/v2.4/contributor-guide/contributing.html#fork) and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](https://devdocs.magento.com/guides/v2.4/contributor-guide/contributing.html#pull_request).
36+
4. Follow the [Forks And Pull Requests Instructions](https://devdocs.magento.com/contributor-guide/contributing.html#forks-and-pull-requests) to fork the Magento 2 repository and send us a pull request.
3737
5. Once your contribution is received the Magento 2 development team will review the contribution and collaborate with you as needed.
3838

3939
## Code of Conduct
@@ -43,6 +43,6 @@ The full text is available in the repository [Wiki](https://github.com/magento/m
4343

4444
## Connecting with Community!
4545

46-
If you have any questions, join us in [#beginners](https://magentocommeng.slack.com/messages/CH8BGFX9D) Slack chat. If you are not on our slack, [click here](http://tinyurl.com/engcom-slack) to join.
46+
If you have any questions, join us in [#beginners](https://magentocommeng.slack.com/archives/CH8BGFX9D) Slack chat. If you are not on our slack, [click here](http://tinyurl.com/engcom-slack) to join.
4747

4848
Need to find a project? Check out the [Slack Channels](https://github.com/magento/magento2/wiki/Slack-Channels) (with listed project info) and the [Magento Community Portal](https://opensource.magento.com/).

app/code/Magento/AsynchronousOperations/Model/BulkManagement.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,14 @@ public function scheduleBulk($bulkUuid, array $operations, $description, $userId
119119
$bulkSummary->setOperationCount((int)$bulkSummary->getOperationCount() + count($operations));
120120
$this->entityManager->save($bulkSummary);
121121

122+
$this->publishOperations($operations);
123+
122124
$connection->commit();
123125
} catch (\Exception $exception) {
124126
$connection->rollBack();
125127
$this->logger->critical($exception->getMessage());
126128
return false;
127129
}
128-
$this->publishOperations($operations);
129130

130131
return true;
131132
}

app/code/Magento/AsynchronousOperations/Model/MassSchedule.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,17 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $
166166
}
167167
}
168168

169-
$this->saveMultipleOperations->execute($operations);
170169
if (!$this->bulkManagement->scheduleBulk($groupId, $operations, $bulkDescription, $userId)) {
171-
throw new LocalizedException(
172-
__('Something went wrong while processing the request.')
173-
);
170+
try {
171+
$this->bulkManagement->deleteBulk($groupId);
172+
} finally {
173+
throw new LocalizedException(
174+
__('Something went wrong while processing the request.')
175+
);
176+
}
174177
}
178+
$this->saveMultipleOperations->execute($operations);
179+
175180
/** @var AsyncResponseInterface $asyncResponse */
176181
$asyncResponse = $this->asyncResponseFactory->create();
177182
$asyncResponse->setBulkUuid($groupId);

app/code/Magento/AsynchronousOperations/Test/Unit/Model/BulkManagementTest.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,85 @@ public function testScheduleBulkWithException()
186186
$this->assertFalse($this->bulkManagement->scheduleBulk($bulkUuid, [$operation], $description, $userId));
187187
}
188188

189+
/**
190+
* Test for scheduleBulk method with exception during publishing.
191+
*
192+
* @return void
193+
*/
194+
public function testScheduleBulkWithExceptionDuringPublishing()
195+
{
196+
$bulkUuid = 'bulk-001';
197+
$description = 'Bulk summary description...';
198+
$userId = 1;
199+
$userType = UserContextInterface::USER_TYPE_ADMIN;
200+
$connectionName = 'default';
201+
$exceptionMessage = 'Exception message';
202+
$operation = $this->createMock(OperationInterface::class);
203+
$metadata = $this->createMock(EntityMetadataInterface::class);
204+
$this->metadataPool->expects($this->once())
205+
->method('getMetadata')
206+
->with(BulkSummaryInterface::class)
207+
->willReturn($metadata);
208+
$metadata->expects($this->once())
209+
->method('getEntityConnectionName')
210+
->willReturn($connectionName);
211+
$connection = $this->createMock(AdapterInterface::class);
212+
$this->resourceConnection->expects($this->once())
213+
->method('getConnectionByName')
214+
->with($connectionName)
215+
->willReturn($connection);
216+
$connection->expects($this->once())
217+
->method('beginTransaction')
218+
->willReturnSelf();
219+
$bulkSummary = $this->createMock(BulkSummaryInterface::class);
220+
$this->bulkSummaryFactory->expects($this->once())
221+
->method('create')
222+
->willReturn($bulkSummary);
223+
$this->entityManager->expects($this->once())
224+
->method('load')
225+
->with($bulkSummary, $bulkUuid)
226+
->willReturn($bulkSummary);
227+
$bulkSummary->expects($this->once())
228+
->method('setBulkId')
229+
->with($bulkUuid)
230+
->willReturnSelf();
231+
$bulkSummary->expects($this->once())
232+
->method('setDescription')
233+
->with($description)
234+
->willReturnSelf();
235+
$bulkSummary->expects($this->once())
236+
->method('setUserId')
237+
->with($userId)
238+
->willReturnSelf();
239+
$bulkSummary->expects($this->once())
240+
->method('setUserType')
241+
->with($userType)
242+
->willReturnSelf();
243+
$bulkSummary->expects($this->once())
244+
->method('getOperationCount')
245+
->willReturn(1);
246+
$bulkSummary->expects($this->once())
247+
->method('setOperationCount')
248+
->with(2)
249+
->willReturnSelf();
250+
$this->entityManager->expects($this->once())
251+
->method('save')
252+
->with($bulkSummary)
253+
->willReturn($bulkSummary);
254+
$this->publisher->expects($this->once())
255+
->method('publish')
256+
->willThrowException(new \Exception($exceptionMessage));
257+
$connection->expects($this->never())
258+
->method('commit');
259+
$connection->expects($this->once())
260+
->method('rollBack')
261+
->willReturnSelf();
262+
$this->logger->expects($this->once())
263+
->method('critical')
264+
->with($exceptionMessage);
265+
$this->assertFalse($this->bulkManagement->scheduleBulk($bulkUuid, [$operation], $description, $userId));
266+
}
267+
189268
/**
190269
* Test for retryBulk method.
191270
*

app/code/Magento/AwsS3/Test/Mftf/Test/AwsS3StorefrontPrintOrderGuestTest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
<description value="Print Order from Guest on Frontend"/>
1616
<severity value="BLOCKER"/>
1717
<testCaseId value="MC-38689"/>
18+
<skip>
19+
<issueId value="MQE-2834" />
20+
</skip>
1821
<group value="remote_storage_aws_s3"/>
1922
</annotations>
2023
<before>

app/code/Magento/Backend/Test/Mftf/Helper/CurlHelpers.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ public function assertImageContentIsEqual(
5757
): void {
5858
$cookie = $this->getCookie($cookieName);
5959
$imageContent = $this->getCurlResponse($url, $cookie, $postBody);
60-
// Must make request twice until bug is resolved: B2B-1789
61-
$imageContent = $this->getCurlResponse($url, $cookie, $postBody);
6260
// md5() here is not for cryptographic use.
6361
// phpcs:ignore Magento2.Security.InsecureFunction
6462
$imageContentMD5 = md5($imageContent);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,7 @@
4848
<element name="currency" type="select" selector="//a[text()='{{arg}}']" parameterized="true"/>
4949
<element name="multiSelectOption" type="select" selector="//div[@class='field option required']//select"/>
5050
<element name="validationMessageBox" type="block" selector="#validation-message-box"/>
51+
<element name="dropDownQuantityValidation" type="input" selector="//span[contains(text(), '{{productName}}')]/../..//input/following-sibling::div[@class='mage-error']" parameterized="true"/>
52+
<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"/>
5153
</section>
5254
</sections>

app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,23 +104,23 @@
104104
<selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct1.name$$ +$$$simpleProduct1.price$$.00" stepKey="selectOption0Product0"/>
105105
<seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct1.name$$ +$$$simpleProduct1.price$$.00" stepKey="checkOption0Product0"/>
106106
<fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="fillQuantity00"/>
107-
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="03" stepKey="checkQuantity00"/>
107+
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="checkQuantity00"/>
108108

109109
<selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct2.name$$ +$$$simpleProduct2.price$$.00" stepKey="selectOption0Product1"/>
110110
<seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$simpleProduct2.name$$ +$$$simpleProduct2.price$$.00" stepKey="checkOption0Product1"/>
111111
<fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="fillQuantity01"/>
112-
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="03" stepKey="checkQuantity01"/>
112+
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="3" stepKey="checkQuantity01"/>
113113

114114
<!--"Radio Buttons" type option-->
115115
<checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="selectOption1Product0"/>
116116
<seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="checkOption1Product0"/>
117117
<fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="fillQuantity10"/>
118-
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="03" stepKey="checkQuantity10"/>
118+
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="checkQuantity10"/>
119119

120120
<checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '2')}}" stepKey="selectOption1Product1"/>
121121
<seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '2')}}" stepKey="checkOption1Product1"/>
122122
<fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="fillQuantity11"/>
123-
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="03" stepKey="checkQuantity11"/>
123+
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="3" stepKey="checkQuantity11"/>
124124

125125
<!--"Checkbox" type option-->
126126
<!--This option does not support user defined quantities-->
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
10+
<test name="StorefrontValidateQuantityBundleProductsTest">
11+
<annotations>
12+
<features value="Bundle"/>
13+
<stories value="Bundle product details page"/>
14+
<title value="Validation for negative quantity on bundle products."/>
15+
<description value="Customer should not be able to add the products to the cart if the quantity is negative value"/>
16+
<severity value="MINOR"/>
17+
<testCaseId value="MC-42765"/>
18+
<group value="Bundle"/>
19+
</annotations>
20+
<before>
21+
<actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
22+
<createData entity="SimpleProduct2" stepKey="createProduct1"/>
23+
<createData entity="SimpleProduct2" stepKey="createProduct2"/>
24+
<magentoCron stepKey="runCronIndex" groups="index"/>
25+
</before>
26+
<after>
27+
<!-- Delete the bundled product -->
28+
<actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup">
29+
<argument name="product" value="BundleProduct"/>
30+
</actionGroup>
31+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
32+
<deleteData createDataKey="createProduct1" stepKey="deleteSimpleProduct1"/>
33+
<deleteData createDataKey="createProduct2" stepKey="deleteSimpleProduct2"/>
34+
</after>
35+
36+
<!-- Start creating a bundle product -->
37+
<actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="goToProductList"/>
38+
<actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct">
39+
<argument name="product" value="BundleProduct"/>
40+
</actionGroup>
41+
<actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku">
42+
<argument name="product" value="BundleProduct"/>
43+
</actionGroup>
44+
45+
<!-- Add Option One, a "Drop-down" type option -->
46+
<actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1">
47+
<argument name="x" value="0"/>
48+
<argument name="n" value="1"/>
49+
<argument name="prodOneSku" value="$$createProduct1.sku$$"/>
50+
<argument name="prodTwoSku" value="$$createProduct2.sku$$"/>
51+
<argument name="optionTitle" value="Option One"/>
52+
<argument name="inputType" value="select"/>
53+
</actionGroup>
54+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '0')}}" stepKey="userDefinedQuantitiyOption0Product0"/>
55+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '1')}}" stepKey="userDefinedQuantitiyOption0Product1"/>
56+
57+
<!-- Add Option Two, a "Radio Buttons" type option -->
58+
<actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2">
59+
<argument name="x" value="1"/>
60+
<argument name="n" value="2"/>
61+
<argument name="prodOneSku" value="$$createProduct1.sku$$"/>
62+
<argument name="prodTwoSku" value="$$createProduct2.sku$$"/>
63+
<argument name="optionTitle" value="Option Two"/>
64+
<argument name="inputType" value="radio"/>
65+
</actionGroup>
66+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('1', '0')}}" stepKey="userDefinedQuantitiyOption1Product0"/>
67+
<checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('1', '1')}}" stepKey="userDefinedQuantitiyOption1Product1"/>
68+
69+
<!-- Save product and go to storefront -->
70+
<actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
71+
72+
<wait stepKey="waitBeforeIndexerAfterBundle" time="60"/>
73+
<magentoCLI command="cron:run --group=index" stepKey="runCronIndexerAfterBundle"/>
74+
75+
<amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/>
76+
<waitForPageLoad stepKey="waitForStorefront"/>
77+
<click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/>
78+
79+
<!--"Drop-down" type option-->
80+
<selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$createProduct1.name$$ +$$$createProduct1.price$$.00" stepKey="selectOption0Product0"/>
81+
<seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionOneProducts('Option One')}}" userInput="$$createProduct1.name$$ +$$$createProduct1.price$$.00" stepKey="checkOption0Product0"/>
82+
<fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="-1" stepKey="fillQuantity00"/>
83+
<seeInField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity('Option One')}}" userInput="-1" stepKey="checkQuantity00"/>
84+
<click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddToCartDropDown"/>
85+
<see selector="{{StorefrontBundledSection.dropDownQuantityValidation('Option One')}}" userInput="Please enter a quantity greater than 0." stepKey="seeQuantityNegativeErrorMessageDropDown"/>
86+
87+
<!--"Radio Buttons" type option-->
88+
<checkOption selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="selectOption1Product0"/>
89+
<seeCheckboxIsChecked selector="{{StorefrontBundledSection.radioButtonOptionTwoProducts('Option Two', '1')}}" stepKey="checkOption1Product0"/>
90+
<fillField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="-1" stepKey="fillQuantity10"/>
91+
<seeInField selector="{{StorefrontBundledSection.radioButtonOptionTwoQuantity('Option Two')}}" userInput="-1" stepKey="checkQuantity10"/>
92+
<click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddToCartRadioButton"/>
93+
<see selector="{{StorefrontBundledSection.radioButtonQuantityValidation('Option Two')}}" userInput="Please enter a quantity greater than 0." stepKey="seeQuantityNegativeErrorMessageRadioButton"/>
94+
</test>
95+
</tests>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Bundle\ViewModel;
9+
10+
use Magento\Framework\Serialize\Serializer\Json;
11+
use Magento\Framework\View\Element\Block\ArgumentInterface;
12+
use Magento\Catalog\Block\Product\View as ProductView;
13+
14+
/**
15+
* ViewModel for Bundle Option Block
16+
*/
17+
class ValidateQuantity implements ArgumentInterface
18+
{
19+
/**
20+
* @var Json
21+
*/
22+
private $serializer;
23+
24+
/**
25+
* @var ProductView
26+
*/
27+
private $productView;
28+
29+
/**
30+
* @param Json $serializer
31+
* @param ProductView $productView
32+
*/
33+
public function __construct(
34+
Json $serializer,
35+
ProductView $productView
36+
) {
37+
$this->serializer = $serializer;
38+
$this->productView = $productView;
39+
}
40+
41+
public function getQuantityValidators(): string
42+
{
43+
return $this->serializer->serialize(
44+
$this->productView->getQuantityValidators()
45+
);
46+
}
47+
}

0 commit comments

Comments
 (0)