Skip to content

Commit db41fab

Browse files
committed
Merge branch 'MAGETWO-94407' into MAGETWO-94407-2.3
2 parents ed8b676 + 38176e7 commit db41fab

File tree

8 files changed

+331
-1
lines changed

8 files changed

+331
-1
lines changed

app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818
<element name="AttributeDescription" type="text" selector="#description"/>
1919
<element name="ChangeAttributeDescriptionToggle" type="checkbox" selector="#toggle_description"/>
2020
<element name="Save" type="button" selector="button[title=Save]" timeout="30"/>
21+
<element name="defaultLabel" type="text" selector="//td[contains(text(), '{{attributeName}}')]/following-sibling::td[contains(@class, 'col-frontend_label')]" parameterized="true"/>
2122
</section>
2223
</sections>

app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@
2525
<element name="miniCartSubtotalField" type="text" selector=".block-minicart .amount span.price"/>
2626
<element name="itemQuantity" type="input" selector="//a[text()='{{productName}}']/../..//input[contains(@class,'cart-item-qty')]" parameterized="true"/>
2727
<element name="itemQuantityUpdate" type="button" selector="//a[text()='{{productName}}']/../..//span[text()='Update']" parameterized="true"/>
28+
<element name="itemDiscount" type="text" selector="//tr[@class='totals']//td[@class='amount']/span"/>
29+
<element name="subtotal" type="text" selector="//tr[@class='totals sub']//td[@class='amount']/span"/>
2830
</section>
2931
</sections>

app/code/Magento/ConfigurableProduct/etc/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,11 @@
228228
</argument>
229229
</arguments>
230230
</type>
231+
<type name="Magento\SalesRule\Model\Quote\ChildrenValidationLocator">
232+
<arguments>
233+
<argument name="productTypeChildrenValidationMap" xsi:type="array">
234+
<item name="configurable" xsi:type="boolean">false</item>
235+
</argument>
236+
</arguments>
237+
</type>
231238
</config>

