Skip to content

Commit 75bcc17

Browse files
authored
Merge branch '2.4-develop' into di_compile_area_order_fix
2 parents eb1e4d8 + bff94c8 commit 75bcc17

File tree

10 files changed

+730
-14
lines changed

10 files changed

+730
-14
lines changed

app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<description value="Verify that Catalog Price Rule and Customer Group Membership are persisted under long-term cookie"/>
1717
<severity value="CRITICAL"/>
1818
<testCaseId value="MC-27571"/>
19-
<group value="persistent"/>
19+
<group value="CatalogRule"/>
2020
</annotations>
2121
<before>
2222

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright 2026 Adobe
5+
* All Rights Reserved.
6+
*/
7+
-->
8+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
10+
<actionGroup name="StorefrontAssertCustomerRegistrationValidationMessagesActionGroup">
11+
<annotations>
12+
<description>Verify all required fields on customer registration form display validation error messages with correct text and CSS class.</description>
13+
</annotations>
14+
<!-- Verify First Name field validation -->
15+
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('firstname-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertFirstNameError"/>
16+
<waitForElementVisible selector="{{StorefrontCustomerCreateFormSection.fieldErrorWithClassByFieldId('firstname-error')}}" stepKey="assertFirstNameErrorClass"/>
17+
<!-- Verify Last Name field validation -->
18+
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('lastname-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertLastNameError"/>
19+
<waitForElementVisible selector="{{StorefrontCustomerCreateFormSection.fieldErrorWithClassByFieldId('lastname-error')}}" stepKey="assertLastNameErrorClass"/>
20+
<!-- Verify Email field validation -->
21+
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('email_address-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertEmailError"/>
22+
<waitForElementVisible selector="{{StorefrontCustomerCreateFormSection.fieldErrorWithClassByFieldId('email_address-error')}}" stepKey="assertEmailErrorClass"/>
23+
<!-- Verify Password field validation -->
24+
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('password-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertPasswordError"/>
25+
<waitForElementVisible selector="{{StorefrontCustomerCreateFormSection.fieldErrorWithClassByFieldId('password-error')}}" stepKey="assertPasswordErrorClass"/>
26+
<!-- Verify Password Confirmation field validation -->
27+
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('password-confirmation-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertPasswordConfirmationError"/>
28+
<waitForElementVisible selector="{{StorefrontCustomerCreateFormSection.fieldErrorWithClassByFieldId('password-confirmation-error')}}" stepKey="assertPasswordConfirmationErrorClass"/>
29+
</actionGroup>
30+
</actionGroups>
31+

app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection/StorefrontCustomerCreateFormSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020
<element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/>
2121
<element name="passwordErrorMessages" type="text" selector="#password-error"/>
2222
<element name="fieldErrorByFieldId" type="text" selector="//div[@id='{{fieldName}}' and contains (text(),'This is a required field.')]" parameterized="true"/>
23+
<element name="fieldErrorWithClassByFieldId" type="text" selector="//div[@id='{{fieldName}}' and contains(@class, 'mage-error')]" parameterized="true"/>
2324
</section>
2425
</sections>

app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyRegistrationFormValidationMessagesTest.xml

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<annotations>
1212
<features value="Customer"/>
1313
<stories value="Customer Registration Form Validation"/>
14-
<title value="Verify validation error messages are displayed on registration form when submitting empty fields"/>
14+
<title value="Verify validation error messages display consistently on registration form across multiple submissions"/>
1515
<description value="Verify that all required fields on customer registration form display proper mage-error validation messages when form is submitted without filling any data"/>
1616
<severity value="MINOR"/>
1717
<testCaseId value="AC-15355"/>
@@ -28,23 +28,39 @@
2828
</after>
2929
<!-- Step 3: Navigate to customer registration page -->
3030
<actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/>
31-
<!-- Step 4: Submit form without filling any data -->
31+
<!-- Step 4: Submit form without filling any data (Iteration 1) -->
3232
<scrollTo selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="scrollToCreateButton"/>
3333
<waitForElementClickable selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="waitForCreateAccountButton"/>
3434
<click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickCreateAccountButton"/>
3535
<waitForPageLoad stepKey="waitForValidation"/>
36-
<!-- Step 5: Verify all required fields display proper error messages -->
37-
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('firstname-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertFirstNameError"/>
38-
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('lastname-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertLastNameError"/>
39-
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('email_address-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertEmailError"/>
40-
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('password-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertPasswordError"/>
41-
<waitForText selector="{{StorefrontCustomerCreateFormSection.fieldErrorByFieldId('password-confirmation-error')}}" userInput="{{ProductWarningMessage.require_Message}}" stepKey="assertPasswordConfirmationError"/>
42-
<!-- Step 6: Enter all needed data -->
36+
<!-- Verify all required fields display proper error messages -->
37+
<actionGroup ref="StorefrontAssertCustomerRegistrationValidationMessagesActionGroup" stepKey="assertValidationMessagesIteration1"/>
38+
<!-- Step 5: Refresh the browser page -->
39+
<reloadPage stepKey="refreshPage"/>
40+
<waitForPageLoad stepKey="waitForPageLoad"/>
41+
<!-- Step 6: Submit form without filling any data (Iteration 2) -->
42+
<scrollTo selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="scrollToCreateButtonAfterRefresh"/>
43+
<waitForElementClickable selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="waitForCreateAccountButtonAfterRefresh"/>
44+
<click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickCreateAccountButtonAfterRefresh"/>
45+
<waitForPageLoad stepKey="waitForValidationAfterRefresh"/>
46+
<!-- Verify all validation messages - Iteration 2 -->
47+
<actionGroup ref="StorefrontAssertCustomerRegistrationValidationMessagesActionGroup" stepKey="assertValidationMessagesIteration2"/>
48+
<!-- Step 7: Repeat steps 5 & 6 (Iteration 3) -->
49+
<reloadPage stepKey="refreshPageThirdTime"/>
50+
<waitForPageLoad stepKey="waitForPageLoadThirdTime"/>
51+
<scrollTo selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="scrollToCreateButtonThirdTime"/>
52+
<waitForElementClickable selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="waitForCreateAccountButtonThirdTime"/>
53+
<click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickCreateAccountButtonThirdTime"/>
54+
<waitForPageLoad stepKey="waitForValidationThirdTime"/>
55+
<!-- Verify all validation messages - Iteration 3 -->
56+
<actionGroup ref="StorefrontAssertCustomerRegistrationValidationMessagesActionGroup" stepKey="assertValidationMessagesIteration3"/>
57+
<!-- Step 8: Fill form with valid data and complete registration -->
4358
<actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm">
4459
<argument name="customer" value="Simple_US_Customer"/>
4560
</actionGroup>
46-
<!-- Step 7: Click on save customer and verify customer is saved successfully -->
61+
<!-- Step 9: Click on save button -->
4762
<actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/>
63+
<!-- Verify success message is displayed -->
4864
<actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage">
4965
<argument name="messageType" value="success"/>
5066
<argument name="message" value="{{customerSavedSuccessfullyMessage.value}}"/>

app/code/Magento/Paypal/Test/Mftf/Test/StorefrontRegisteredUserPaypalCheckoutWithFlatRatePaymentActionSaleTest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
4444
<!-- Delete Product -->
4545
<deleteData stepKey="deleteCategory" createDataKey="createCategory"/>
46-
<deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
46+
<deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/>
4747
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
4848
</after>
4949
<!-- Login to Storefront -->
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
<?php
2+
/**
3+
* Copyright 2026 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\SalesRule\Test\Mftf\Helper;
9+
10+
use Magento\FunctionalTestingFramework\DataGenerator\Persist\CurlHandler;
11+
use Magento\FunctionalTestingFramework\DataGenerator\Objects\EntityDataObject;
12+
use Magento\FunctionalTestingFramework\Helper\Helper;
13+
use Magento\FunctionalTestingFramework\ObjectManagerFactory;
14+
use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil;
15+
16+
/**
17+
* This helper use to delete sales rules via API:
18+
*
19+
* - CurlHandler::executeRequest() for all API calls
20+
* - Operation definitions from SalesRuleMeta.xml
21+
* - EntityDataObject for request structure
22+
*
23+
* NO custom HTTP clients, NO external dependencies, NO custom helpers
24+
*/
25+
class SalesRuleApiHelper extends Helper
26+
{
27+
/**
28+
* Delete all cart price rules using API (faster and more reliable than UI-based deletion)
29+
*
30+
* @param string $enableLogging Enable progress logging (true/false)
31+
* @param string $pageSize Rules per page
32+
* @return void
33+
*/
34+
public function deleteAllSalesRulesApi(string $enableLogging = 'true', string $pageSize = '100'): void
35+
{
36+
$enableLog = ($enableLogging === 'true');
37+
$pageNum = (int)$pageSize;
38+
$stats = [
39+
'total_deleted' => 0,
40+
'total_failed' => 0,
41+
];
42+
43+
$this->logMessage($enableLog, "Starting cart price rule deletion via API...");
44+
45+
try {
46+
$allRules = $this->getAllSalesRules($pageNum);
47+
48+
if ($this->handleEmptyRuleList($allRules, $enableLog)) {
49+
return;
50+
}
51+
52+
$this->processRuleDeletion($allRules, $stats, $enableLog);
53+
$message = "Cart price rule deletion completed: {$stats['total_deleted']} successful, " .
54+
"{$stats['total_failed']} failed.";
55+
$this->logMessage($enableLog, $message);
56+
57+
} catch (\Exception $e) {
58+
$this->logMessage($enableLog, "ERROR: Cart price rule deletion failed: " . $e->getMessage());
59+
}
60+
}
61+
62+
/**
63+
* Delete single rule by ID using MFTF CurlHandler
64+
*
65+
* @param int $ruleId
66+
* @param array $stats
67+
* @return void
68+
* @throws \Exception
69+
*/
70+
private function deleteByRuleId(int $ruleId, array &$stats): void
71+
{
72+
if (empty($ruleId)) {
73+
throw new \Exception("Rule ID cannot be empty");
74+
}
75+
76+
$ruleEntity = new EntityDataObject(
77+
name: 'salesrule_to_delete_' . $ruleId,
78+
type: 'SalesRule',
79+
data: ['rule_id' => $ruleId],
80+
linkedEntities: [],
81+
uniquenessData: [],
82+
vars: [],
83+
parentEntity: null,
84+
filename: null,
85+
deprecated: null
86+
);
87+
88+
$curlHandler = ObjectManagerFactory::getObjectManager()->create(
89+
CurlHandler::class,
90+
[
91+
'operation' => 'delete',
92+
'entityObject' => $ruleEntity,
93+
'storeCode' => null
94+
]
95+
);
96+
$response = $curlHandler->executeRequest([]);
97+
98+
if (!$this->isResponseSuccessful($response)) {
99+
$errorMessage = "Rule deletion failed for ID '{$ruleId}' - unexpected response: " . json_encode($response);
100+
throw new \Exception($errorMessage);
101+
}
102+
103+
$stats['total_deleted']++;
104+
}
105+
106+
/**
107+
* Log message if logging is enabled
108+
*
109+
* @param bool $enableLog
110+
* @param string $message
111+
* @return void
112+
*/
113+
private function logMessage(bool $enableLog, string $message): void
114+
{
115+
if ($enableLog) {
116+
LoggingUtil::getInstance()->getLogger(self::class)->info($message);
117+
}
118+
}
119+
120+
/**
121+
* Handle empty rule list scenario
122+
*
123+
* @param array $allRules
124+
* @param bool $enableLog
125+
* @return bool True if should return early, false to continue
126+
*/
127+
private function handleEmptyRuleList(array $allRules, bool $enableLog): bool
128+
{
129+
if (empty($allRules)) {
130+
$this->logMessage($enableLog, "No cart price rules found.");
131+
return true;
132+
}
133+
return false;
134+
}
135+
136+
/**
137+
* Process deletion of all rules
138+
*
139+
* @param array $allRules
140+
* @param array $stats
141+
* @param bool $enableLog
142+
* @return void
143+
*/
144+
private function processRuleDeletion(array $allRules, array &$stats, bool $enableLog): void
145+
{
146+
foreach ($allRules as $rule) {
147+
$ruleId = $rule['rule_id'] ?? '';
148+
149+
if (empty($ruleId)) {
150+
$stats['total_failed']++;
151+
continue;
152+
}
153+
154+
try {
155+
$this->deleteByRuleId((int)$ruleId, $stats);
156+
} catch (\Exception $e) {
157+
$stats['total_failed']++;
158+
$ruleName = $rule['name'] ?? 'Unknown';
159+
$errorMsg = "Failed to delete rule '{$ruleName}' (ID: {$ruleId}): " . $e->getMessage();
160+
$this->logMessage($enableLog, $errorMsg);
161+
}
162+
}
163+
}
164+
165+
/**
166+
* Check if API response indicates success
167+
*
168+
* @param mixed $response
169+
* @return bool
170+
*/
171+
private function isResponseSuccessful($response): bool
172+
{
173+
// Direct success values
174+
if (in_array($response, [true, 'true', '1', ''], true)) {
175+
return true;
176+
}
177+
178+
// String response handling
179+
if (is_string($response)) {
180+
return $this->validateStringResponse($response);
181+
}
182+
183+
return false;
184+
}
185+
186+
/**
187+
* Validate string response for success indicators
188+
*
189+
* @param string $response
190+
* @return bool
191+
*/
192+
private function validateStringResponse(string $response): bool
193+
{
194+
// Try JSON parsing first
195+
$jsonData = json_decode($response, true);
196+
if (json_last_error() === JSON_ERROR_NONE) {
197+
return ($jsonData === true ||
198+
($jsonData['success'] ?? false) ||
199+
($jsonData['status'] ?? '') === 'success');
200+
}
201+
202+
// Plain text fallback
203+
return in_array(trim($response), ['true', '1', 'success', ''], true);
204+
}
205+
206+
/**
207+
* Get all cart price rules using MFTF CurlHandler with GetSalesRuleList operation
208+
*
209+
* @param int $pageSize Rules per page
210+
* @return array Rule data
211+
* @throws \Exception
212+
*/
213+
private function getAllSalesRules(int $pageSize): array
214+
{
215+
try {
216+
// Create EntityDataObject for GetSalesRuleList operation
217+
$ruleListEntity = new EntityDataObject(
218+
name: 'salesrule_list_all',
219+
type: 'salesrule_list',
220+
data: [
221+
'pageSize' => $pageSize,
222+
'currentPage' => 1,
223+
'fields' => 'items[rule_id,name,is_active]'
224+
],
225+
linkedEntities: [],
226+
uniquenessData: [],
227+
vars: [],
228+
parentEntity: null,
229+
filename: null,
230+
deprecated: null
231+
);
232+
233+
// Create CurlHandler for GetSalesRuleList operation
234+
$curlHandler = ObjectManagerFactory::getObjectManager()->create(
235+
CurlHandler::class,
236+
[
237+
'operation' => 'get',
238+
'entityObject' => $ruleListEntity,
239+
'storeCode' => null
240+
]
241+
);
242+
243+
// Execute using MFTF CurlHandler
244+
$response = $curlHandler->executeRequest([]);
245+
246+
// Parse response
247+
if (is_string($response)) {
248+
$responseData = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
249+
} else {
250+
$responseData = $response;
251+
}
252+
253+
return $responseData['items'] ?? [];
254+
255+
} catch (\Exception $e) {
256+
$errorMessage = "Failed to retrieve cart price rules: " . $e->getMessage();
257+
throw new \Exception($errorMessage, 0, $e);
258+
}
259+
}
260+
}

app/code/Magento/SalesRule/Test/Mftf/Metadata/SalesRuleMeta.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,9 @@
9292
<operation name="DeleteSalesRule" dataType="SalesRule" type="delete" auth="adminOauth" url="/V1/salesRules/{rule_id}" method="DELETE">
9393
<contentType>application/json</contentType>
9494
</operation>
95+
<operation name="GetSalesRuleList" dataType="salesrule_list" type="get" auth="adminOauth"
96+
url="/V1/salesRules/search?searchCriteria[pageSize]={pageSize}&amp;searchCriteria[currentPage]={currentPage}&amp;fields={fields}"
97+
method="GET">
98+
<contentType>application/json</contentType>
99+
</operation>
95100
</operations>

app/code/Magento/Shipping/Test/Mftf/Test/AdminPrintShippingLabelForFedexShipmentTest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@
7575
<waitForElementClickable selector="{{CheckoutShippingMethodsSection.smartPostShippingMethod}}" stepKey="waitForSelectFedexShippingMethod"/>
7676
<click selector="{{CheckoutShippingMethodsSection.smartPostShippingMethod}}" stepKey="selectFedexShippingMethod"/>
7777
<actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="clickNext"/>
78+
<!-- Wait for payment section to be fully loaded after FedEx validation -->
79+
<waitForElementVisible selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="60" stepKey="waitForPaymentSectionLoaded"/>
80+
<seeCurrentUrlMatches regex="~/checkout/?#payment~" stepKey="assertCheckoutPaymentUrl"/>
7881
<actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/>
82+
<waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/>
7983
<actionGroup ref="ClickPlaceOrderActionGroup" stepKey="clickPlaceOrder"/>
8084
<!-- I see order successful Page -->
8185
<actionGroup ref="AssertStorefrontCheckoutSuccessActionGroup" stepKey="assertOrderSuccess"/>

0 commit comments

Comments
 (0)