app/code/Magento/SalesRule/Model/Rule/Condition/Product.php

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,131 @@ protected function _addSpecialAttributes(array &$attributes)
2525
$attributes['quote_item_qty'] = __('Quantity in cart');
2626
$attributes['quote_item_price'] = __('Price in cart');
2727
$attributes['quote_item_row_total'] = __('Row total in cart');
28+
29+
$attributes['parent::category_ids'] = __('Category (Parent only)');
30+
$attributes['children::category_ids'] = __('Category (Children Only)');
31+
}
32+
33+
/**
34+
* Retrieve attribute
35+
*
36+
* @return string
37+
*/
38+
public function getAttribute()
39+
{
40+
$attribute = $this->getData('attribute');
41+
if (strpos($attribute, '::') !== false) {
42+
list (, $attribute) = explode('::', $attribute);
43+
}
44+
return $attribute;
45+
}
46+
47+
/**
48+
* @inheritdoc
49+
*/
50+
public function getAttributeName()
51+
{
52+
$attribute = $this->getAttribute();
53+
if ($this->getAttributeScope()) {
54+
$attribute = $this->getAttributeScope() . '::' . $attribute;
55+
}
56+
return $this->getAttributeOption($attribute);
57+
}
58+
59+
/**
60+
* @inheritdoc
61+
*/
62+
public function loadAttributeOptions()
63+
{
64+
$productAttributes = $this->_productResource->loadAllAttributes()->getAttributesByCode();
65+
66+
$attributes = [];
67+
foreach ($productAttributes as $attribute) {
68+
/* @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
69+
if (!$attribute->isAllowedForRuleCondition() || !$attribute->getDataUsingMethod(
70+
$this->_isUsedForRuleProperty
71+
)
72+
) {
73+
continue;
74+
}
75+
$frontLabel = $attribute->getFrontendLabel();
76+
$attributes[$attribute->getAttributeCode()] = $frontLabel;
77+
$attributes['parent::' . $attribute->getAttributeCode()] = $frontLabel . __('(Parent Only)');
78+
$attributes['children::' . $attribute->getAttributeCode()] = $frontLabel . __('(Children Only)');
79+
}
80+
81+
$this->_addSpecialAttributes($attributes);
82+
83+
asort($attributes);
84+
$this->setAttributeOption($attributes);
85+
86+
return $this;
87+
}
88+
89+
/**
90+
* @inheritdoc
91+
*/
92+
public function getAttributeElementHtml()
93+
{
94+
$html = parent::getAttributeElementHtml() .
95+
$this->getAttributeScopeElement()->getHtml();
96+
return $html;
97+
}
98+
99+
/**
100+
* Retrieve form element for scope element
101+
*
102+
* @return \Magento\Framework\Data\Form\Element\AbstractElement
103+
*/
104+
private function getAttributeScopeElement()
105+
{
106+
return $this->getForm()->addField(
107+
$this->getPrefix() . '__' . $this->getId() . '__attribute_scope',
108+
'hidden',
109+
[
110+
'name' => $this->elementName . '[' . $this->getPrefix() . '][' . $this->getId() . '][attribute_scope]',
111+
'value' => $this->getAttributeScope(),
112+
'no_span' => true,
113+
'class' => 'hidden',
114+
'data-form-part' => $this->getFormName()
115+
]
116+
);
117+
}
118+
119+
/**
120+
* Set attribute value
121+
*
122+
* @param $value
123+
*/
124+
public function setAttribute($value)
125+
{
126+
if (strpos($value, '::') !== false) {
127+
list($scope, $attribute) = explode('::', $value);
128+
$this->setData('attribute_scope', $scope);
129+
$this->setData('attribute', $attribute);
130+
} else {
131+
$this->setData('attribute', $value);
132+
}
133+
}
134+
135+
/**
136+
* @inheritdoc
137+
*/
138+
public function loadArray($arr)
139+
{
140+
parent::loadArray($arr);
141+
$this->setAttributeScope(isset($arr['attribute_scope']) ? $arr['attribute_scope'] : null);
142+
return $this;
143+
}
144+
145+
/**
146+
* @inheritdoc
147+
*/
148+
public function asArray(array $arrAttributes = [])
149+
{
150+
$out = parent::asArray($arrAttributes);
151+
$out['attribute_scope'] = $this->getAttributeScope();
152+
return $out;
28153
}
29154

30155
/**

app/code/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,46 @@ public function collectValidatedAttributes($productCollection)
8585
}
8686
return $this;
8787
}
88+
89+
/**
90+
* @inheritdoc
91+
*/
92+
protected function _isValid($entity)
93+
{
94+
if (!$this->getConditions()) {
95+
return true;
96+
}
97+
98+
$all = $this->getAggregator() === 'all';
99+
$true = (bool)$this->getValue();
100+
101+
foreach ($this->getConditions() as $cond) {
102+
if ($entity instanceof \Magento\Framework\Model\AbstractModel) {
103+
$attributeScope = $cond->getAttributeScope();
104+
if ($attributeScope === 'parent') {
105+
$validateEntities = [$entity];
106+
} elseif ($attributeScope === 'children') {
107+
$validateEntities = $entity->getChildren() ?: [$entity];
108+
} else {
109+
$validateEntities = $entity->getChildren() ?: [];
110+
$validateEntities[] = $entity;
111+
}
112+
$validated = !$true;
113+
foreach ($validateEntities as $validateEntity) {
114+
$validated = $cond->validate($validateEntity);
115+
if ($validated === $true) {
116+
break;
117+
}
118+
}
119+
} else {
120+
$validated = $cond->validateByEntityId($entity);
121+
}
122+
if ($all && $validated !== $true) {
123+
return false;
124+
} elseif (!$all && $validated === $true) {
125+
return true;
126+
}
127+
}
128+
return $all ? true : false;
129+
}
88130
}

app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
<!-- Actions sub-form -->
2626
<element name="actionsHeader" type="button" selector="div[data-index='actions']" timeout="30"/>
2727
<element name="apply" type="select" selector="select[name='simple_action']"/>
28+
<element name="conditions" type="button" selector=".rule-param.rule-param-new-child > a"/>
29+
<element name="childAttribute" type="select" selector="//select[contains(@name, 'new_child')]"/>
30+
<element name="condition" type="text" selector="//span[@class='rule-param']/a[text()='{{arg}}']" parameterized="true"/>
31+
<element name="operator" type="select" selector="//select[contains(@name, '[operator]')]"/>
32+
<element name="option" type="select" selector="//ul[@class='rule-param-children']//select[contains(@name, '[value]')]"/>
2833
<element name="applyDiscountToShipping" type="checkbox" selector="input[name='apply_to_shipping']"/>
2934
<element name="applyDiscountToShippingLabel" type="checkbox" selector="input[name='apply_to_shipping']+label"/>
3035
<element name="discountAmount" type="input" selector="input[name='discount_amount']"/>

app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
<argument name="ruleName" value="{{_defaultCoupon.code}}"/>
3131
</actionGroup>
3232
<deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/>
33-
<amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/>
33+
<actionGroup ref="logout" stepKey="logout"/>
3434
</after>
3535

3636
<!-- Create a cart price rule based on a coupon code -->
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
11+
<test name="CartPriceRuleForConfigurableProductTest">
12+
<annotations>
13+
<features value="SalesRule"/>
14+
<stories value="MAGETWO-94407 - Cart Price Rule for configurable products"/>
15+
<title value="Checking Cart Price Rule for configurable products"/>
16+
<description value="Checking Cart Price Rule for configurable products"/>
17+
<severity value="BLOCKER"/>
18+
<testCaseId value="MAGETWO-94471"/>
19+
<group value="SalesRule"/>
20+
</annotations>
21+
22+
<before>
23+
<!-- Create the category -->
24+
<createData entity="ApiCategory" stepKey="createCategory"/>
25+
<!-- Create the configurable product and add it to the category -->
26+
<createData entity="ApiConfigurableProduct" stepKey="createConfigProduct">
27+
<requiredEntity createDataKey="createCategory"/>
28+
</createData>
29+
<!-- Create an attribute with two options to be used in the first child product -->
30+
<createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/>
31+
<createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1">
32+
<requiredEntity createDataKey="createConfigProductAttribute"/>
33+
</createData>
34+
<createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2">
35+
<requiredEntity createDataKey="createConfigProductAttribute"/>
36+
</createData>
37+
38+
<!-- Add the attribute we just created to default attribute set -->
39+
<createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet">
40+
<requiredEntity createDataKey="createConfigProductAttribute"/>
41+
</createData>
42+
43+
<!-- Get the option of the attribute we created -->
44+
<getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1">
45+
<requiredEntity createDataKey="createConfigProductAttribute"/>
46+
</getData>
47+
<getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2">
48+
<requiredEntity createDataKey="createConfigProductAttribute"/>
49+
</getData>
50+
<!-- Create a simple product and give it the attribute with option -->
51+
<createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1">
52+
<requiredEntity createDataKey="createConfigProductAttribute"/>
53+
<requiredEntity createDataKey="getConfigAttributeOption1"/>
54+
</createData>
55+
<createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2">
56+
<requiredEntity createDataKey="createConfigProductAttribute"/>
57+
<requiredEntity createDataKey="getConfigAttributeOption2"/>
58+
</createData>
59+
60+
<!-- Create the configurable product -->
61+
<createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption">
62+
<requiredEntity createDataKey="createConfigProduct"/>
63+
<requiredEntity createDataKey="createConfigProductAttribute"/>
64+
<requiredEntity createDataKey="getConfigAttributeOption1"/>
65+
<requiredEntity createDataKey="getConfigAttributeOption2"/>
66+
</createData>
67+
68+
<!-- Add simple product to the configurable product -->
69+
<createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1">
70+
<requiredEntity createDataKey="createConfigProduct"/>
71+
<requiredEntity createDataKey="createConfigChildProduct1"/>
72+
</createData>
73+
<createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2">
74+
<requiredEntity createDataKey="createConfigProduct"/>
75+
<requiredEntity createDataKey="createConfigChildProduct2"/>
76+
</createData>
77+
<actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/>
78+
</before>
79+
80+
<after>
81+
<actionGroup ref="DeleteCartPriceRuleByName" stepKey="DeleteCartPriceRuleByName">
82+
<argument name="ruleName" value="{{SimpleSalesRule.name}}"/>
83+
</actionGroup>
84+
<deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/>
85+
<deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/>
86+
<deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/>
87+
<deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/>
88+
<deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/>
89+
<actionGroup ref="logout" stepKey="logout"/>
90+
</after>
91+
92+
<!-- Create the rule -->
93+
<amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/>
94+
<waitForPageLoad stepKey="waitForRulesPage"/>
95+
<click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/>
96+
<fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/>
97+
<selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/>
98+
<actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/>
99+
<selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/>
100+
<fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="ABCD" stepKey="fillCouponCOde"/>
101+
<click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/>
102+
<fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="50" stepKey="fillDiscountAmount"/>
103+
<scrollTo selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="ScrollToApplyRuleForConditions"/>
104+
<click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="ApplyRuleForConditions"/>
105+
<waitForPageLoad stepKey="waitForDropDownOpened"/>
106+
<selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$" stepKey="selectAttribute"/>
107+
<waitForPageLoad stepKey="waitForOperatorOpened"/>
108+
<click selector="{{AdminCartPriceRulesFormSection.condition('is')}}" stepKey="clickToChooseCondition"/>
109+
<selectOption selector="{{AdminCartPriceRulesFormSection.operator}}" userInput="is not" stepKey="selectOperator"/>
110+
<waitForPageLoad stepKey="waitForOperatorOpened1"/>
111+
<click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption"/>
112+
<waitForPageLoad stepKey="waitForConditionOpened2"/>
113+
<selectOption selector="{{AdminCartPriceRulesFormSection.option}}" userInput="option1" stepKey="selectOption"/>
114+
<waitForPageLoad stepKey="waitForPageLoaded"/>
115+
<click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/>
116+
<see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/>
117+
118+
<!-- Add the first product to the cart -->
119+
<amOnPage url="$$createConfigChildProduct1.sku$$.html" stepKey="goToProductPage1"/>
120+
<waitForPageLoad stepKey="waitForProductPageLoad1"/>
121+
<click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart1"/>
122+
<waitForPageLoad stepKey="waitForAddToCart1"/>
123+
<!-- Add the second product to the cart -->
124+
<amOnPage url="$$createConfigChildProduct2.sku$$.html" stepKey="goToProductPage2"/>
125+
<waitForPageLoad stepKey="waitForProductPageLoad2"/>
126+
<click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart2"/>
127+
<waitForPageLoad stepKey="waitForAddToCart2"/>
128+
129+
<!--View and edit cart-->
130+
<actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickViewAndEditCartFromMiniCart"/>
131+
<click selector="{{DiscountSection.DiscountTab}}" stepKey="scrollToDiscountTab" />
132+
<fillField selector="{{DiscountSection.CouponInput}}" userInput="ABCD" stepKey="fillCouponCode" />
133+
<click selector="{{DiscountSection.ApplyCodeBtn}}" stepKey="applyCode"/>
134+
<waitForPageLoad stepKey="waitForPageLoad3"/>
135+
<see userInput="You used coupon code" stepKey="assertText"/>
136+
<!--Verify values-->
137+
<grabTextFrom selector="{{StorefrontMinicartSection.itemDiscount}}" stepKey="getDiscount"/>
138+
<grabTextFrom selector="{{StorefrontMinicartSection.subtotal}}" stepKey="getSubtotal"/>
139+
<assertEquals stepKey="checkDescount">
140+
<expectedResult type="string">-$117.00</expectedResult>
141+
<actualResult type="variable">$getDiscount</actualResult>
142+
</assertEquals>
143+
<assertEquals stepKey="checkSubtotal">
144+
<expectedResult type="string">$357.00</expectedResult>
145+
<actualResult type="variable">$getSubtotal</actualResult>
146+
</assertEquals>
147+
</test>
148+
</tests>

0 commit comments

Comments
 (0)