From 9ffcdc60c5afa199d760d74e1b127ab69878c703 Mon Sep 17 00:00:00 2001 From: Anna Pak Date: Sun, 4 Oct 2020 15:26:12 +0300 Subject: [PATCH 001/112] refactoring of AdminCreateInvoiceTest --- .../Test/Mftf/Test/AdminCreateInvoiceTest.xml | 93 +++++++------------ 1 file changed, 31 insertions(+), 62 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 73c126bb7794f..e6a0a8b3661dd 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -18,77 +18,46 @@ - + + - + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - + - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - From 0c14e5ffd0778b5d7d203b3b74deea361a3d3f80 Mon Sep 17 00:00:00 2001 From: Anna Pak Date: Sun, 4 Oct 2020 15:38:27 +0300 Subject: [PATCH 002/112] refactored --- .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index e6a0a8b3661dd..f91c8d9993044 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -55,7 +55,7 @@ - + From ea995471fc9a9c0f75ad3dae3cbfbaba215327f0 Mon Sep 17 00:00:00 2001 From: Anna Pak Date: Mon, 5 Oct 2020 13:16:32 +0300 Subject: [PATCH 003/112] wrap openOrder action with ActionGroup Please enter the commit message for your changes. Lines starting --- .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index f91c8d9993044..f79154398eaff 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -45,7 +45,9 @@ - + + + From 3e467313566cb38f7af8bb6c5d9b991b35d0dcbe Mon Sep 17 00:00:00 2001 From: Anna Pak Date: Mon, 5 Oct 2020 14:14:04 +0300 Subject: [PATCH 004/112] deprecating old test Please enter the commit message for your changes. Lines starting --- .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index f79154398eaff..49ae1844a1cf1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -8,7 +8,7 @@ - + From 7806115a430973fec96cef5be76de5d30cf81a76 Mon Sep 17 00:00:00 2001 From: Oleh Usik Date: Wed, 4 Nov 2020 12:41:57 +0200 Subject: [PATCH 005/112] Assert discount --- ...ssertStorefrontCartDiscountActionGroup.xml | 21 +++++++++++++++++++ ...tCheckoutWithCouponAndZeroSubtotalTest.xml | 6 ++++-- ...efrontUKCustomerCheckoutWithCouponTest.xml | 6 ++++-- ...eConditionAndFreeShippingIsAppliedTest.xml | 4 +++- ...inCreateCartPriceRuleEmptyFromDateTest.xml | 6 ++++-- ...inCreateCartPriceRuleForCouponCodeTest.xml | 6 ++++-- ...ateCartPriceRuleForGeneratedCouponTest.xml | 6 ++++-- ...talAndVerifyRuleConditionIsAppliedTest.xml | 4 +++- ...oryAndVerifyRuleConditionIsAppliedTest.xml | 4 +++- ...ghtAndVerifyRuleConditionIsAppliedTest.xml | 4 +++- .../StorefrontCartPriceRuleCountryTest.xml | 6 ++++-- .../StorefrontCartPriceRulePostcodeTest.xml | 6 ++++-- .../StorefrontCartPriceRuleQuantityTest.xml | 6 ++++-- .../Test/StorefrontCartPriceRuleStateTest.xml | 6 ++++-- .../StorefrontCartPriceRuleSubtotalTest.xml | 6 ++++-- ...ValueWithFullDiscountUsingCartRuleTest.xml | 6 ++++-- 16 files changed, 77 insertions(+), 26 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCartDiscountActionGroup.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCartDiscountActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCartDiscountActionGroup.xml new file mode 100644 index 0000000000000..ed34b460d6158 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCartDiscountActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + Assert that the provided Discount is present in the Storefront Shopping Cart. + + + + + + + + diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml index f910a9d47244f..fbfd781505ab7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml @@ -47,8 +47,10 @@ - - + + + + diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml index d037718a1ec94..fd38120aab98b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml @@ -75,8 +75,10 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml index 88853b2c40d9a..96da616818ae1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml @@ -107,6 +107,8 @@ - + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml index e206633808057..65bb0b4cbfb99 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml @@ -87,8 +87,10 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 16af210066997..198ba1cd64f35 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -80,7 +80,9 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index 6577ff9440456..70624a4e2e339 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -84,7 +84,9 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml index da8c8e4bc1f9d..1fe97b1f45036 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml @@ -116,6 +116,8 @@ - + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml index f6e736c73db74..c80f43385d166 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml @@ -118,6 +118,8 @@ - + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml index 5f110f7074f6f..cd72ec8529816 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml @@ -106,6 +106,8 @@ - + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml index ea96fa41e5cad..3b54df544210f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountryTest.xml @@ -77,8 +77,10 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml index 62c494b988bbd..d0cba156f635e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcodeTest.xml @@ -81,8 +81,10 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml index 70ed09df7a2cc..1a449017e0386 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantityTest.xml @@ -83,7 +83,9 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml index da9ca9055d31b..68f6fc93eab94 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleStateTest.xml @@ -77,8 +77,10 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml index ce0d814e50308..0ffe1516d0232 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotalTest.xml @@ -81,7 +81,9 @@ - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml index 1178ca2cfb328..8df45937bb542 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml @@ -117,8 +117,10 @@ - - + + + + From d157d42ebef3a903b39c3a8e224ab0646b91061d Mon Sep 17 00:00:00 2001 From: Anna Pak Date: Mon, 23 Nov 2020 10:41:42 +0200 Subject: [PATCH 006/112] AdminCreateInvoiceTest deprecated --- .../Test/Mftf/Test/AdminCreateInvoiceTest.xml | 96 ++++++++++++------- .../Test/Mftf/Test/AdminInvoiceOrderTest.xml | 65 +++++++++++++ 2 files changed, 127 insertions(+), 34 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 49ae1844a1cf1..3e8fd1e42c12b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -12,54 +12,82 @@ - + <title value="DEPRECATED. Admin should be able to create an invoice"/> <description value="Admin should be able to create an invoice"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-72096"/> <group value="sales"/> + <skip> + <issueID value="DEPRECATED">Use AdminInvoiceOrderTest instead</issueID> + </skip> </annotations> - - <before> + <before> <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createSimpleProductApi"> + <createData entity="_defaultProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <createData entity="GuestCart" stepKey="createGuestCart"/> - <createData entity="SimpleCartItem" stepKey="addCartItem"> - <requiredEntity createDataKey="createGuestCart"/> - <requiredEntity createDataKey="createSimpleProductApi"/> - </createData> - <createData entity="GuestAddressInformation" stepKey="addGuestOrderAddress"> - <requiredEntity createDataKey="createGuestCart"/> - </createData> - <updateData createDataKey="createGuestCart" entity="GuestOrderPaymentMethod" stepKey="sendGuestPaymentInformation"> - <requiredEntity createDataKey="createGuestCart"/> - </updateData> </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createSimpleProductApi" stepKey="deleteSimpleProductApi"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + <deleteData createDataKey="createCategory" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct" stepKey="deleteCategory1"/> </after> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> - - <actionGroup ref="AdminOpenOrderByEntityIdActionGroup" stepKey="openOrder"> - <argument name="entity_id" value="$createGuestCart.return$"/> - </actionGroup> - - <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> + <!-- todo: Create an order via the api instead of driving the browser --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <actionGroup ref="StorefrontClickAddToCartButtonActionGroup" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <actionGroup ref="StorefrontClickOnMiniCartActionGroup" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <fillField selector="{{CheckoutShippingGuestInfoSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingGuestInfoSection.firstName}}" userInput="{{CustomerEntityOne.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingGuestInfoSection.lastName}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingGuestInfoSection.street}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingGuestInfoSection.city}}" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutShippingGuestInfoSection.region}}" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutShippingGuestInfoSection.postcode}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutShippingGuestInfoSection.telephone}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> + <actionGroup ref="StorefrontCheckoutClickNextButtonActionGroup" stepKey="clickNext"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + <!-- end todo --> - <actionGroup ref="FilterInvoiceGridByOrderIdWithCleanFiltersActionGroup" stepKey="filterInvoiceGridByOrderId"> - <argument name="orderId" value="$createGuestCart.return$"/> - </actionGroup> - - <actionGroup ref="AdminSelectFirstGridRowActionGroup" stepKey="openInvoiceFromGrid"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> - <actionGroup ref="AdminOrderViewCheckStatusActionGroup" stepKey="checkIfOrderStatusIsProcessing"> - <argument name="status" value="Processing"/> - </actionGroup> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> + <actionGroup ref="AdminOrderGridClickFirstRowActionGroup" stepKey="clickOrderRow"/> + <actionGroup ref="AdminClickInvoiceButtonOrderViewActionGroup" stepKey="clickInvoice"/> + <actionGroup ref="AdminInvoiceClickSubmitActionGroup" stepKey="clickSubmitInvoice"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeSuccessMessage"/> + <click selector="{{AdminOrderDetailsOrderViewSection.invoices}}" stepKey="clickInvoices"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask5" /> + <see selector="{{AdminOrderDetailsInvoicesSection.content}}" userInput="{$grabOrderNumber}" stepKey="seeInvoice1"/> + <see selector="{{AdminOrderDetailsInvoicesSection.content}}" userInput="John Doe" stepKey="seeInvoice2"/> + <click selector="{{AdminOrderDetailsOrderViewSection.information}}" stepKey="clickInformation"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrderInformationTabLoadingMask"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Processing" stepKey="seeOrderStatus"/> + <amOnPage url="{{AdminInvoicesPage.url}}" stepKey="goToInvoices"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask6" /> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridInitial"/> + <click selector="{{AdminInvoicesGridSection.filter}}" stepKey="clickFilters"/> + <fillField selector="{{AdminInvoicesFiltersSection.orderNum}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum2"/> + <click selector="{{AdminInvoicesFiltersSection.applyFilters}}" stepKey="clickApplyFilters"/> + <click selector="{{AdminInvoicesGridSection.firstRow}}" stepKey="clickInvoice2"/> + <see selector="{{AdminInvoiceDetailsInformationSection.orderStatus}}" userInput="Processing" stepKey="seeOrderStatus2"/> </test> -</tests> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml new file mode 100644 index 0000000000000..14fe9c1ece95b --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminInvoiceOrderTest"> + <annotations> + <features value="Sales"/> + <stories value="Create an Invoice via the Admin"/> + <title value="Admin should be able to create an invoice"/> + <description value="Admin should be able to create an invoice"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-72096"/> + <group value="sales"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProductApi"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="GuestCart" stepKey="createGuestCart"/> + <createData entity="SimpleCartItem" stepKey="addCartItem"> + <requiredEntity createDataKey="createGuestCart"/> + <requiredEntity createDataKey="createSimpleProductApi"/> + </createData> + <createData entity="GuestAddressInformation" stepKey="addGuestOrderAddress"> + <requiredEntity createDataKey="createGuestCart"/> + </createData> + <updateData createDataKey="createGuestCart" entity="GuestOrderPaymentMethod" stepKey="sendGuestPaymentInformation"> + <requiredEntity createDataKey="createGuestCart"/> + </updateData> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProductApi" stepKey="deleteSimpleProductApi"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> + + <actionGroup ref="AdminOpenOrderByEntityIdActionGroup" stepKey="openOrder"> + <argument name="entity_id" value="$createGuestCart.return$"/> + </actionGroup> + + <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> + + <actionGroup ref="FilterInvoiceGridByOrderIdWithCleanFiltersActionGroup" stepKey="filterInvoiceGridByOrderId"> + <argument name="orderId" value="$createGuestCart.return$"/> + </actionGroup> + + <actionGroup ref="AdminSelectFirstGridRowActionGroup" stepKey="openInvoiceFromGrid"/> + + <actionGroup ref="AdminOrderViewCheckStatusActionGroup" stepKey="checkIfOrderStatusIsProcessing"> + <argument name="status" value="Processing"/> + </actionGroup> + + </test> +</tests> From b23cad2a6a4741d85471c856d2aa8c07e44ba0f6 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Mon, 23 Nov 2020 10:48:15 +0200 Subject: [PATCH 007/112] refactored --- .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 3e8fd1e42c12b..1916fb9ebdbbb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -18,7 +18,7 @@ <testCaseId value="MAGETWO-72096"/> <group value="sales"/> <skip> - <issueID value="DEPRECATED">Use AdminInvoiceOrderTest instead</issueID> + <issueId value="DEPRECATED">Use AdminInvoiceOrderTest instead</issueId> </skip> </annotations> <before> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml index 14fe9c1ece95b..3b28fd10d58b9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml @@ -46,7 +46,7 @@ <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="AdminOpenOrderByEntityIdActionGroup" stepKey="openOrder"> - <argument name="entity_id" value="$createGuestCart.return$"/> + <argument name="entityId" value="$createGuestCart.return$"/> </actionGroup> <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> From 2d8be9cd0387dc81d2086e9f5f766efc2cd1d813 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Mon, 23 Nov 2020 11:26:49 +0200 Subject: [PATCH 008/112] refactored --- .../Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml index 3b28fd10d58b9..922037fe4a3cd 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminInvoiceOrderTest.xml @@ -35,6 +35,9 @@ <updateData createDataKey="createGuestCart" entity="GuestOrderPaymentMethod" stepKey="sendGuestPaymentInformation"> <requiredEntity createDataKey="createGuestCart"/> </updateData> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> <after> @@ -42,8 +45,6 @@ <deleteData createDataKey="createSimpleProductApi" stepKey="deleteSimpleProductApi"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> - - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/> <actionGroup ref="AdminOpenOrderByEntityIdActionGroup" stepKey="openOrder"> <argument name="entityId" value="$createGuestCart.return$"/> From 97e34547ec7acfadffec4951d4dadd9cae3531f5 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Mon, 23 Nov 2020 21:14:35 +0200 Subject: [PATCH 009/112] Refactored AdminMassProductPriceUpdateTest --- ...inCheckProductOnProductGridActionGroup.xml | 22 +++++++++ .../AdminSetPriceForMassUpdateActionGroup.xml | 24 ++++++++++ .../Test/AdminMassProductPriceUpdateTest.xml | 48 ++++++++++++++----- 3 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml new file mode 100644 index 0000000000000..34c9c1edf50cb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCheckProductOnProductGridActionGroup"> + <annotations> + <description>Check the product on the Product Grid</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + </arguments> + + <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku(product.sku)}}" stepKey="selectProduct"/> + + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml new file mode 100644 index 0000000000000..b00f181c92360 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSetPriceForMassUpdateActionGroup"> + <annotations> + <description>Click the "Change" checkbox for the "Price" field. Set new price.</description> + </annotations> + <arguments> + <argument name="price" type="string"/> + </arguments> + + <scrollTo stepKey="scrollToPriceCheckBox" selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" x="0" y="-160"/> + <click selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="selectPriceCheckBox"/> + <fillField stepKey="fillPrice" selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="{{price}}"/> + + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index 070c07d9feb7d..b3cf6e357fd6a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -41,19 +41,27 @@ <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <!--Select products--> - <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku($$simpleProduct1.sku$$)}}" stepKey="selectFirstProduct"/> - <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku($$simpleProduct2.sku$$)}}" stepKey="selectSecondProduct"/> + <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectFirstProduct"> + <argument name="product" value="$simpleProduct1$"/> + </actionGroup> + <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectSecondProduct"> + <argument name="product" value="$simpleProduct2$"/> + </actionGroup> <!-- Update product price--> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickChangeStatus"/> - <waitForPageLoad stepKey="waitForProductAttributePageToLoad"/> - <scrollTo stepKey="scrollToPriceCheckBox" selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" x="0" y="-160"/> - <click selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="selectPriceCheckBox"/> - <fillField stepKey="fillPrice" selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="90.99"/> - <click stepKey="clickOnSaveButton" selector="{{AdminEditProductAttributesSection.Save}}"/> - <waitForPageLoad stepKey="waitForUpdatedProductToSave" /> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + <actionGroup ref="AdminClickMassUpdateProductAttributesActionGroup" stepKey="clickDropdown"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickChangeStatus"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForProductAttributePageToLoad"/> + + <actionGroup ref="AdminSetPriceForMassUpdateActionGroup" stepKey="scrollToPriceCheckBox"> + <argument name="price" value="90.99"/> + </actionGroup> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="selectPriceCheckBox"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="fillPrice"/> + + <actionGroup ref="AdminSaveProductsMassAttributesUpdateActionGroup" stepKey="clickOnSaveButton"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForUpdatedProductToSave"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeAttributeUpateSuccessMsg"/> <!-- Start message queue --> <actionGroup ref="CliConsumerStartActionGroup" stepKey="startMessageQueueConsumer"> @@ -64,7 +72,21 @@ <magentoCLI command="cron:run --group=index" stepKey="runCron"/> <!--Verify product name, sku and updated price--> - <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$simpleProduct1.sku$$)}}"/> + <!-- <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToFirstProductEditPage"> + <argument name="productId" value="$$simpleProduct1.id$$"/> + </actionGroup> --> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="waitForFirstProductToLoad"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + + <!-- <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondProductEditPage"> + <argument name="productId" value="$$simpleProduct2.id$$"/> + </actionGroup> --> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="waitForSecondProductToLoad"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + + <!-- <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$simpleProduct1.sku$$)}}"/> <waitForPageLoad stepKey="waitForFirstProductToLoad"/> <seeInField stepKey="seeFirstProductNameInField" selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct1.name$$"/> <seeInField stepKey="seeFirstProductSkuInField" selector="{{AdminProductFormSection.productSku}}" userInput="$$simpleProduct1.sku$$"/> @@ -75,6 +97,6 @@ <waitForPageLoad stepKey="waitForSecondProductToLoad"/> <seeInField stepKey="seeSecondProductNameInField" selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct2.name$$"/> <seeInField stepKey="seeSecondProductSkuInField" selector="{{AdminProductFormSection.productSku}}" userInput="$$simpleProduct2.sku$$"/> - <seeInField stepKey="seeSecondProductPriceInField" selector="{{AdminProductFormSection.productPrice}}" userInput="90.99"/> + <seeInField stepKey="seeSecondProductPriceInField" selector="{{AdminProductFormSection.productPrice}}" userInput="90.99"/> --> </test> </tests> From 131cd1aae2a397e81457c0daf3962db39d5c0072 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Wed, 25 Nov 2020 17:06:44 +0200 Subject: [PATCH 010/112] Refactored AdminMassProductPriceUpdateTest --- ...oductPriceUpdatedOnEditPageActionGroup.xml | 25 +++++++++ .../Test/AdminMassProductPriceUpdateTest.xml | 54 +++++++------------ 2 files changed, 45 insertions(+), 34 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml new file mode 100644 index 0000000000000..c7a665119b328 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminProductPriceUpdatedOnEditPageActionGroup" extends="OpenEditProductOnBackendActionGroup"> + <annotations> + <description>Validate if Product price is updated on the Product creation/edit page</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + <argument name="price" type="string"/> + </arguments> + + <waitForPageLoad stepKey="waitForProductToLoad"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="seeProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="seeProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{price}}" stepKey="seeProductPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index b3cf6e357fd6a..42719e2d6aa12 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -25,22 +25,16 @@ </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <!--Open Product Index Page--> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndex"/> - <!--Search products using keyword --> - <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> - <argument name="keyword" value="Testp"/> - </actionGroup> + <actionGroup ref="ClearFiltersAdminProductGridActionGroup" stepKey="searchByKeyword"/> - <!--Sort Products by ID in descending order--> <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> - <!--Select products--> <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectFirstProduct"> <argument name="product" value="$simpleProduct1$"/> </actionGroup> @@ -48,7 +42,6 @@ <argument name="product" value="$simpleProduct2$"/> </actionGroup> - <!-- Update product price--> <actionGroup ref="AdminClickMassUpdateProductAttributesActionGroup" stepKey="clickDropdown"/> <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickChangeStatus"/> <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForProductAttributePageToLoad"/> @@ -63,40 +56,33 @@ <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForUpdatedProductToSave"/> <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeAttributeUpateSuccessMsg"/> - <!-- Start message queue --> <actionGroup ref="CliConsumerStartActionGroup" stepKey="startMessageQueueConsumer"> <argument name="consumerName" value="{{AdminProductAttributeUpdateConsumerData.consumerName}}"/> <argument name="maxMessages" value="{{AdminProductAttributeUpdateConsumerData.messageLimit}}"/> </actionGroup> - <!-- Run cron --> + <magentoCLI command="cron:run --group=index" stepKey="runCron"/> - <!--Verify product name, sku and updated price--> - <!-- <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToFirstProductEditPage"> - <argument name="productId" value="$$simpleProduct1.id$$"/> - </actionGroup> --> - <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="waitForFirstProductToLoad"> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openFirstProduct"/> + <actionGroup ref="AssertAdminProductPriceUpdatedOnEditPageActionGroup" stepKey="waitForFirstProductToLoad"> <argument name="product" value="$$simpleProduct1$$"/> + <argument name="price" value="90.99"/> </actionGroup> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductNameInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductSkuInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductPriceInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickOnBackButton"/> - <!-- <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondProductEditPage"> - <argument name="productId" value="$$simpleProduct2.id$$"/> - </actionGroup> --> - <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="waitForSecondProductToLoad"> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="waitForProductsToLoad"/> + + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openSecondProduct"/> + <actionGroup ref="AssertAdminProductPriceUpdatedOnEditPageActionGroup" stepKey="waitForSecondProductToLoad"> <argument name="product" value="$$simpleProduct2$$"/> - </actionGroup> - - <!-- <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$simpleProduct1.sku$$)}}"/> - <waitForPageLoad stepKey="waitForFirstProductToLoad"/> - <seeInField stepKey="seeFirstProductNameInField" selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct1.name$$"/> - <seeInField stepKey="seeFirstProductSkuInField" selector="{{AdminProductFormSection.productSku}}" userInput="$$simpleProduct1.sku$$"/> - <seeInField stepKey="seeFirstProductPriceInField" selector="{{AdminProductFormSection.productPrice}}" userInput="90.99"/> - <click stepKey="clickOnBackButton" selector="{{AdminGridMainControls.back}}"/> - <waitForPageLoad stepKey="waitForProductsToLoad"/> - <click stepKey="openSecondProduct" selector="{{AdminProductGridSection.productRowBySku($$simpleProduct2.sku$$)}}"/> - <waitForPageLoad stepKey="waitForSecondProductToLoad"/> - <seeInField stepKey="seeSecondProductNameInField" selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct2.name$$"/> - <seeInField stepKey="seeSecondProductSkuInField" selector="{{AdminProductFormSection.productSku}}" userInput="$$simpleProduct2.sku$$"/> - <seeInField stepKey="seeSecondProductPriceInField" selector="{{AdminProductFormSection.productPrice}}" userInput="90.99"/> --> + <argument name="price" value="90.99"/> + </actionGroup> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductNameInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductSkuInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductPriceInField"/> + </test> </tests> From bdb653dc3ff44f1ce1486935e27873620ea3f3bd Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Wed, 25 Nov 2020 17:14:32 +0200 Subject: [PATCH 011/112] delete unneeded file Please enter the commit message for your changes. Lines starting --- ...inCheckProductOnProductGridActionGroup.xml | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml deleted file mode 100644 index 34c9c1edf50cb..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminCheckProductOnProductGridActionGroup"> - <annotations> - <description>Check the product on the Product Grid</description> - </annotations> - <arguments> - <argument name="product" type="entity"/> - </arguments> - - <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku(product.sku)}}" stepKey="selectProduct"/> - - </actionGroup> -</actionGroups> From faf0c872ac4e03555a278009f12743c0ef0b8412 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Wed, 25 Nov 2020 17:22:58 +0200 Subject: [PATCH 012/112] revert deletion --- ...inCheckProductOnProductGridActionGroup.xml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml new file mode 100644 index 0000000000000..3bd68e80810e6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCheckProductOnProductGridActionGroup"> + <annotations> + <description>Check the product on the Product Grid</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + </arguments> + + <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku(product.sku)}}" stepKey="selectProduct"/> + + </actionGroup> +</actionGroups> \ No newline at end of file From dd4334df1a3c28e4ee1c84e782971d77c15622d6 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Wed, 25 Nov 2020 17:29:51 +0200 Subject: [PATCH 013/112] extended decription fo Action Group Please enter the commit message for your changes. Lines starting --- .../ActionGroup/AdminCheckProductOnProductGridActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml index 3bd68e80810e6..aebd595a919f8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCheckProductOnProductGridActionGroup"> <annotations> - <description>Check the product on the Product Grid</description> + <description>Check the checkbox for the product on the Product Grid</description> </annotations> <arguments> <argument name="product" type="entity"/> From 758b0d64067f7e3091b0a4a550b0a2a79e79322e Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Mon, 30 Nov 2020 11:01:33 +0200 Subject: [PATCH 014/112] refactoring --- .../ActionGroup/AdminCheckProductOnProductGridActionGroup.xml | 2 +- .../Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml index aebd595a919f8..2faeea1cc299b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml @@ -19,4 +19,4 @@ <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku(product.sku)}}" stepKey="selectProduct"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index 42719e2d6aa12..63277e2ed88fd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -25,7 +25,7 @@ </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> From ce7a3517b4d08ecd4b07eefcbf7e791e4f94db6b Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Mon, 30 Nov 2020 17:06:52 +0200 Subject: [PATCH 015/112] refactored --- .../Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 1916fb9ebdbbb..c2070043e63de 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -90,4 +90,4 @@ <click selector="{{AdminInvoicesGridSection.firstRow}}" stepKey="clickInvoice2"/> <see selector="{{AdminInvoiceDetailsInformationSection.orderStatus}}" userInput="Processing" stepKey="seeOrderStatus2"/> </test> -</tests> \ No newline at end of file +</tests> From 52e5d2cb33ddf9412e9fda9ef0694013f9796938 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Wed, 2 Dec 2020 17:19:59 +0200 Subject: [PATCH 016/112] autoformatted --- ...inCheckProductOnProductGridActionGroup.xml | 25 ++++---- .../AdminSetPriceForMassUpdateActionGroup.xml | 7 +-- ...oductPriceUpdatedOnEditPageActionGroup.xml | 5 +- .../Test/AdminMassProductPriceUpdateTest.xml | 57 +++++++++---------- 4 files changed, 45 insertions(+), 49 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml index 2faeea1cc299b..4216f4eedfda6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0" encoding="UTF-8"?> <!-- /** * Copyright © Magento, Inc. All rights reserved. @@ -6,17 +6,16 @@ */ --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminCheckProductOnProductGridActionGroup"> - <annotations> - <description>Check the checkbox for the product on the Product Grid</description> - </annotations> - <arguments> - <argument name="product" type="entity"/> - </arguments> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCheckProductOnProductGridActionGroup"> + <annotations> + <description>Check the checkbox for the product on the Product Grid</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + </arguments> - <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku(product.sku)}}" stepKey="selectProduct"/> + <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku(product.sku)}}" stepKey="selectProduct"/> - </actionGroup> -</actionGroups> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml index b00f181c92360..0bae2a2f627a5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml @@ -6,8 +6,7 @@ */ --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSetPriceForMassUpdateActionGroup"> <annotations> <description>Click the "Change" checkbox for the "Price" field. Set new price.</description> @@ -16,9 +15,9 @@ <argument name="price" type="string"/> </arguments> - <scrollTo stepKey="scrollToPriceCheckBox" selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" x="0" y="-160"/> + <scrollTo stepKey="scrollToPriceCheckBox" selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" x="0" y="-160"/> <click selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="selectPriceCheckBox"/> <fillField stepKey="fillPrice" selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="{{price}}"/> </actionGroup> -</actionGroups> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml index c7a665119b328..42577f5e7735d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml @@ -6,8 +6,7 @@ */ --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertAdminProductPriceUpdatedOnEditPageActionGroup" extends="OpenEditProductOnBackendActionGroup"> <annotations> <description>Validate if Product price is updated on the Product creation/edit page</description> @@ -22,4 +21,4 @@ <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="seeProductSku"/> <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{price}}" stepKey="seeProductPrice"/> </actionGroup> -</actionGroups> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index 63277e2ed88fd..f76ff23a3ec97 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -6,8 +6,7 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassProductPriceUpdateTest"> <annotations> <stories value="Mass product update"/> @@ -36,25 +35,25 @@ <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectFirstProduct"> - <argument name="product" value="$simpleProduct1$"/> + <argument name="product" value="$simpleProduct1$"/> </actionGroup> - <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectSecondProduct"> - <argument name="product" value="$simpleProduct2$"/> + <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectSecondProduct"> + <argument name="product" value="$simpleProduct2$"/> </actionGroup> <actionGroup ref="AdminClickMassUpdateProductAttributesActionGroup" stepKey="clickDropdown"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickChangeStatus"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForProductAttributePageToLoad"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickChangeStatus"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForProductAttributePageToLoad"/> - <actionGroup ref="AdminSetPriceForMassUpdateActionGroup" stepKey="scrollToPriceCheckBox"> - <argument name="price" value="90.99"/> - </actionGroup> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="selectPriceCheckBox"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="fillPrice"/> + <actionGroup ref="AdminSetPriceForMassUpdateActionGroup" stepKey="scrollToPriceCheckBox"> + <argument name="price" value="90.99"/> + </actionGroup> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="selectPriceCheckBox"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="fillPrice"/> <actionGroup ref="AdminSaveProductsMassAttributesUpdateActionGroup" stepKey="clickOnSaveButton"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForUpdatedProductToSave"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeAttributeUpateSuccessMsg"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForUpdatedProductToSave"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeAttributeUpateSuccessMsg"/> <actionGroup ref="CliConsumerStartActionGroup" stepKey="startMessageQueueConsumer"> <argument name="consumerName" value="{{AdminProductAttributeUpdateConsumerData.consumerName}}"/> @@ -63,26 +62,26 @@ <magentoCLI command="cron:run --group=index" stepKey="runCron"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openFirstProduct"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openFirstProduct"/> <actionGroup ref="AssertAdminProductPriceUpdatedOnEditPageActionGroup" stepKey="waitForFirstProductToLoad"> - <argument name="product" value="$$simpleProduct1$$"/> - <argument name="price" value="90.99"/> + <argument name="product" value="$$simpleProduct1$$"/> + <argument name="price" value="90.99"/> </actionGroup> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductNameInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductSkuInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductPriceInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickOnBackButton"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductNameInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductSkuInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductPriceInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickOnBackButton"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="waitForProductsToLoad"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openSecondProduct"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openSecondProduct"/> <actionGroup ref="AssertAdminProductPriceUpdatedOnEditPageActionGroup" stepKey="waitForSecondProductToLoad"> - <argument name="product" value="$$simpleProduct2$$"/> - <argument name="price" value="90.99"/> - </actionGroup> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductNameInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductSkuInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductPriceInField"/> + <argument name="product" value="$$simpleProduct2$$"/> + <argument name="price" value="90.99"/> + </actionGroup> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductNameInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductSkuInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductPriceInField"/> </test> -</tests> +</tests> \ No newline at end of file From a048fb7c242556759d8d15081340906b48ed26ab Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Wed, 2 Dec 2020 17:30:29 +0200 Subject: [PATCH 017/112] corrected spacing --- .../AdminSetPriceForMassUpdateActionGroup.xml | 7 ++- ...oductPriceUpdatedOnEditPageActionGroup.xml | 5 +- .../Test/AdminMassProductPriceUpdateTest.xml | 57 ++++++++++--------- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml index 0bae2a2f627a5..b00f181c92360 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetPriceForMassUpdateActionGroup.xml @@ -6,7 +6,8 @@ */ --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSetPriceForMassUpdateActionGroup"> <annotations> <description>Click the "Change" checkbox for the "Price" field. Set new price.</description> @@ -15,9 +16,9 @@ <argument name="price" type="string"/> </arguments> - <scrollTo stepKey="scrollToPriceCheckBox" selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" x="0" y="-160"/> + <scrollTo stepKey="scrollToPriceCheckBox" selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" x="0" y="-160"/> <click selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="selectPriceCheckBox"/> <fillField stepKey="fillPrice" selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="{{price}}"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml index 42577f5e7735d..c7a665119b328 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductPriceUpdatedOnEditPageActionGroup.xml @@ -6,7 +6,8 @@ */ --> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertAdminProductPriceUpdatedOnEditPageActionGroup" extends="OpenEditProductOnBackendActionGroup"> <annotations> <description>Validate if Product price is updated on the Product creation/edit page</description> @@ -21,4 +22,4 @@ <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="seeProductSku"/> <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{price}}" stepKey="seeProductPrice"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index f76ff23a3ec97..63277e2ed88fd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -6,7 +6,8 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassProductPriceUpdateTest"> <annotations> <stories value="Mass product update"/> @@ -35,25 +36,25 @@ <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectFirstProduct"> - <argument name="product" value="$simpleProduct1$"/> + <argument name="product" value="$simpleProduct1$"/> </actionGroup> - <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectSecondProduct"> - <argument name="product" value="$simpleProduct2$"/> + <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectSecondProduct"> + <argument name="product" value="$simpleProduct2$"/> </actionGroup> <actionGroup ref="AdminClickMassUpdateProductAttributesActionGroup" stepKey="clickDropdown"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickChangeStatus"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForProductAttributePageToLoad"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickChangeStatus"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForProductAttributePageToLoad"/> - <actionGroup ref="AdminSetPriceForMassUpdateActionGroup" stepKey="scrollToPriceCheckBox"> - <argument name="price" value="90.99"/> - </actionGroup> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="selectPriceCheckBox"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="fillPrice"/> + <actionGroup ref="AdminSetPriceForMassUpdateActionGroup" stepKey="scrollToPriceCheckBox"> + <argument name="price" value="90.99"/> + </actionGroup> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="selectPriceCheckBox"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="fillPrice"/> <actionGroup ref="AdminSaveProductsMassAttributesUpdateActionGroup" stepKey="clickOnSaveButton"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForUpdatedProductToSave"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeAttributeUpateSuccessMsg"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForUpdatedProductToSave"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeAttributeUpateSuccessMsg"/> <actionGroup ref="CliConsumerStartActionGroup" stepKey="startMessageQueueConsumer"> <argument name="consumerName" value="{{AdminProductAttributeUpdateConsumerData.consumerName}}"/> @@ -62,26 +63,26 @@ <magentoCLI command="cron:run --group=index" stepKey="runCron"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openFirstProduct"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openFirstProduct"/> <actionGroup ref="AssertAdminProductPriceUpdatedOnEditPageActionGroup" stepKey="waitForFirstProductToLoad"> - <argument name="product" value="$$simpleProduct1$$"/> - <argument name="price" value="90.99"/> + <argument name="product" value="$$simpleProduct1$$"/> + <argument name="price" value="90.99"/> </actionGroup> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductNameInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductSkuInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductPriceInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickOnBackButton"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductNameInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductSkuInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeFirstProductPriceInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickOnBackButton"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="waitForProductsToLoad"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openSecondProduct"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openSecondProduct"/> <actionGroup ref="AssertAdminProductPriceUpdatedOnEditPageActionGroup" stepKey="waitForSecondProductToLoad"> - <argument name="product" value="$$simpleProduct2$$"/> - <argument name="price" value="90.99"/> - </actionGroup> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductNameInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductSkuInField"/> - <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductPriceInField"/> + <argument name="product" value="$$simpleProduct2$$"/> + <argument name="price" value="90.99"/> + </actionGroup> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductNameInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductSkuInField"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSecondProductPriceInField"/> </test> -</tests> \ No newline at end of file +</tests> From 6fc419105aa467bc909305775e8398d151969f0f Mon Sep 17 00:00:00 2001 From: Anna Pak <58164147+AnnaAPak@users.noreply.github.com> Date: Fri, 4 Dec 2020 09:33:05 +0200 Subject: [PATCH 018/112] Added line --- .../ActionGroup/AdminCheckProductOnProductGridActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml index 4216f4eedfda6..627659062b133 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml @@ -18,4 +18,4 @@ <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku(product.sku)}}" stepKey="selectProduct"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> From 64b617f95788ea499ced46dd620bf70a07868478 Mon Sep 17 00:00:00 2001 From: Anna Pak <58164147+AnnaAPak@users.noreply.github.com> Date: Fri, 4 Dec 2020 09:34:35 +0200 Subject: [PATCH 019/112] Added space --- .../Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index 63277e2ed88fd..8a5da6d6e3640 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -38,7 +38,7 @@ <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectFirstProduct"> <argument name="product" value="$simpleProduct1$"/> </actionGroup> - <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectSecondProduct"> + <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="selectSecondProduct"> <argument name="product" value="$simpleProduct2$"/> </actionGroup> From 9691ef605f9f46e4ee0d4b478dc0d1f736f1d125 Mon Sep 17 00:00:00 2001 From: Anna Pak <58164147+AnnaAPak@users.noreply.github.com> Date: Fri, 4 Dec 2020 09:43:49 +0200 Subject: [PATCH 020/112] corrected spacing --- .../ActionGroup/AdminCheckProductOnProductGridActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml index 627659062b133..0e98db3e5d8a8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductOnProductGridActionGroup.xml @@ -9,10 +9,10 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCheckProductOnProductGridActionGroup"> <annotations> - <description>Check the checkbox for the product on the Product Grid</description> + <description>Check the checkbox for the product on the Product Grid</description> </annotations> <arguments> - <argument name="product" type="entity"/> + <argument name="product" type="entity"/> </arguments> <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku(product.sku)}}" stepKey="selectProduct"/> From 2f10855d003758de54fb23c83a972de25b6a26a7 Mon Sep 17 00:00:00 2001 From: Vova Yatsyuk <vova.yatsyuk@gmail.com> Date: Fri, 4 Dec 2020 10:40:19 +0200 Subject: [PATCH 021/112] Allow to load base64 encoded images and fonts --- app/code/Magento/Csp/etc/config.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/code/Magento/Csp/etc/config.xml b/app/code/Magento/Csp/etc/config.xml index 6e2235479da93..07761995fdbca 100644 --- a/app/code/Magento/Csp/etc/config.xml +++ b/app/code/Magento/Csp/etc/config.xml @@ -92,6 +92,9 @@ <inline>1</inline> <eval>0</eval> <dynamic>0</dynamic> + <schemes> + <data>data</data> + </schemes> </images> <frames> <policy_id>frame-src</policy_id> @@ -120,6 +123,9 @@ <inline>1</inline> <eval>0</eval> <dynamic>0</dynamic> + <schemes> + <data>data</data> + </schemes> </fonts> </storefront> <admin> @@ -197,6 +203,9 @@ <inline>1</inline> <eval>0</eval> <dynamic>0</dynamic> + <schemes> + <data>data</data> + </schemes> </images> <frames> <policy_id>frame-src</policy_id> @@ -225,6 +234,9 @@ <inline>1</inline> <eval>0</eval> <dynamic>0</dynamic> + <schemes> + <data>data</data> + </schemes> </fonts> </admin> </policies> From 41c3aa20144741faa1a5935c1764416872166474 Mon Sep 17 00:00:00 2001 From: Renon Stewart <srenon@users.noreply.github.com> Date: Tue, 8 Dec 2020 01:16:25 -0500 Subject: [PATCH 022/112] Issues 31197 - Loading wrong order tax info --- app/code/Magento/Sales/Block/Adminhtml/Order/Totals/Tax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Totals/Tax.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Totals/Tax.php index e923b006a0ac6..1f8d0a0bc265d 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Totals/Tax.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Totals/Tax.php @@ -95,7 +95,7 @@ public function getFullTaxInfo() $taxClassAmount = $this->_taxHelper->getCalculatedTaxes($source); if (empty($taxClassAmount)) { - $rates = $this->_taxOrderFactory->create()->getCollection()->loadByOrder($source)->toArray(); + $rates = $this->_taxOrderFactory->create()->getCollection()->loadByOrder($this->getOrder())->toArray(); $taxClassAmount = $this->_taxCalculation->reproduceProcess($rates['items']); } From 8b5549cc4e92c64720b6e7b802eca7b9512229ba Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Wed, 9 Dec 2020 00:53:21 -0600 Subject: [PATCH 023/112] MC-39353: Incorrect Quantity Shipped Displayed on order detail page on the front - MFTF test and the solution --- .../StorefrontCustomerOrderShipmentPage.xml | 14 +++ ...ontOrderShipmentsQtyShippedActionGroup.xml | 24 ++++ .../StorefrontSalesOrderShipmentSection.xml | 14 +++ ...ifyOrderShipmentForDecimalQuantityTest.xml | 104 ++++++++++++++++++ .../shipment/items/renderer/default.phtml | 2 +- 5 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderShipmentPage.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontOrderShipmentsQtyShippedActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/StorefrontSalesOrderShipmentSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/StorefrontVerifyOrderShipmentForDecimalQuantityTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderShipmentPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderShipmentPage.xml new file mode 100644 index 0000000000000..da41e6ada79a0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerOrderShipmentPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerOrderShipmentPage" url="sales/order/shipment/order_id/{{var1}}" area="storefront" module="Magento_Customer" parameterized="true"> + <section name="StorefrontCustomerOrderSection"/> + </page> +</pages> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontOrderShipmentsQtyShippedActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontOrderShipmentsQtyShippedActionGroup.xml new file mode 100644 index 0000000000000..0c3544f8944ed --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontOrderShipmentsQtyShippedActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontOrderShipmentsQtyShippedActionGroup"> + <annotations> + <description>Verify Customer Order Shipments Qty Shipped</description> + </annotations> + <arguments> + <argument name="expectedQtyShipped" type="string" defaultValue="0"/> + </arguments> + <grabTextFrom selector="{{StorefrontSalesOrderShipmentSection.salesOrderQtyShipped}}" stepKey="grabSalesOrderQtyShipped"/> + <assertEquals stepKey="assertOrderQtyShipped"> + <actualResult type="string">$grabSalesOrderQtyShipped</actualResult> + <expectedResult type="string">{{expectedQtyShipped}}</expectedResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontSalesOrderShipmentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontSalesOrderShipmentSection.xml new file mode 100644 index 0000000000000..e2e814efb9f02 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontSalesOrderShipmentSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontSalesOrderShipmentSection"> + <element name="salesOrderQtyShipped" type="text" selector="//td[@data-th='Qty Shipped']"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontVerifyOrderShipmentForDecimalQuantityTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontVerifyOrderShipmentForDecimalQuantityTest.xml new file mode 100644 index 0000000000000..b086074132e85 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontVerifyOrderShipmentForDecimalQuantityTest.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontVerifyOrderShipmentForDecimalQuantityTest"> + <annotations> + <title value="Incorrect Quantity Shipped Displayed on order detail page on the front"/> + <stories value="Verify shipment quantity for decimal quantity at frontend order shipment tab"/> + <description value="Verify shipment quantity for decimal quantity at frontend order shipment tab"/> + <features value="Sales"/> + <testCaseId value="MC-39777"/> + <useCaseId value="MC-39353"/> + <severity value="MAJOR"/> + <group value="Sales"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createSimpleCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createSimpleCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer"> + <field key="group_id">1</field> + </createData> + </before> + <after> + <!--Clear Filters--> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="ClearFiltersAfter"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderListingFilters"/> + <deleteData createDataKey="createSimpleCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deletePreReqSimpleProduct"/> + <!--Logout from customer account--> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomerOne"/> + <waitForPageLoad stepKey="waitLogoutCustomerOne"/> + <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Step1. Login as admin. Go to Catalog > Products page. Filtering *prod1*. Open *prod1* to edit--> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin" /> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="filterGroupedProductOptions"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!-- Step2. Update product Advanced Inventory Setting. + Set *Qty Uses Decimals* to *Yes* and *Enable Qty Increments* to *Yes* and *Qty Increments* to *2.14*. --> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProduct"/> + <actionGroup ref="AdminClickOnAdvancedInventoryLinkActionGroup" stepKey="clickOnAdvancedInventoryLink"/> + <actionGroup ref="AdminSetQtyUsesDecimalsConfigActionGroup" stepKey="setQtyUsesDecimalsConfig"> + <argument name="value" value="Yes"/> + </actionGroup> + <actionGroup ref="AdminSetEnableQtyIncrementsActionGroup" stepKey="setEnableQtyIncrements"> + <argument name="value" value="Yes"/> + </actionGroup> + <actionGroup ref="AdminSetQtyIncrementsForProductActionGroup" stepKey="setQtyIncrementsValue"> + <argument name="qty" value="2.14"/> + </actionGroup> + <actionGroup ref="AdminSetMinAllowedQtyForProductActionGroup" stepKey="fillMiniAllowedQty"> + <argument name="qty" value="2.14"/> + </actionGroup> + <actionGroup ref="AdminSubmitAdvancedInventoryFormActionGroup" stepKey="clickOnDoneButton"/> + + <!--Step3. Save the product--> + <actionGroup ref="AdminProductFormSaveActionGroup" stepKey="clickOnSaveButton"/> + <!--Step4. Open *Customer view* (Go to *Store Front*). Open *prod1* page (Find via search and click on product name) --> + <!--Step5. Log in to Storefront as Customer--> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUpNewUser"> + <argument name="Customer" value="$$createSimpleUsCustomer$$"/> + </actionGroup> + <!--Step6. Go to product page--> + <amOnPage url="$$createSimpleProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad"/> + <!--Step7. Add Product to Shopping Cart--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + </actionGroup> + + <!--Step8. Navigate to checkout--> + <actionGroup ref="StorefrontOpenCheckoutPageActionGroup" stepKey="openCheckoutPage"/> + <!--Step9. Click next button to open payment section--> + <actionGroup ref="StorefrontCheckoutClickNextButtonActionGroup" stepKey="clickNext"/> + <!--Step10. Click place order--> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="placeOrder"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <!--Step11. Go to admin Order page for newly created order--> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="filterOrdersGridById"> + <argument name="orderId" value="{$grabOrderNumber}"/> + </actionGroup> + + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> + + <actionGroup ref="StorefrontNavigateToCustomerOrdersHistoryPageActionGroup" stepKey="goToOrderHistoryPage"/> + <!--Step12. Go to Customer Order Shipment Page and Checking the correctness of displayed Qty Shipped --> + <amOnPage url="{{StorefrontCustomerOrderShipmentPage.url({$grabOrderNumber})}}" stepKey="amOnOrderShipmentPage"/> + <waitForPageLoad time="30" stepKey="waitForOrderShipmentsPageLoad"/> + <actionGroup ref="AssertStorefrontOrderShipmentsQtyShippedActionGroup" stepKey="verifyAssertOrderShipments"> + <argument name="expectedQtyShipped" value="2.14"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml index 6c7567a8cd14b..d2caa1a2ce935 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml @@ -42,5 +42,5 @@ <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"> <?= /* @noEscape */ $block->prepareSku($block->getSku()) ?> </td> - <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty Shipped')) ?>"><?= (int) $_item->getQty() ?></td> + <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty Shipped')) ?>"><?= (float) $_item->getQty() ?></td> </tr> From 8ba64a2f3901149a1f42a798385803d24c2e5013 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Wed, 9 Dec 2020 17:37:32 -0600 Subject: [PATCH 024/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../Magento/UrlRewrite/Model/UrlRewrite.php | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index 5f29008621c1a..0f72116ac7614 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -7,8 +7,17 @@ namespace Magento\UrlRewrite\Model; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\Page; use Magento\Framework\App\ObjectManager; +use Magento\Framework\EntityManager\EventManager; +use Magento\Framework\Indexer\CacheContext; use Magento\Framework\Serialize\Serializer\Json; +use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; /** * UrlRewrite model class @@ -92,4 +101,58 @@ public function setMetadata($metadata) } return $this->setData(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::METADATA, $metadata); } + + private function opt1() { + $map = [ + Rewrite::ENTITY_TYPE_PRODUCT => Product::CACHE_TAG, + Rewrite::ENTITY_TYPE_CATEGORY => Category::CACHE_TAG, + Rewrite::ENTITY_TYPE_CMS_PAGE => Page::CACHE_TAG + ]; + + if ($this->getEntityType() !== Rewrite::ENTITY_TYPE_CUSTOM) { + $cacheKey = $map[$this->getEntityType()]; + + $cacheContext = ObjectManager::getInstance()->get(CacheContext::class); + $eventManager = ObjectManager::getInstance()->get(EventManager::class); + + $cacheContext->registerEntities($cacheKey, [$this->getEntityId()]); + $eventManager->dispatch('clean_cache_by_tags', ['object' => $cacheContext]); + } + } + + private function opt2() { + $map = [ + Rewrite::ENTITY_TYPE_PRODUCT => function ($prodId) { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); + return $productRepository->getById($prodId); + }, + Rewrite::ENTITY_TYPE_CATEGORY => function ($catId) { + /** @var CategoryRepositoryInterface $productRepository */ + $categoryRepository = ObjectManager::getInstance()->get(CategoryRepositoryInterface::class); + return $categoryRepository->get($catId); + }, + Rewrite::ENTITY_TYPE_CMS_PAGE => function ($cmsId) { + /** @var PageRepositoryInterface $productRepository */ + $pageRepository = ObjectManager::getInstance()->get(PageRepositoryInterface::class); + return $pageRepository->getById($cmsId); + }, + Rewrite::ENTITY_TYPE_CUSTOM => false + ]; + + $getter = $map[$this->getEntityType()]; + + if ($getter) { + $entity = $getter($this->getEntityId()); + + $entityManager = ObjectManager::getInstance()->get(EventManager::class); + $entityManager->dispatch('clean_cache_by_tags', ['object' => $entity]); + } + } + + public function afterSave() + { + $this->opt1(); + return parent::afterSave(); // TODO: Change the autogenerated stub + } } From 9c797541a473849301d58f29cb264fff3e850345 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Thu, 10 Dec 2020 07:39:07 -0600 Subject: [PATCH 025/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../Magento/UrlRewrite/Model/UrlRewrite.php | 120 +++++++++--------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index 0f72116ac7614..708811eccd9cc 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -7,17 +7,20 @@ namespace Magento\UrlRewrite\Model; -use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Product; -use Magento\Cms\Api\PageRepositoryInterface; use Magento\Cms\Model\Page; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\EntityManager\EventManager; use Magento\Framework\Indexer\CacheContext; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; /** * UrlRewrite model class @@ -36,32 +39,49 @@ * @method UrlRewrite setStoreId($value) * @method UrlRewrite setDescription($value) */ -class UrlRewrite extends \Magento\Framework\Model\AbstractModel +class UrlRewrite extends AbstractModel { /** * @var Json */ private $serializer; + /** + * @var CacheContext|mixed|null + */ + private $cacheContext; + + /** + * @var EventManager|mixed|null + */ + private $eventManager; + /** * UrlRewrite constructor. * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource - * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param Context $context + * @param Registry $registry + * @param AbstractResource|null $resource + * @param AbstractDb|null $resourceCollection * @param array $data - * @param Json $serializer + * @param Json|null $serializer + * @param CacheContext|null $cacheContext + * @param EventManager|null $eventManager */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + Context $context, + Registry $registry, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, array $data = [], - Json $serializer = null - ) { + Json $serializer = null, + CacheContext $cacheContext = null, + EventManager $eventManager = null + ) + { $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->cacheContext = $cacheContext ?: ObjectManager::getInstance()->get(CacheContext::class); + $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(EventManager::class); parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -72,8 +92,8 @@ public function __construct( */ protected function _construct() { - $this->_init(\Magento\UrlRewrite\Model\ResourceModel\UrlRewrite::class); - $this->_collectionName = \Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection::class; + $this->_init(ResourceModel\UrlRewrite::class); + $this->_collectionName = UrlRewriteCollection::class; } /** @@ -102,57 +122,37 @@ public function setMetadata($metadata) return $this->setData(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::METADATA, $metadata); } - private function opt1() { - $map = [ - Rewrite::ENTITY_TYPE_PRODUCT => Product::CACHE_TAG, - Rewrite::ENTITY_TYPE_CATEGORY => Category::CACHE_TAG, - Rewrite::ENTITY_TYPE_CMS_PAGE => Page::CACHE_TAG - ]; - - if ($this->getEntityType() !== Rewrite::ENTITY_TYPE_CUSTOM) { - $cacheKey = $map[$this->getEntityType()]; + /** + * Clean cache for the entity which was affected by updating UrlRewrite + * + * @param $entityType + * @param $entityId + */ + private function cleanCacheForEntity($entityType, $entityId) + { + if ($entityType !== Rewrite::ENTITY_TYPE_CUSTOM) { + $map = [ + Rewrite::ENTITY_TYPE_PRODUCT => Product::CACHE_TAG, + Rewrite::ENTITY_TYPE_CATEGORY => Category::CACHE_TAG, + Rewrite::ENTITY_TYPE_CMS_PAGE => Page::CACHE_TAG + ]; - $cacheContext = ObjectManager::getInstance()->get(CacheContext::class); - $eventManager = ObjectManager::getInstance()->get(EventManager::class); + $cacheKey = $map[$entityType]; - $cacheContext->registerEntities($cacheKey, [$this->getEntityId()]); - $eventManager->dispatch('clean_cache_by_tags', ['object' => $cacheContext]); + $this->cacheContext->registerEntities($cacheKey, [$entityId]); + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); } } - private function opt2() { - $map = [ - Rewrite::ENTITY_TYPE_PRODUCT => function ($prodId) { - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); - return $productRepository->getById($prodId); - }, - Rewrite::ENTITY_TYPE_CATEGORY => function ($catId) { - /** @var CategoryRepositoryInterface $productRepository */ - $categoryRepository = ObjectManager::getInstance()->get(CategoryRepositoryInterface::class); - return $categoryRepository->get($catId); - }, - Rewrite::ENTITY_TYPE_CMS_PAGE => function ($cmsId) { - /** @var PageRepositoryInterface $productRepository */ - $pageRepository = ObjectManager::getInstance()->get(PageRepositoryInterface::class); - return $pageRepository->getById($cmsId); - }, - Rewrite::ENTITY_TYPE_CUSTOM => false - ]; - - $getter = $map[$this->getEntityType()]; - - if ($getter) { - $entity = $getter($this->getEntityId()); - - $entityManager = ObjectManager::getInstance()->get(EventManager::class); - $entityManager->dispatch('clean_cache_by_tags', ['object' => $entity]); - } + public function afterDelete() + { + $this->cleanCacheForEntity($this->getEntityType(), $this->getEntityId()); + return parent::afterDelete(); // TODO: Change the autogenerated stub } public function afterSave() { - $this->opt1(); - return parent::afterSave(); // TODO: Change the autogenerated stub + $this->cleanCacheForEntity($this->getEntityType(), $this->getEntityId()); + return parent::afterSave(); } } From a4798a0e43c1c8789e14e069baa60a02854d9bd9 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Thu, 10 Dec 2020 14:22:27 -0600 Subject: [PATCH 026/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- app/code/Magento/CatalogUrlRewrite/etc/di.xml | 8 +++++++ app/code/Magento/CmsUrlRewrite/etc/di.xml | 7 ++++++ .../Magento/UrlRewrite/Model/UrlRewrite.php | 22 ++++++++++--------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/etc/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/di.xml index 5fb7d33546d60..62b63d41f0501 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/di.xml @@ -56,6 +56,14 @@ </argument> </arguments> </type> + <type name="Magento\UrlRewrite\Model\UrlRewrite"> + <arguments> + <argument name="entityToCacheTagMap" xsi:type="array"> + <item name="product" xsi:type="const">Magento\Catalog\Model\Product::CACHE_TAG</item> + <item name="category" xsi:type="const">Magento\Catalog\Model\Category::CACHE_TAG</item> + </argument> + </arguments> + </type> <type name="Magento\Eav\Model\Config"> <arguments> <argument name="attributesForPreload" xsi:type="array"> diff --git a/app/code/Magento/CmsUrlRewrite/etc/di.xml b/app/code/Magento/CmsUrlRewrite/etc/di.xml index 497d7a175842d..0463bf5b696c5 100644 --- a/app/code/Magento/CmsUrlRewrite/etc/di.xml +++ b/app/code/Magento/CmsUrlRewrite/etc/di.xml @@ -9,4 +9,11 @@ <type name="Magento\Cms\Model\ResourceModel\Page"> <plugin name="cms_url_rewrite_plugin" type="Magento\CmsUrlRewrite\Plugin\Cms\Model\ResourceModel\Page"/> </type> + <type name="Magento\UrlRewrite\Model\UrlRewrite"> + <arguments> + <argument name="entityToCacheTagMap" xsi:type="array"> + <item name="cms-page" xsi:type="const">Magento\Cms\Model\Page::CACHE_TAG</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index 708811eccd9cc..363a3388daab0 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -56,6 +56,11 @@ class UrlRewrite extends AbstractModel */ private $eventManager; + /** + * @var array + */ + private $entityToCacheTagMap; + /** * UrlRewrite constructor. * @@ -67,6 +72,7 @@ class UrlRewrite extends AbstractModel * @param Json|null $serializer * @param CacheContext|null $cacheContext * @param EventManager|null $eventManager + * @param array $entityToCacheTagMap */ public function __construct( Context $context, @@ -76,12 +82,14 @@ public function __construct( array $data = [], Json $serializer = null, CacheContext $cacheContext = null, - EventManager $eventManager = null + EventManager $eventManager = null, + array $entityToCacheTagMap = [] ) { $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); $this->cacheContext = $cacheContext ?: ObjectManager::getInstance()->get(CacheContext::class); $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(EventManager::class); + $this->entityToCacheTagMap = $entityToCacheTagMap; parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -130,14 +138,8 @@ public function setMetadata($metadata) */ private function cleanCacheForEntity($entityType, $entityId) { - if ($entityType !== Rewrite::ENTITY_TYPE_CUSTOM) { - $map = [ - Rewrite::ENTITY_TYPE_PRODUCT => Product::CACHE_TAG, - Rewrite::ENTITY_TYPE_CATEGORY => Category::CACHE_TAG, - Rewrite::ENTITY_TYPE_CMS_PAGE => Page::CACHE_TAG - ]; - - $cacheKey = $map[$entityType]; + if ($entityType !== Rewrite::ENTITY_TYPE_CUSTOM && array_key_exists($entityType, $this->entityToCacheTagMap)) { + $cacheKey = $this->entityToCacheTagMap[$entityType]; $this->cacheContext->registerEntities($cacheKey, [$entityId]); $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); @@ -147,7 +149,7 @@ private function cleanCacheForEntity($entityType, $entityId) public function afterDelete() { $this->cleanCacheForEntity($this->getEntityType(), $this->getEntityId()); - return parent::afterDelete(); // TODO: Change the autogenerated stub + return parent::afterDelete(); } public function afterSave() From e8d2a5ece6479d6c1b2e3cf17f5245946e34420c Mon Sep 17 00:00:00 2001 From: Viktor Rad <vrad@adobe.com> Date: Mon, 14 Dec 2020 14:13:34 -0600 Subject: [PATCH 027/112] MC-39132: [MAGENTO CLOUD] PWA causing failed Magento Tester results --- .../Magento/Framework/Interception/PluginListGenerator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Interception/PluginListGenerator.php b/lib/internal/Magento/Framework/Interception/PluginListGenerator.php index effc291bb883b..8f3a03dd4fc20 100644 --- a/lib/internal/Magento/Framework/Interception/PluginListGenerator.php +++ b/lib/internal/Magento/Framework/Interception/PluginListGenerator.php @@ -227,8 +227,6 @@ public function loadScopedVirtualTypes($scopePriorityScheme, $loadedScopes, $plu $data = $this->reader->read($scopeCode) ?: []; unset($data['preferences']); if (count($data) > 0) { - $inherited = []; - $processed = []; $pluginData = $this->merge($data, $pluginData); foreach ($data as $class => $config) { if (isset($config['type'])) { @@ -236,6 +234,8 @@ public function loadScopedVirtualTypes($scopePriorityScheme, $loadedScopes, $plu } } } + $inherited = []; + $processed = []; $loadedScopes[$scopeCode] = true; } if ($this->isCurrentScope($scopeCode)) { From 93c78c070d17c5363c6ce5890d54e39fcdffebc0 Mon Sep 17 00:00:00 2001 From: Viktor Rad <vrad@adobe.com> Date: Mon, 14 Dec 2020 16:08:00 -0600 Subject: [PATCH 028/112] MC-39132: [MAGENTO CLOUD] PWA causing failed Magento Tester results --- .../Interception/PluginListGeneratorTest.php | 61 ++++++++++++++----- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Interception/PluginListGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Framework/Interception/PluginListGeneratorTest.php index 1046c678e253a..bda4cdd6cbaa5 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Interception/PluginListGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Interception/PluginListGeneratorTest.php @@ -23,7 +23,14 @@ class PluginListGeneratorTest extends TestCase /** * Generated plugin list config for frontend scope */ - const CACHE_ID = 'primary|global|frontend|plugin-list'; + const CACHE_ID_FRONTEND = 'primary|global|frontend|plugin-list'; + + /** + * Generated plugin list config for dummy scope + */ + const CACHE_ID_DUMMY = 'primary|global|dummy|plugin-list'; + + private $cacheIds = [self::CACHE_ID_FRONTEND, self::CACHE_ID_DUMMY]; /** * @var PluginListGenerator @@ -90,31 +97,51 @@ protected function setUp(): void */ public function testPluginListConfigGeneration() { - $scopes = ['frontend']; + $scopes = ['global', 'frontend', 'dummy']; + $globalPlugin = 'genericHeaderPlugin'; + $frontendPlugin = 'response-http-page-cache'; $this->model->write($scopes); - $configData = $this->model->load(self::CACHE_ID); - $this->assertNotEmpty($configData[0]); - $this->assertNotEmpty($configData[1]); - $this->assertNotEmpty($configData[2]); - $expected = [ + $configDataFrontend = $this->model->load(self::CACHE_ID_FRONTEND); + $this->assertNotEmpty($configDataFrontend[0]); + $this->assertNotEmpty($configDataFrontend[1]); + $this->assertNotEmpty($configDataFrontend[2]); + $expectedFrontend = [ 1 => [ - 0 => 'genericHeaderPlugin', - 1 => 'response-http-page-cache' + 0 => $globalPlugin, + 1 => $frontendPlugin ] ]; // Here in test is assumed that this class below has 3 plugins. But the amount of plugins and class itself // may vary. If it is changed, please update these assertions. $this->assertArrayHasKey( 'Magento\\Framework\\App\\Response\\Http_sendResponse___self', - $configData[2], + $configDataFrontend[2], 'Processed plugin does not exist in the processed plugins array.' ); $this->assertSame( - $expected, - $configData[2]['Magento\\Framework\\App\\Response\\Http_sendResponse___self'], + $expectedFrontend, + $configDataFrontend[2]['Magento\\Framework\\App\\Response\\Http_sendResponse___self'], 'Plugin configurations are not equal' ); + + $configDataDummy = $this->model->load(self::CACHE_ID_DUMMY); + /** + * Make sure "dummy" scope with no plugins in system should not contain plugins from "frontend" scope + */ + $this->assertNotContains( + $frontendPlugin, + $configDataDummy[2]['Magento\\Framework\\App\\Response\\Http_sendResponse___self'][1], + 'Plugin configurations are not equal. "dummy" scope should not contain plugins from "frontend" scope' + ); + /** + * Make sure "dummy" scope with no plugins in system should contain plugins from "global" scope + */ + $this->assertContains( + $globalPlugin, + $configDataDummy[2]['Magento\\Framework\\App\\Response\\Http_sendResponse___self'][1], + 'Plugin configurations are not equal. "dummy" scope should contain plugins from "global" scope' + ); } /** @@ -137,11 +164,13 @@ private function getCustomDirs(): array */ protected function tearDown(): void { - $filePath = $this->directoryList->getPath(DirectoryList::GENERATED_METADATA) - . '/' . self::CACHE_ID . '.' . 'php'; + foreach ($this->cacheIds as $cacheId) { + $filePath = $this->directoryList->getPath(DirectoryList::GENERATED_METADATA) + . '/' . $cacheId . '.' . 'php'; - if (file_exists($filePath)) { - $this->file->deleteFile($filePath); + if (file_exists($filePath)) { + $this->file->deleteFile($filePath); + } } } } From 7f127923ba83be5dba9caef78ce6f584e0ec16e9 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 15 Dec 2020 11:48:12 +0200 Subject: [PATCH 029/112] MC-39104: 2 error messages in Cart when product is out of stock --- .../Model/Quote/Item/QuantityValidator.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php index 317a573a653e9..b4754eea5c064 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php @@ -157,11 +157,13 @@ public function validate(Observer $observer) if ($stockStatus->getStockStatus() === Stock::STOCK_OUT_OF_STOCK || $parentStockStatus && $parentStockStatus->getStockStatus() == Stock::STOCK_OUT_OF_STOCK ) { - $quoteItem->addErrorInfo( - 'cataloginventory', - Data::ERROR_QTY, - __('This product is out of stock.') - ); + if (!$quoteItem->getStockStateResult() && !$quoteItem->getStockStateResult()->getHasError()) { + $quoteItem->addErrorInfo( + 'cataloginventory', + Data::ERROR_QTY, + __('This product is out of stock.') + ); + } $quoteItem->getQuote()->addErrorInfo( 'stock', 'cataloginventory', From 60e6c687fd2aa57734a519a983ab81e1ee381427 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 15 Dec 2020 14:28:46 +0200 Subject: [PATCH 030/112] MC-39104: 2 error messages in Cart when product is out of stock --- .../Model/Quote/Item/QuantityValidator.php | 4 +- .../Initializer/QuantityValidatorTest.php | 50 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php index b4754eea5c064..12d9206ebff17 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php @@ -157,7 +157,9 @@ public function validate(Observer $observer) if ($stockStatus->getStockStatus() === Stock::STOCK_OUT_OF_STOCK || $parentStockStatus && $parentStockStatus->getStockStatus() == Stock::STOCK_OUT_OF_STOCK ) { - if (!$quoteItem->getStockStateResult() && !$quoteItem->getStockStateResult()->getHasError()) { + $hasError = $quoteItem->getStockStateResult() + ? $quoteItem->getStockStateResult()->getHasError() : false; + if (!$hasError) { $quoteItem->addErrorInfo( 'cataloginventory', Data::ERROR_QTY, diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/QuantityValidatorTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/QuantityValidatorTest.php index edc22a008c554..0a1eee76d6960 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/QuantityValidatorTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/QuantityValidatorTest.php @@ -153,7 +153,7 @@ protected function setUp(): void ->getMock(); $this->storeMock = $this->createMock(Store::class); $this->quoteItemMock = $this->getMockBuilder(Item::class) - ->addMethods(['getProductId', 'getHasError']) + ->addMethods(['getProductId', 'getHasError', 'getStockStateResult']) ->onlyMethods( [ 'getQuote', @@ -460,6 +460,54 @@ public function testException() $this->quantityValidator->validate($this->observerMock); } + /** + * This tests the scenario when the error is in the quote item already + * + * @return void + */ + public function testValidateOutStockWithAlreadyErrorInQuoteItem(): void + { + $this->createInitialStub(1); + $resultMock = $this->getMockBuilder(DataObject::class) + ->addMethods(['checkQtyIncrements', 'getMessage', 'getQuoteMessage', 'getHasError']) + ->getMock(); + $resultMock->method('getHasError') + ->willReturn(true); + $this->stockRegistryMock->method('getStockItem') + ->willReturn($this->stockItemMock); + $this->stockRegistryMock->expects($this->at(1)) + ->method('getStockStatus') + ->willReturn($this->stockStatusMock); + $this->quoteItemMock->method('getParentItem') + ->willReturn($this->parentItemMock); + $this->quoteItemMock->method('getStockStateResult') + ->willReturn($resultMock); + $this->stockRegistryMock->expects($this->at(2)) + ->method('getStockStatus') + ->willReturn($this->parentStockItemMock); + $this->parentStockItemMock->method('getStockStatus') + ->willReturn(0); + $this->stockStatusMock->expects($this->atLeastOnce()) + ->method('getStockStatus') + ->willReturn(1); + $this->quoteItemMock->expects($this->never()) + ->method('addErrorInfo') + ->with( + 'cataloginventory', + Data::ERROR_QTY, + __('This product is out of stock.') + ); + $this->quoteMock->expects($this->once()) + ->method('addErrorInfo') + ->with( + 'stock', + 'cataloginventory', + Data::ERROR_QTY, + __('Some of the products are out of stock.') + ); + $this->quantityValidator->validate($this->observerMock); + } + /** * @param $qty * @param $hasError From 58f9f29843f1fcb07a642f270c0d8cb042bf0075 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Tue, 15 Dec 2020 16:48:58 -0600 Subject: [PATCH 031/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../Magento/UrlRewrite/Model/UrlRewrite.php | 88 ++++++++++++++++--- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index 363a3388daab0..3722c27a1b312 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -7,9 +7,6 @@ namespace Magento\UrlRewrite\Model; -use Magento\Catalog\Model\Category; -use Magento\Catalog\Model\Product; -use Magento\Cms\Model\Page; use Magento\Framework\App\ObjectManager; use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\EntityManager\EventManager; @@ -61,6 +58,11 @@ class UrlRewrite extends AbstractModel */ private $entityToCacheTagMap; + /** + * @var UrlFinderInterface + */ + private $urlFinder; + /** * UrlRewrite constructor. * @@ -72,6 +74,7 @@ class UrlRewrite extends AbstractModel * @param Json|null $serializer * @param CacheContext|null $cacheContext * @param EventManager|null $eventManager + * @param UrlFinderInterface|null $urlFinder * @param array $entityToCacheTagMap */ public function __construct( @@ -83,12 +86,14 @@ public function __construct( Json $serializer = null, CacheContext $cacheContext = null, EventManager $eventManager = null, + UrlFinderInterface $urlFinder = null, array $entityToCacheTagMap = [] ) { $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); $this->cacheContext = $cacheContext ?: ObjectManager::getInstance()->get(CacheContext::class); $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(EventManager::class); + $this->urlFinder = $urlFinder ?: ObjectManager::getInstance()->get(UrlFinderInterface::class); $this->entityToCacheTagMap = $entityToCacheTagMap; parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -131,30 +136,91 @@ public function setMetadata($metadata) } /** - * Clean cache for the entity which was affected by updating UrlRewrite + * Gets final target UrlRewrite for custom rewrite record * - * @param $entityType - * @param $entityId + * @param string $path + * @param int $storeId + * @return UrlRewrite|null + */ + private function getFinalTargetUrlRewrite(string $path, int $storeId) { + $urlRewriteTarget = $this->urlFinder->findOneByData( + [ + 'request_path' => $path, + 'store_id' => $storeId + ] + ); + + while ($urlRewriteTarget && $urlRewriteTarget->getRedirectType() > 0) { + $urlRewriteTarget = $this->urlFinder->findOneByData( + [ + 'request_path' => $urlRewriteTarget->getTargetPath(), + 'store_id' => $urlRewriteTarget->getStoreId() + ] + ); + } + + return $urlRewriteTarget; + } + + /** + * Clean the cache for entities affected by current rewrite */ - private function cleanCacheForEntity($entityType, $entityId) + private function cleanEntitiesCache() { + if ($this->getEntityType() === Rewrite::ENTITY_TYPE_CUSTOM) { + $urlRewrite = $this->getFinalTargetUrlRewrite( + $this->getTargetPath(), + (int)$this->getStoreId() + ); + + if ($urlRewrite) { + $this->cleanCacheForEntity($urlRewrite->getEntityType(), (int) $urlRewrite->getEntityId()); + } + + if ($this->getOrigData() && $this->getOrigData('target_path') !== $this->getTargetPath()) { + $origUrlRewrite = $this->getFinalTargetUrlRewrite( + $this->getOrigData('target_path'), + (int)$this->getOrigData('store_id') + ); + + if ($origUrlRewrite) { + $this->cleanCacheForEntity($origUrlRewrite->getEntityType(), (int) $origUrlRewrite->getEntityId()); + } + } + } else { + $this->cleanCacheForEntity($this->getEntityType(), (int) $this->getEntityId()); + } + } + + /** + * Clean cache for specified entity type by id + * + * @param string $entityType + * @param int $entityId + */ + private function cleanCacheForEntity(string $entityType, int $entityId) { - if ($entityType !== Rewrite::ENTITY_TYPE_CUSTOM && array_key_exists($entityType, $this->entityToCacheTagMap)) { + if (array_key_exists($entityType, $this->entityToCacheTagMap)) { $cacheKey = $this->entityToCacheTagMap[$entityType]; - $this->cacheContext->registerEntities($cacheKey, [$entityId]); $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); } } + /** + * @inheritdoc + */ public function afterDelete() { - $this->cleanCacheForEntity($this->getEntityType(), $this->getEntityId()); + $this->cleanEntitiesCache(); return parent::afterDelete(); } + /** + * @inheritdoc + */ public function afterSave() { - $this->cleanCacheForEntity($this->getEntityType(), $this->getEntityId()); + $this->cleanEntitiesCache(); return parent::afterSave(); } } From d3d41033af551138433d2e413f2f5b378884815b Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 15 Dec 2020 18:41:43 -0600 Subject: [PATCH 032/112] MC-39861: Customer is redirected to the blank page after using PayPal WPPHS payment on checkout --- .../Controller/Hostedpro/ReturnAction.php | 6 +-- .../Plugin/TransparentSessionChecker.php | 16 +++++- .../Controller/Hostedpro/ReturnActionTest.php | 54 +++++++++++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php diff --git a/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php b/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php index bb8b5f8fa0b46..dbaf432878de9 100644 --- a/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php +++ b/app/code/Magento/Paypal/Controller/Hostedpro/ReturnAction.php @@ -26,11 +26,7 @@ class ReturnAction extends Action implements CsrfAwareActionInterface, HttpPostA */ public function execute() { - $session = $this->_objectManager->get(\Magento\Checkout\Model\Session::class); - //TODO: some actions with order - if ($session->getLastRealOrderId()) { - $this->_redirect('checkout/onepage/success'); - } + $this->_redirect('checkout/onepage/success'); } /** diff --git a/app/code/Magento/Paypal/Plugin/TransparentSessionChecker.php b/app/code/Magento/Paypal/Plugin/TransparentSessionChecker.php index 5157ba3208fb7..d53fd183c1942 100644 --- a/app/code/Magento/Paypal/Plugin/TransparentSessionChecker.php +++ b/app/code/Magento/Paypal/Plugin/TransparentSessionChecker.php @@ -15,7 +15,13 @@ */ class TransparentSessionChecker { - private const TRANSPARENT_REDIRECT_PATH = 'paypal/transparent/redirect'; + /** + * @var string[] + */ + private $disableSessionUrls = [ + 'paypal/transparent/redirect', + 'paypal/hostedpro/return', + ]; /** * @var Http @@ -45,6 +51,12 @@ public function afterCheck(SessionStartChecker $subject, bool $result): bool return false; } - return strpos((string)$this->request->getPathInfo(), self::TRANSPARENT_REDIRECT_PATH) === false; + foreach ($this->disableSessionUrls as $url) { + if (strpos((string)$this->request->getPathInfo(), $url) !== false) { + return false; + } + } + + return true; } } diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php new file mode 100644 index 0000000000000..d52c2501a565a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Controller\Hostedpro; + +use Magento\TestFramework\TestCase\AbstractController; +use Zend\Stdlib\Parameters; + +/** + * Tests PayPal HostedPro return controller. + */ +class ReturnActionTest extends AbstractController +{ + /** + * Tests customer redirect on success page after return from PayPal HostedPro payment. + * + * @SuppressWarnings(PHPMD.Superglobals) + */ + public function testReturnRedirect() + { + $redirectUri = 'paypal/hostedpro/return'; + $this->setRequestUri($redirectUri); + $this->getRequest()->setMethod('POST'); + + $this->dispatch($redirectUri); + $this->assertRedirect($this->stringContains('checkout/onepage/success')); + + $this->assertEmpty( + $_SESSION, + 'Session start has to be skipped for current controller' + ); + } + + /** + * Sets REQUEST_URI into request object. + * + * @param string $requestUri + * @return void + */ + private function setRequestUri(string $requestUri) + { + $request = $this->getRequest(); + $reflection = new \ReflectionClass($request); + $property = $reflection->getProperty('requestUri'); + $property->setAccessible(true); + $property->setValue($request, null); + + $request->setServer(new Parameters(['REQUEST_URI' => $requestUri])); + } +} From 468383e24bb059e1f25e902a782e51cf03d9f312 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 16 Dec 2020 15:51:30 +0200 Subject: [PATCH 033/112] MC-39104: 2 error messages in Cart when product is out of stock --- .../CatalogInventory/Model/Quote/Item/QuantityValidator.php | 2 ++ .../Test/NoOptionAvailableToConfigureDisabledProductTest.xml | 4 ++-- .../Magento/Quote/Api/GuestCartItemRepositoryTest.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php index 12d9206ebff17..12a48caf62414 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php @@ -165,6 +165,8 @@ public function validate(Observer $observer) Data::ERROR_QTY, __('This product is out of stock.') ); + } else { + $quoteItem->addErrorInfo(null, Data::ERROR_QTY); } $quoteItem->getQuote()->addErrorInfo( 'stock', diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml index e0dae94f13150..b75dd590dbbf1 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -125,7 +125,7 @@ <actionGroup ref="AdminSetStockStatusActionGroup" stepKey="outOfStockStatus"> <argument name="stockStatus" value="Out of Stock"/> </actionGroup> - + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProductForm"/> <!-- Go to created customer page --> <comment userInput="Go to created customer page" stepKey="goToCreatedCustomerPage"/> @@ -158,7 +158,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickToAddProductToOrder"/> <waitForPageLoad stepKey="waitForNewOrderPageLoad"/> - <see userInput="This product is out of stock." stepKey="seeTheErrorMessageDisplayed"/> + <see userInput="There are no source items with the in stock status" stepKey="seeTheErrorMessageDisplayed"/> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="createNewOrderThirdTime"> <argument name="customer" value="$createCustomer$"/> diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php index 373ad64ba39d4..a9c402096aaf0 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php @@ -265,7 +265,7 @@ public function updateItemDataProvider(): array 'use_config_backorders' => 0, 'backorders' => Stock::BACKORDERS_NO, ], - 'This product is out of stock.' + 'There are no source items with the in stock status' ], [ [ From 4531c763b5afad4af054bf5dce14669dce0c49ee Mon Sep 17 00:00:00 2001 From: Viktor Rad <vrad@adobe.com> Date: Wed, 16 Dec 2020 07:54:37 -0600 Subject: [PATCH 034/112] MC-38834: Uploading a new logo for print on Logo for HTML Print View settings does not reflect on frontend my account order --- .../ViewModel/Header/LogoPathResolver.php | 69 +++++++++++++++++ .../frontend/layout/sales_order_print.xml | 5 ++ .../layout/sales_order_printcreditmemo.xml | 5 ++ .../layout/sales_order_printinvoice.xml | 5 ++ .../layout/sales_order_printshipment.xml | 5 ++ .../Magento/Theme/Block/Html/Header/Logo.php | 16 ++-- .../Test/Unit/Block/Html/Header/LogoTest.php | 7 +- .../Block/Html/Header/LogoPathResolver.php | 51 +++++++++++++ .../Html/Header/LogoPathResolverInterface.php | 21 ++++++ .../Theme/view/frontend/layout/default.xml | 6 +- .../Sales/Block/Order/PrintOrder/LogoTest.php | 74 +++++++++++++++++++ 11 files changed, 253 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Sales/ViewModel/Header/LogoPathResolver.php create mode 100644 app/code/Magento/Theme/ViewModel/Block/Html/Header/LogoPathResolver.php create mode 100644 app/code/Magento/Theme/ViewModel/Block/Html/Header/LogoPathResolverInterface.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Order/PrintOrder/LogoTest.php diff --git a/app/code/Magento/Sales/ViewModel/Header/LogoPathResolver.php b/app/code/Magento/Sales/ViewModel/Header/LogoPathResolver.php new file mode 100644 index 0000000000000..c58654e160749 --- /dev/null +++ b/app/code/Magento/Sales/ViewModel/Header/LogoPathResolver.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\ViewModel\Header; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Theme\ViewModel\Block\Html\Header\LogoPathResolverInterface; +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Sales\Model\Order; +use Magento\Framework\Registry; + +/** + * Class for resolving logo path + */ +class LogoPathResolver implements LogoPathResolverInterface, ArgumentInterface +{ + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * Core registry + * + * @var Registry + */ + private $coreRegistry; + + /** + * @param ScopeConfigInterface $scopeConfig + * @param Registry $registry + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + Registry $registry + ) { + $this->scopeConfig = $scopeConfig; + $this->coreRegistry = $registry; + } + + /** + * Return logo image path + * + * @return string|null + */ + public function getPath(): ?string + { + $path = null; + $storeId = null; + $order = $this->coreRegistry->registry('current_order'); + if ($order instanceof Order) { + $storeId = $order->getStoreId(); + } + $storeLogoPath = $this->scopeConfig->getValue( + 'sales/identity/logo_html', + ScopeInterface::SCOPE_STORE, + $storeId + ); + if ($storeLogoPath !== null) { + $path = 'sales/store/logo_html/' . $storeLogoPath; + } + return $path; + } +} diff --git a/app/code/Magento/Sales/view/frontend/layout/sales_order_print.xml b/app/code/Magento/Sales/view/frontend/layout/sales_order_print.xml index 4410a6fc4a9a2..8a52e65a9f70c 100644 --- a/app/code/Magento/Sales/view/frontend/layout/sales_order_print.xml +++ b/app/code/Magento/Sales/view/frontend/layout/sales_order_print.xml @@ -44,5 +44,10 @@ <block class="Magento\Sales\Block\Order\Info" as="sales.order.print.info" name="sales.order.print.info" template="Magento_Sales::order/info.phtml"/> </referenceContainer> <block class="Magento\Framework\View\Element\Template" name="additional.product.info" template="Magento_Theme::template.phtml"/> + <referenceBlock name="logo"> + <arguments> + <argument name="logoPathResolver" xsi:type="object">Magento\Sales\ViewModel\Header\LogoPathResolver</argument> + </arguments> + </referenceBlock> </body> </page> diff --git a/app/code/Magento/Sales/view/frontend/layout/sales_order_printcreditmemo.xml b/app/code/Magento/Sales/view/frontend/layout/sales_order_printcreditmemo.xml index 0021eeede4f2b..317ee419f6d96 100644 --- a/app/code/Magento/Sales/view/frontend/layout/sales_order_printcreditmemo.xml +++ b/app/code/Magento/Sales/view/frontend/layout/sales_order_printcreditmemo.xml @@ -28,5 +28,10 @@ </block> </referenceContainer> <block class="Magento\Framework\View\Element\Template" name="additional.product.info" template="Magento_Theme::template.phtml"/> + <referenceBlock name="logo"> + <arguments> + <argument name="logoPathResolver" xsi:type="object">Magento\Sales\ViewModel\Header\LogoPathResolver</argument> + </arguments> + </referenceBlock> </body> </page> diff --git a/app/code/Magento/Sales/view/frontend/layout/sales_order_printinvoice.xml b/app/code/Magento/Sales/view/frontend/layout/sales_order_printinvoice.xml index 0272286696e24..e0bb15bc0e7fd 100644 --- a/app/code/Magento/Sales/view/frontend/layout/sales_order_printinvoice.xml +++ b/app/code/Magento/Sales/view/frontend/layout/sales_order_printinvoice.xml @@ -35,5 +35,10 @@ </block> </referenceContainer> <block class="Magento\Framework\View\Element\Template" name="additional.product.info" template="Magento_Theme::template.phtml"/> + <referenceBlock name="logo"> + <arguments> + <argument name="logoPathResolver" xsi:type="object">Magento\Sales\ViewModel\Header\LogoPathResolver</argument> + </arguments> + </referenceBlock> </body> </page> diff --git a/app/code/Magento/Sales/view/frontend/layout/sales_order_printshipment.xml b/app/code/Magento/Sales/view/frontend/layout/sales_order_printshipment.xml index 30053b41a96a9..b7940c0d406cc 100644 --- a/app/code/Magento/Sales/view/frontend/layout/sales_order_printshipment.xml +++ b/app/code/Magento/Sales/view/frontend/layout/sales_order_printshipment.xml @@ -20,5 +20,10 @@ </block> </referenceContainer> <block class="Magento\Framework\View\Element\Template" name="additional.product.info" template="Magento_Theme::template.phtml"/> + <referenceBlock name="logo"> + <arguments> + <argument name="logoPathResolver" xsi:type="object">Magento\Sales\ViewModel\Header\LogoPathResolver</argument> + </arguments> + </referenceBlock> </body> </page> diff --git a/app/code/Magento/Theme/Block/Html/Header/Logo.php b/app/code/Magento/Theme/Block/Html/Header/Logo.php index 792ee95de4995..3c43e5bfc6fe1 100644 --- a/app/code/Magento/Theme/Block/Html/Header/Logo.php +++ b/app/code/Magento/Theme/Block/Html/Header/Logo.php @@ -6,6 +6,8 @@ namespace Magento\Theme\Block\Html\Header; +use Magento\Theme\ViewModel\Block\Html\Header\LogoPathResolverInterface; + /** * Logo page header block * @@ -124,16 +126,16 @@ public function getLogoHeight() */ protected function _getLogoUrl() { - $folderName = \Magento\Config\Model\Config\Backend\Image\Logo::UPLOAD_DIR; - $storeLogoPath = $this->_scopeConfig->getValue( - 'design/header/logo_src', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - $path = $folderName . '/' . $storeLogoPath; + $path = null; + /** @var LogoPathResolverInterface $logoPathResolver */ + $logoPathResolver = $this->getData('logoPathResolver'); + if ($logoPathResolver instanceof LogoPathResolverInterface) { + $path = $logoPathResolver->getPath(); + } $logoUrl = $this->_urlBuilder ->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]) . $path; - if ($storeLogoPath !== null && $this->_isFile($path)) { + if ($path !== null && $this->_isFile($path)) { $url = $logoUrl; } elseif ($this->getLogoFile()) { $url = $this->getViewFileUrl($this->getLogoFile()); diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php index 1978362810763..a5095674a4673 100644 --- a/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php @@ -7,6 +7,7 @@ namespace Magento\Theme\Test\Unit\Block\Html\Header; +use Magento\Theme\ViewModel\Block\Html\Header\LogoPathResolverInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\Read; @@ -25,11 +26,11 @@ public function testGetLogoSrc() { $filesystem = $this->createMock(Filesystem::class); $mediaDirectory = $this->createMock(Read::class); - $scopeConfig = $this->getMockForAbstractClass(ScopeConfigInterface::class); + $logoPathResolver = $this->getMockForAbstractClass(LogoPathResolverInterface::class); $urlBuilder = $this->getMockForAbstractClass(UrlInterface::class); - $scopeConfig->expects($this->once())->method('getValue')->willReturn('default/image.gif'); + $logoPathResolver->expects($this->once())->method('getPath')->willReturn('logo/default/image.gif'); $urlBuilder->expects( $this->once() )->method( @@ -46,7 +47,7 @@ public function testGetLogoSrc() $objectManager = new ObjectManager($this); $arguments = [ - 'scopeConfig' => $scopeConfig, + 'data' => ['logoPathResolver' => $logoPathResolver], 'urlBuilder' => $urlBuilder, 'fileStorageHelper' => $helper, 'filesystem' => $filesystem, diff --git a/app/code/Magento/Theme/ViewModel/Block/Html/Header/LogoPathResolver.php b/app/code/Magento/Theme/ViewModel/Block/Html/Header/LogoPathResolver.php new file mode 100644 index 0000000000000..1a10fe9177320 --- /dev/null +++ b/app/code/Magento/Theme/ViewModel/Block/Html/Header/LogoPathResolver.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Theme\ViewModel\Block\Html\Header; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Config\Model\Config\Backend\Image\Logo; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * Class for resolving logo path + */ +class LogoPathResolver implements LogoPathResolverInterface, ArgumentInterface +{ + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + ScopeConfigInterface $scopeConfig + ) { + $this->scopeConfig = $scopeConfig; + } + + /** + * Return logo image path + * + * @return string|null + */ + public function getPath(): ?string + { + $path = null; + $storeLogoPath = $this->scopeConfig->getValue( + 'design/header/logo_src', + ScopeInterface::SCOPE_STORE + ); + if ($storeLogoPath !== null) { + $path = Logo::UPLOAD_DIR . '/' . $storeLogoPath; + } + return $path; + } +} diff --git a/app/code/Magento/Theme/ViewModel/Block/Html/Header/LogoPathResolverInterface.php b/app/code/Magento/Theme/ViewModel/Block/Html/Header/LogoPathResolverInterface.php new file mode 100644 index 0000000000000..3ac8442aa0ea7 --- /dev/null +++ b/app/code/Magento/Theme/ViewModel/Block/Html/Header/LogoPathResolverInterface.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Theme\ViewModel\Block\Html\Header; + +/** + * Interface for resolving logo path + */ +interface LogoPathResolverInterface +{ + /** + * Return logo image path + * + * @return null|string + */ + public function getPath(): ?string; +} diff --git a/app/code/Magento/Theme/view/frontend/layout/default.xml b/app/code/Magento/Theme/view/frontend/layout/default.xml index bf76933b356c0..f3e57b12150c9 100644 --- a/app/code/Magento/Theme/view/frontend/layout/default.xml +++ b/app/code/Magento/Theme/view/frontend/layout/default.xml @@ -51,7 +51,11 @@ </container> </container> <container name="header-wrapper" label="Page Header" as="header-wrapper" htmlTag="div" htmlClass="header content"> - <block class="Magento\Theme\Block\Html\Header\Logo" name="logo"/> + <block class="Magento\Theme\Block\Html\Header\Logo" name="logo"> + <arguments> + <argument name="logoPathResolver" xsi:type="object">Magento\Theme\ViewModel\Block\Html\Header\LogoPathResolver</argument> + </arguments> + </block> </container> </referenceContainer> <referenceContainer name="page.top"> diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/PrintOrder/LogoTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/PrintOrder/LogoTest.php new file mode 100644 index 0000000000000..aadd3ab7b956e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/PrintOrder/LogoTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Block\Order\PrintOrder; + +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\State; +use Magento\Theme\Block\Html\Header\Logo; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\ObjectManagerInterface; +use Magento\Theme\ViewModel\Block\Html\Header\LogoPathResolver as LogoPathResolverDefault; +use Magento\Sales\ViewModel\Header\LogoPathResolver as LogoPathResolverSales; +use \PHPUnit\Framework\TestCase; + +class LogoTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var WriteInterface + */ + private $mediaDirectory; + + protected function setUp(): void + { + $this->objectManager = Bootstrap::getObjectManager(); + $filesystem = $this->objectManager->get(Filesystem::class); + $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->objectManager->get(State::class) + ->setAreaCode(\Magento\Framework\App\Area::AREA_FRONTEND); + Bootstrap::getInstance() + ->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND); + } + + /** + * @magentoConfigFixture default_store design/header/logo_src default/logo.jpg + * @magentoConfigFixture default_store sales/identity/logo_html default/logo_sales.jpg + * @throws \Magento\Framework\Exception\FileSystemException + */ + public function testGetLogoSrc(): void + { + $host = 'http://localhost/media/'; + $defaultLogoFile= 'logo.jpg'; + $defaultPath = 'logo/default/' . $defaultLogoFile; + $salesLogoFile = 'logo_sales.jpg'; + $salesPath = 'sales/store/logo_html/default/' . $salesLogoFile; + $this->mediaDirectory->writeFile($defaultPath, ''); + $this->mediaDirectory->writeFile($salesPath, ''); + $blockArguments = ['data' => + ['logoPathResolver' => $this->objectManager->get(LogoPathResolverDefault::class)] + ]; + /** @var Logo $block */ + $block = $this->objectManager->create(LayoutInterface::class) + ->createBlock(Logo::class, 'logo', $blockArguments); + $this->assertSame($host . $defaultPath, $block->getLogoSrc()); + $blockArguments = ['data' => + ['logoPathResolver' => $this->objectManager->get(LogoPathResolverSales::class)] + ]; + /** @var Logo $block */ + $block = $this->objectManager->create(LayoutInterface::class) + ->createBlock(Logo::class, 'logo', $blockArguments); + $this->assertSame($host . $salesPath, $block->getLogoSrc()); + $this->mediaDirectory->delete($defaultPath); + $this->mediaDirectory->delete($salesPath); + } +} From e55a9e3624bb7b7e2d0a35877dcc1a97975962e7 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 16 Dec 2020 18:06:20 +0200 Subject: [PATCH 035/112] MC-39104: 2 error messages in Cart when product is out of stock --- .../QuantityValidator/Initializer/QuantityValidatorTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/QuantityValidatorTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/QuantityValidatorTest.php index 0a1eee76d6960..36b9fd0adeb81 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/QuantityValidatorTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Quote/Item/QuantityValidator/Initializer/QuantityValidatorTest.php @@ -490,12 +490,11 @@ public function testValidateOutStockWithAlreadyErrorInQuoteItem(): void $this->stockStatusMock->expects($this->atLeastOnce()) ->method('getStockStatus') ->willReturn(1); - $this->quoteItemMock->expects($this->never()) + $this->quoteItemMock->expects($this->once()) ->method('addErrorInfo') ->with( - 'cataloginventory', + null, Data::ERROR_QTY, - __('This product is out of stock.') ); $this->quoteMock->expects($this->once()) ->method('addErrorInfo') From 5559a51bccae19c3b032a3fe17e08047a67aacf8 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Wed, 16 Dec 2020 18:20:29 +0200 Subject: [PATCH 036/112] Fix ftp_nlist for empty folders return false --- lib/internal/Magento/Framework/Filesystem/Io/Ftp.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php b/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php index 04df5fd3f3a6c..4bd3e49485681 100644 --- a/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php +++ b/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php @@ -313,9 +313,10 @@ public function chmod($filename, $mode) */ public function ls($grep = null) { - $ls = @ftp_nlist($this->_conn, '.'); + $ls = @ftp_nlist($this->_conn, '.') ?? []; $list = []; + foreach ($ls as $file) { $list[] = ['text' => $file, 'id' => $this->pwd() . '/' . $file]; } From c42d022322d7e6114d6a8cd4b7029adc5e44022a Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Wed, 16 Dec 2020 18:27:40 +0200 Subject: [PATCH 037/112] Update Ftp.php --- lib/internal/Magento/Framework/Filesystem/Io/Ftp.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php b/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php index 4bd3e49485681..0027329e7d54c 100644 --- a/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php +++ b/lib/internal/Magento/Framework/Filesystem/Io/Ftp.php @@ -313,7 +313,7 @@ public function chmod($filename, $mode) */ public function ls($grep = null) { - $ls = @ftp_nlist($this->_conn, '.') ?? []; + $ls = @ftp_nlist($this->_conn, '.') ?: []; $list = []; From 253f0d2e9507f05ccb2fd00f0ba2f4d79c0ad5e4 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Wed, 16 Dec 2020 12:49:20 -0600 Subject: [PATCH 038/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- app/code/Magento/UrlRewrite/Model/UrlRewrite.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index 3722c27a1b312..1fa3ef049daae 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -142,7 +142,8 @@ public function setMetadata($metadata) * @param int $storeId * @return UrlRewrite|null */ - private function getFinalTargetUrlRewrite(string $path, int $storeId) { + private function getFinalTargetUrlRewrite(string $path, int $storeId): ?UrlRewrite + { $urlRewriteTarget = $this->urlFinder->findOneByData( [ 'request_path' => $path, @@ -165,7 +166,8 @@ private function getFinalTargetUrlRewrite(string $path, int $storeId) { /** * Clean the cache for entities affected by current rewrite */ - private function cleanEntitiesCache() { + private function cleanEntitiesCache() + { if ($this->getEntityType() === Rewrite::ENTITY_TYPE_CUSTOM) { $urlRewrite = $this->getFinalTargetUrlRewrite( $this->getTargetPath(), From a1750ac2296bac2f728a5462ce696df70e1988df Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Wed, 16 Dec 2020 15:57:42 -0600 Subject: [PATCH 039/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../Magento/UrlRewrite/Model/UrlRewrite.php | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index 1fa3ef049daae..5f867e6ef0aa6 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -168,28 +168,30 @@ private function getFinalTargetUrlRewrite(string $path, int $storeId): ?UrlRewri */ private function cleanEntitiesCache() { - if ($this->getEntityType() === Rewrite::ENTITY_TYPE_CUSTOM) { - $urlRewrite = $this->getFinalTargetUrlRewrite( - $this->getTargetPath(), - (int)$this->getStoreId() - ); + if (!$this->isEmpty()) { + if ($this->getEntityType() === Rewrite::ENTITY_TYPE_CUSTOM) { + $urlRewrite = $this->getFinalTargetUrlRewrite( + $this->getTargetPath(), + (int)$this->getStoreId() + ); - if ($urlRewrite) { - $this->cleanCacheForEntity($urlRewrite->getEntityType(), (int) $urlRewrite->getEntityId()); - } + if ($urlRewrite) { + $this->cleanCacheForEntity($urlRewrite->getEntityType(), (int) $urlRewrite->getEntityId()); + } - if ($this->getOrigData() && $this->getOrigData('target_path') !== $this->getTargetPath()) { - $origUrlRewrite = $this->getFinalTargetUrlRewrite( - $this->getOrigData('target_path'), - (int)$this->getOrigData('store_id') - ); + if ($this->getOrigData() && $this->getOrigData('target_path') !== $this->getTargetPath()) { + $origUrlRewrite = $this->getFinalTargetUrlRewrite( + $this->getOrigData('target_path'), + (int)$this->getOrigData('store_id') + ); - if ($origUrlRewrite) { - $this->cleanCacheForEntity($origUrlRewrite->getEntityType(), (int) $origUrlRewrite->getEntityId()); + if ($origUrlRewrite) { + $this->cleanCacheForEntity($origUrlRewrite->getEntityType(), (int) $origUrlRewrite->getEntityId()); + } } + } else { + $this->cleanCacheForEntity($this->getEntityType(), (int) $this->getEntityId()); } - } else { - $this->cleanCacheForEntity($this->getEntityType(), (int) $this->getEntityId()); } } From 375fd4cb19c05cdbb88a047865ed0d23bf310e21 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Wed, 16 Dec 2020 17:11:47 -0600 Subject: [PATCH 040/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- app/code/Magento/UrlRewrite/Model/UrlRewrite.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index 5f867e6ef0aa6..d82781ee66450 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -18,6 +18,7 @@ use Magento\Framework\Serialize\Serializer\Json; use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteService; /** * UrlRewrite model class @@ -140,9 +141,9 @@ public function setMetadata($metadata) * * @param string $path * @param int $storeId - * @return UrlRewrite|null + * @return UrlRewriteService|null */ - private function getFinalTargetUrlRewrite(string $path, int $storeId): ?UrlRewrite + private function getFinalTargetUrlRewrite(string $path, int $storeId): ?UrlRewriteService { $urlRewriteTarget = $this->urlFinder->findOneByData( [ @@ -166,7 +167,7 @@ private function getFinalTargetUrlRewrite(string $path, int $storeId): ?UrlRewri /** * Clean the cache for entities affected by current rewrite */ - private function cleanEntitiesCache() + public function cleanEntitiesCache() { if (!$this->isEmpty()) { if ($this->getEntityType() === Rewrite::ENTITY_TYPE_CUSTOM) { @@ -215,7 +216,7 @@ private function cleanCacheForEntity(string $entityType, int $entityId) */ public function afterDelete() { - $this->cleanEntitiesCache(); + $this->_getResource()->addCommitCallback([$this, 'cleanEntitiesCache']); return parent::afterDelete(); } @@ -224,7 +225,7 @@ public function afterDelete() */ public function afterSave() { - $this->cleanEntitiesCache(); + $this->_getResource()->addCommitCallback([$this, 'cleanEntitiesCache']); return parent::afterSave(); } } From 5e35bb3739ca1fb526c6caf170d5c1d2e1a181d3 Mon Sep 17 00:00:00 2001 From: Vova Yatsyuk <vova.yatsyuk@gmail.com> Date: Thu, 17 Dec 2020 15:12:09 +0200 Subject: [PATCH 041/112] Fixed failed integration test --- .../Magento/Csp/Model/Collector/ConfigCollectorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php index 2d8cbbeedeab9..cf6287ed5b4e1 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php @@ -62,9 +62,9 @@ private function getExpectedPolicies(): array [], true ), - 'font-src' => new FetchPolicy('font-src', false, [], [], true), + 'font-src' => new FetchPolicy('font-src', false, [], ['data'], true), 'frame-src' => new FetchPolicy('frame-src', false, [], [], true, false, false, [], [], true), - 'img-src' => new FetchPolicy('img-src', false, [], [], true), + 'img-src' => new FetchPolicy('img-src', false, [], ['data'], true), 'manifest-src' => new FetchPolicy('manifest-src', false, [], [], true), 'media-src' => new FetchPolicy('media-src', false, [], [], true), 'object-src' => new FetchPolicy('object-src', false, [], [], true), From 7f9a7fd39b4514fee0ca9eb0a1a8701c6f2b4030 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transogtgroup.com> Date: Thu, 17 Dec 2020 15:28:31 +0200 Subject: [PATCH 042/112] MC-39864: [Magento Cloud] - Tax Miscalculation --- .../Model/Order/Creditmemo/Total/Tax.php | 209 ++++++++++++------ .../Model/Order/Creditmemo/Total/TaxTest.php | 32 +-- 2 files changed, 156 insertions(+), 85 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php index 95dace13d832f..9e6e8979e46ee 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -5,22 +5,20 @@ */ namespace Magento\Sales\Model\Order\Creditmemo\Total; +use Magento\Sales\Model\Order\Creditmemo; + /** * Collects credit memo taxes. */ class Tax extends AbstractTotal { /** - * Collects credit memo taxes. - * - * @param \Magento\Sales\Model\Order\Creditmemo $creditmemo - * @return $this - * + * {@inheritdoc} * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) + public function collect(Creditmemo $creditmemo) { $shippingTaxAmount = 0; $baseShippingTaxAmount = 0; @@ -28,38 +26,37 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) $baseTotalTax = 0; $totalDiscountTaxCompensation = 0; $baseTotalDiscountTaxCompensation = 0; - $order = $creditmemo->getOrder(); - /** @var $item \Magento\Sales\Model\Order\Creditmemo\Item */ foreach ($creditmemo->getAllItems() as $item) { $orderItem = $item->getOrderItem(); if ($orderItem->isDummy() || $item->getQty() <= 0) { continue; } + $orderItemTax = (double)$orderItem->getTaxInvoiced(); $baseOrderItemTax = (double)$orderItem->getBaseTaxInvoiced(); $orderItemQty = (double)$orderItem->getQtyInvoiced(); if ($orderItemQty) { - /** - * Check item tax amount - */ - - $tax = $orderItemTax - $orderItem->getTaxRefunded(); - $baseTax = $baseOrderItemTax - $orderItem->getBaseTaxRefunded(); - $discountTaxCompensation = $orderItem->getDiscountTaxCompensationInvoiced() - - $orderItem->getDiscountTaxCompensationRefunded(); - $baseDiscountTaxCompensation = $orderItem->getBaseDiscountTaxCompensationInvoiced() - - $orderItem->getBaseDiscountTaxCompensationRefunded(); + /** Check item tax amount */ + $tax = ($orderItemTax - $orderItem->getTaxRefunded()); + $baseTax = ($baseOrderItemTax - $orderItem->getBaseTaxRefunded()); + $discountTaxCompensation = ($orderItem->getDiscountTaxCompensationInvoiced() + - $orderItem->getDiscountTaxCompensationRefunded()); + $baseDiscountTaxCompensation = ($orderItem->getBaseDiscountTaxCompensationInvoiced() + - $orderItem->getBaseDiscountTaxCompensationRefunded()); if (!$item->isLast()) { - $availableQty = $orderItemQty - $orderItem->getQtyRefunded(); + $availableQty = ($orderItemQty - $orderItem->getQtyRefunded()); $tax = $creditmemo->roundPrice($tax / $availableQty * $item->getQty()); - $baseTax = $creditmemo->roundPrice($baseTax / $availableQty * $item->getQty(), 'base'); - $discountTaxCompensation = - $creditmemo->roundPrice($discountTaxCompensation / $availableQty * $item->getQty()); - $baseDiscountTaxCompensation = - $creditmemo->roundPrice($baseDiscountTaxCompensation / $availableQty * $item->getQty(), 'base'); + $baseTax = $creditmemo->roundPrice(($baseTax / $availableQty * $item->getQty()), 'base'); + $discountTaxCompensation = $creditmemo->roundPrice( + $discountTaxCompensation / $availableQty * $item->getQty() + ); + $baseDiscountTaxCompensation = $creditmemo->roundPrice( + $baseDiscountTaxCompensation / $availableQty * $item->getQty(), + 'base' + ); } $item->setTaxAmount($tax); @@ -77,14 +74,14 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) $isPartialShippingRefunded = false; $baseOrderShippingAmount = (float)$order->getBaseShippingAmount(); if ($invoice = $creditmemo->getInvoice()) { - //recalculate tax amounts in case if refund shipping value was changed + // recalculate tax amounts in case if refund shipping value was changed if ($baseOrderShippingAmount && $creditmemo->getBaseShippingAmount() !== null) { - $taxFactor = $creditmemo->getBaseShippingAmount() / $baseOrderShippingAmount; - $shippingTaxAmount = $invoice->getShippingTaxAmount() * $taxFactor; - $baseShippingTaxAmount = $invoice->getBaseShippingTaxAmount() * $taxFactor; - $totalDiscountTaxCompensation += $invoice->getShippingDiscountTaxCompensationAmount() * $taxFactor; - $baseTotalDiscountTaxCompensation += - $invoice->getBaseShippingDiscountTaxCompensationAmnt() * $taxFactor; + $taxFactor = ($creditmemo->getBaseShippingAmount() / $baseOrderShippingAmount); + $shippingTaxAmount = ($invoice->getShippingTaxAmount() * $taxFactor); + $baseShippingTaxAmount = ($invoice->getBaseShippingTaxAmount() * $taxFactor); + $totalDiscountTaxCompensation += ($invoice->getShippingDiscountTaxCompensationAmount() * $taxFactor); + $baseTotalDiscountTaxCompensation += $invoice->getBaseShippingDiscountTaxCompensationAmnt() + * $taxFactor; $shippingTaxAmount = $creditmemo->roundPrice($shippingTaxAmount); $baseShippingTaxAmount = $creditmemo->roundPrice($baseShippingTaxAmount, 'base'); $totalDiscountTaxCompensation = $creditmemo->roundPrice($totalDiscountTaxCompensation); @@ -97,61 +94,52 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) } } else { $orderShippingAmount = $order->getShippingAmount(); - $baseOrderShippingRefundedAmount = $order->getBaseShippingRefunded(); - $shippingTaxAmount = 0; $baseShippingTaxAmount = 0; $shippingDiscountTaxCompensationAmount = 0; $baseShippingDiscountTaxCompensationAmount = 0; - - $shippingDelta = $baseOrderShippingAmount - $baseOrderShippingRefundedAmount; + $shippingDelta = ($baseOrderShippingAmount - $baseOrderShippingRefundedAmount); if ($shippingDelta > $creditmemo->getBaseShippingAmount()) { - $part = $creditmemo->getShippingAmount() / $orderShippingAmount; - $basePart = $creditmemo->getBaseShippingAmount() / $baseOrderShippingAmount; - $shippingTaxAmount = $order->getShippingTaxAmount() * $part; - $baseShippingTaxAmount = $order->getBaseShippingTaxAmount() * $basePart; + $part = ($creditmemo->getShippingAmount() / $orderShippingAmount); + $basePart = ($creditmemo->getBaseShippingAmount() / $baseOrderShippingAmount); + $shippingTaxAmount = ($order->getShippingTaxAmount() * $part); + $baseShippingTaxAmount = ($order->getBaseShippingTaxAmount() * $basePart); $shippingDiscountTaxCompensationAmount = $order->getShippingDiscountTaxCompensationAmount() * $part; - $baseShippingDiscountTaxCompensationAmount = - $order->getBaseShippingDiscountTaxCompensationAmnt() * $basePart; + $baseShippingDiscountTaxCompensationAmount = $order->getBaseShippingDiscountTaxCompensationAmnt() + * $basePart; $shippingTaxAmount = $creditmemo->roundPrice($shippingTaxAmount); $baseShippingTaxAmount = $creditmemo->roundPrice($baseShippingTaxAmount, 'base'); - $shippingDiscountTaxCompensationAmount = - $creditmemo->roundPrice($shippingDiscountTaxCompensationAmount); - $baseShippingDiscountTaxCompensationAmount = - $creditmemo->roundPrice($baseShippingDiscountTaxCompensationAmount, 'base'); + $shippingDiscountTaxCompensationAmount = $creditmemo->roundPrice( + $shippingDiscountTaxCompensationAmount + ); + $baseShippingDiscountTaxCompensationAmount = $creditmemo->roundPrice( + $baseShippingDiscountTaxCompensationAmount, + 'base' + ); if ($part < 1 && $order->getShippingTaxAmount() > 0) { $isPartialShippingRefunded = true; } } elseif ($shippingDelta == $creditmemo->getBaseShippingAmount()) { $shippingTaxAmount = $order->getShippingTaxAmount() - $order->getShippingTaxRefunded(); $baseShippingTaxAmount = $order->getBaseShippingTaxAmount() - $order->getBaseShippingTaxRefunded(); - $shippingDiscountTaxCompensationAmount = $order->getShippingDiscountTaxCompensationAmount() - - $order->getShippingDiscountTaxCompensationRefunded(); - $baseShippingDiscountTaxCompensationAmount = $order->getBaseShippingDiscountTaxCompensationAmnt() - - $order->getBaseShippingDiscountTaxCompensationRefunded(); + $shippingDiscountTaxCompensationAmount = $order->getShippingDiscountTaxCompensationAmount() + - $order->getShippingDiscountTaxCompensationRefunded(); + $baseShippingDiscountTaxCompensationAmount = $order->getBaseShippingDiscountTaxCompensationAmnt() + - $order->getBaseShippingDiscountTaxCompensationRefunded(); } + $totalTax += $shippingTaxAmount; $baseTotalTax += $baseShippingTaxAmount; $totalDiscountTaxCompensation += $shippingDiscountTaxCompensationAmount; $baseTotalDiscountTaxCompensation += $baseShippingDiscountTaxCompensationAmount; } - $allowedTax = $order->getTaxInvoiced() - $order->getTaxRefunded() - $creditmemo->getTaxAmount(); - $allowedBaseTax = $order->getBaseTaxInvoiced() - $order->getBaseTaxRefunded() - $creditmemo->getBaseTaxAmount(); - $allowedDiscountTaxCompensation = $order->getDiscountTaxCompensationInvoiced() + - $order->getShippingDiscountTaxCompensationAmount() - - $order->getDiscountTaxCompensationRefunded() - - $order->getShippingDiscountTaxCompensationRefunded() - - $creditmemo->getDiscountTaxCompensationAmount() - - $creditmemo->getShippingDiscountTaxCompensationAmount(); - $allowedBaseDiscountTaxCompensation = $order->getBaseDiscountTaxCompensationInvoiced() + - $order->getBaseShippingDiscountTaxCompensationAmnt() - - $order->getBaseDiscountTaxCompensationRefunded() - - $order->getBaseShippingDiscountTaxCompensationRefunded() - - $creditmemo->getBaseShippingDiscountTaxCompensationAmnt() - - $creditmemo->getBaseDiscountTaxCompensationAmount(); + $allowedTax = $this->calculateAllowedTax($creditmemo); + $allowedBaseTax = $this->calculateAllowedBaseTax($creditmemo); + $allowedDiscountTaxCompensation = $this->calculateAllowedDiscountTaxCompensation($creditmemo); + $allowedBaseDiscountTaxCompensation = $this->calculateAllowedBaseDiscountTaxCompensation($creditmemo); if ($creditmemo->isLast() && !$isPartialShippingRefunded) { $totalTax = $allowedTax; @@ -161,10 +149,11 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) } else { $totalTax = min($allowedTax, $totalTax); $baseTotalTax = min($allowedBaseTax, $baseTotalTax); - $totalDiscountTaxCompensation = - min($allowedDiscountTaxCompensation, $totalDiscountTaxCompensation); - $baseTotalDiscountTaxCompensation = - min($allowedBaseDiscountTaxCompensation, $baseTotalDiscountTaxCompensation); + $totalDiscountTaxCompensation = min($allowedDiscountTaxCompensation, $totalDiscountTaxCompensation); + $baseTotalDiscountTaxCompensation = min( + $allowedBaseDiscountTaxCompensation, + $baseTotalDiscountTaxCompensation + ); } $creditmemo->setTaxAmount($creditmemo->getTaxAmount() + $totalTax); @@ -177,9 +166,91 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) $creditmemo->setGrandTotal($creditmemo->getGrandTotal() + $totalTax + $totalDiscountTaxCompensation); $creditmemo->setBaseGrandTotal( - $creditmemo->getBaseGrandTotal() + - $baseTotalTax + $baseTotalDiscountTaxCompensation + $creditmemo->getBaseGrandTotal() + $baseTotalTax + $baseTotalDiscountTaxCompensation ); return $this; + + } + + /** + * Calculate allowed to Credit Memo tax amount + * + * @param Creditmemo $creditMemo + * @return float + */ + private function calculateAllowedTax(Creditmemo $creditMemo): float + { + $invoice = $creditMemo->getInvoice(); + $order = $creditMemo->getOrder(); + $amount = $invoice !== null ? $invoice->getTaxAmount() : $order->getTaxInvoiced(); + + return (float) $amount - $order->getTaxRefunded() - $creditMemo->getTaxAmount(); + } + + /** + * Calculate allowed to Credit Memo tax amount in the base currency + * + * @param Creditmemo $creditMemo + * @return float + */ + private function calculateAllowedBaseTax(Creditmemo $creditMemo): float + { + $invoice = $creditMemo->getInvoice(); + $order = $creditMemo->getOrder(); + $amount = $invoice !== null ? $invoice->getBaseTaxAmount() : $order->getBaseTaxInvoiced(); + + return (float) $amount - $order->getBaseTaxRefunded() - $creditMemo->getBaseTaxAmount(); + } + + /** + * Calculate allowed to Credit Memo discount tax compensation amount + * + * @param Creditmemo $creditMemo + * @return float + */ + private function calculateAllowedDiscountTaxCompensation(Creditmemo $creditMemo): float + { + $invoice = $creditMemo->getInvoice(); + $order = $creditMemo->getOrder(); + + if ($invoice) { + $amount = $invoice->getDiscountTaxCompensationAmount() + + $invoice->getShippingDiscountTaxCompensationAmount(); + } else { + $amount = $order->getDiscountTaxCompensationInvoiced() + + $order->getShippingDiscountTaxCompensationAmount(); + } + + return (float) $amount + - $order->getDiscountTaxCompensationRefunded() + - $order->getShippingDiscountTaxCompensationRefunded() + - $creditMemo->getDiscountTaxCompensationAmount() + - $creditMemo->getShippingDiscountTaxCompensationAmount(); + } + + /** + * Calculate allowed to Credit Memo discount tax compensation amount in the base currency + * + * @param Creditmemo $creditMemo + * @return float + */ + private function calculateAllowedBaseDiscountTaxCompensation(Creditmemo $creditMemo): float + { + $invoice = $creditMemo->getInvoice(); + $order = $creditMemo->getOrder(); + + if ($invoice) { + $amount = $invoice->getBaseDiscountTaxCompensationAmount() + + $invoice->getBaseShippingDiscountTaxCompensationAmnt(); + } else { + $amount = $order->getBaseDiscountTaxCompensationInvoiced() + + $order->getBaseShippingDiscountTaxCompensationAmnt(); + } + + return (float) $amount + - $order->getBaseDiscountTaxCompensationRefunded() + - $order->getBaseShippingDiscountTaxCompensationRefunded() + - $creditMemo->getBaseShippingDiscountTaxCompensationAmnt() + - $creditMemo->getBaseDiscountTaxCompensationAmount(); } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php index 94346fc1b7a28..7c9d249124a9a 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php @@ -17,6 +17,9 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +/** + * Class to test Collecting credit memo taxes + */ class TaxTest extends TestCase { /** @@ -44,6 +47,9 @@ class TaxTest extends TestCase */ protected $invoice; + /** + * @inheritdoc + */ protected function setUp(): void { $this->objectManager = new ObjectManager($this); @@ -188,6 +194,8 @@ public function collectDataProvider() 'base_tax_amount' => 0.82, 'invoice' => new MagentoObject( [ + 'tax_amount' => 24.33, + 'base_tax_amount' => 24.33, 'shipping_tax_amount' => 2.45, 'base_shipping_tax_amount' => 2.45, 'shipping_discount_tax_compensation_amount' => 0, @@ -277,6 +285,8 @@ public function collectDataProvider() 'base_tax_amount' => 0.82, 'invoice' => new MagentoObject( [ + 'tax_amount' => 24.33 * $currencyRatio, + 'base_tax_amount' => 24.33, 'shipping_tax_amount' => 2.45 * $currencyRatio, 'base_shipping_tax_amount' => 2.45, 'shipping_discount_tax_compensation_amount' => 0, @@ -352,6 +362,8 @@ public function collectDataProvider() 'base_tax_amount' => 1.65, 'invoice' => new MagentoObject( [ + 'tax_amount' => 11.14, + 'base_tax_amount' => 11.14, 'shipping_tax_amount' => 1.24, 'base_shipping_tax_amount' => 1.24, 'shipping_discount_tax_compensation_amount' => 0, @@ -428,6 +440,8 @@ public function collectDataProvider() 'base_tax_amount' => 0.82, 'invoice' => new MagentoObject( [ + 'tax_amount' => 16.09, + 'base_tax_amount' => 16.09, 'shipping_tax_amount' => 1.24, 'base_shipping_tax_amount' => 1.24, 'shipping_discount_tax_compensation_amount' => 0, @@ -507,14 +521,6 @@ public function collectDataProvider() 'base_shipping_amount' => 0, 'tax_amount' => 0.76, 'base_tax_amount' => 0.76, - 'invoice' => new MagentoObject( - [ - 'shipping_tax_amount' => 0, - 'base_shipping_tax_amount' => 0, - 'shipping_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, - ] - ), ], ], 'expected_results' => [ @@ -583,6 +589,8 @@ public function collectDataProvider() 'base_tax_amount' => 0.82, 'invoice' => new MagentoObject( [ + 'tax_amount' => 16.09, + 'base_tax_amount' => 16.09, 'shipping_tax_amount' => 1.24, 'base_shipping_tax_amount' => 1.24, 'shipping_discount_tax_compensation_amount' => 0, @@ -712,14 +720,6 @@ public function collectDataProvider() 'base_shipping_amount' => 0, 'tax_amount' => 0, 'base_tax_amount' => 0, - 'invoice' => new MagentoObject( - [ - 'shipping_tax_amount' => 0, - 'base_shipping_tax_amount' => 0, - 'shipping_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, - ] - ), ], ], 'expected_results' => [ From 3a808ffdac1301bfa61a2add14d41e59b5a3bd0f Mon Sep 17 00:00:00 2001 From: Sergiy Vasiutynskyi <s.vasiutynskyi@atwix.com> Date: Thu, 17 Dec 2020 16:10:03 +0200 Subject: [PATCH 043/112] Removed usage or changed value of CliIndexerReindexActionGroup action group for Catalog module --- .../Test/AddOutOfStockProductToCompareListTest.xml | 4 ++-- .../Mftf/Test/AdminAddInStockProductToTheCartTest.xml | 5 +---- ...CheckCustomAttributeValuesAfterProductSaveTest.xml | 4 +--- .../AdminSimpleProductImagesTest.xml | 4 +--- .../AdminUpdateFlatCategoryNameAndDescriptionTest.xml | 11 +++-------- .../Test/AdminUpdateSimpleProductTieredPriceTest.xml | 5 +---- ...gularPriceInStockVisibleInCatalogAndSearchTest.xml | 5 +---- ...ithRegularPriceInStockVisibleInCatalogOnlyTest.xml | 5 +---- .../Test/Mftf/Test/CheckTierPricingOfProductsTest.xml | 5 +---- .../EndToEndB2CGuestUserTest.xml | 5 +---- ...ProductAvailableAfterEnablingSubCategoriesTest.xml | 2 +- ...toreFrontProductsDisplayUsingElasticSearchTest.xml | 4 +--- .../Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml | 4 +--- .../StoreFrontRecentlyViewedAtStoreViewLevelTest.xml | 4 +--- ...refrontCheckDefaultNumberProductsToDisplayTest.xml | 5 +---- .../StorefrontProductNameWithHTMLEntitiesTest.xml | 4 +--- ...oryProductAndProductCategoryPartialReindexTest.xml | 4 +--- 17 files changed, 20 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml index 92be79fdfe720..212400ac4dd6e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml @@ -52,8 +52,8 @@ <!--Clear cache and reindex--> <comment userInput="Clear cache and reindex" stepKey="cleanCache"/> <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> -</actionGroup> + <argument name="indices" value="catalog_product_price"/> + </actionGroup> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> <argument name="tags" value=""/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml index 94d3b46aaa5f1..73019bb5ec0e0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml @@ -58,10 +58,7 @@ <actionGroup ref="AdminSubmitAdvancedInventoryFormActionGroup" stepKey="clickOnDoneButton"/> <actionGroup ref="AdminProductFormSaveActionGroup" stepKey="clickOnSaveButton"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> - <!--Clear cache and reindex--> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> <argument name="tags" value=""/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml index 0bdf19c9b8950..ca0616213c593 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml @@ -33,9 +33,7 @@ </createData> <!-- Create simple product --> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexCatalogSearch"> - <argument name="indices" value="catalogsearch_fulltext"/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindexCatalogSearch"/> <!-- Login to Admin page --> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml index de116b26d1414..8a33f6132aeb9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest/AdminSimpleProductImagesTest.xml @@ -141,9 +141,7 @@ <!-- Save the second product --> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> <argument name="tags" value=""/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml index 27a834833ed76..2124efed31293 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml @@ -34,10 +34,7 @@ <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 1"/> <!--Open Index Management Page and Select Index mode "Update by Schedule" --> <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="schedule" /> - <!--Run full reindex and clear caches --> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> <argument name="tags" value=""/> </actionGroup> @@ -45,9 +42,7 @@ <after> <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 0 "/> <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="realtime" /> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="indexerReindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="indexerReindex"/> <deleteData stepKey="deleteCategory" createDataKey="createCategory" /> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> <argument name="customStore" value="customStoreEN"/> @@ -71,7 +66,7 @@ <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> <!--Run full reindex and clear caches --> <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> + <argument name="indices" value="catalog_category_flat"/> </actionGroup> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> <argument name="tags" value=""/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml index 300b312612253..95d5e4d349478 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml @@ -26,10 +26,7 @@ </createData> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> - <!--TODO: REMOVE AFTER FIX MC-21717 --> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> <argument name="tags" value="full_page"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index 320edba5feeff..1735dc692ed3b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -91,10 +91,7 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice245InStock.urlKey}}" stepKey="seeUrlKey"/> - <!--Run re-index task --> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <!--Verify customer see updated simple product link on category page --> <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml index 77c3e7548a3cf..bd3160248682e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml @@ -91,10 +91,7 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice32501InStock.urlKey}}" stepKey="seeUrlKey"/> - <!--Run re-index task --> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <!--Verify customer see updated simple product link on category page --> <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml index 5f7e9c4225c00..2095d56ce6c59 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml @@ -332,10 +332,7 @@ <createData entity="CustomerAccountSharingDefault" stepKey="setConfigCustomerAccountDefault"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - <!--Do reindex and flush cache--> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> <argument name="tags" value=""/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml index ff68bba78cae8..aed2976df8f73 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest/EndToEndB2CGuestUserTest.xml @@ -56,10 +56,7 @@ <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> </after> - <!--Re-index--> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <!-- Step 1: User browses catalog --> <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml index 7fd752d7df98d..b5980cf977791 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml @@ -50,7 +50,7 @@ <!--Run re-index task--> <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> + <argument name="indices" value="cataloginventory_stock"/> </actionGroup> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml index cde7b14614f8e..3d3867d1efcf1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml @@ -114,9 +114,7 @@ </createData> <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="performReindex"> - <argument name="indices" value="catalogsearch_fulltext"/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="performReindex"/> <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanFullPageCache"> <argument name="tags" value="full_page"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml index e1b5aca6382e9..64ad348257853 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml @@ -74,9 +74,7 @@ </actionGroup> <!-- Logout Admin --> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCacheAfterDeletion"> <argument name="tags" value=""/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml index 0117493906de1..4d04a25c8d12f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml @@ -66,9 +66,7 @@ <!-- Logout Admin --> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCacheAfterDeletion"> <argument name="tags" value=""/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml index a73bd5a533ad0..ca561e4af70de 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml @@ -187,10 +187,7 @@ <seeInField selector="{{AdminCatalogStorefrontConfigSection.productsPerPageAllowedValues}}" userInput="12,24,36" stepKey="seeDefaultValueAllowedNumberProductsPerPage"/> <seeInField selector="{{AdminCatalogStorefrontConfigSection.productsPerPageDefaultValue}}" userInput="12" stepKey="seeDefaultValueProductPerPage"/> - <!-- Perform reindex and flush cache --> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> <argument name="tags" value=""/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml index 2156178ea88d0..9819357704d44 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuoteTest/StorefrontProductNameWithHTMLEntitiesTest.xml @@ -33,9 +33,7 @@ </after> <!--Run re-index task--> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <!--Check product in category listing--> <amOnPage url="{{StorefrontCategoryPage.url($$createCategoryOne.name$$)}}" stepKey="navigateToCategoryPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml index ce04b377300f8..cf1bb065349b6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml @@ -65,9 +65,7 @@ <after> <!-- Change "Category Products" and "Product Categories" indexers to "Update on Save" mode --> <magentoCLI command="indexer:set-mode" arguments="realtime" stepKey="setRealtimeMode"/> - <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value=""/> - </actionGroup> + <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="reindex"/> <!-- Delete data --> <deleteData createDataKey="productA" stepKey="deleteProductA"/> From 42e25513694695a1383fa183a981566a4d717c8a Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Wed, 16 Dec 2020 17:14:57 -0600 Subject: [PATCH 044/112] MC-39895: PayPal PayflowPro redirect Parameter list format error - Fix checkout with Credit Card (Payflow Pro) fails if billing address has special characters (&, =) --- .../Paypal/Model/Payflow/Service/Gateway.php | 68 ++++++- .../Model/Payflow/Service/GatewayTest.php | 177 +++++++++++++++--- 2 files changed, 217 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Gateway.php b/app/code/Magento/Paypal/Model/Payflow/Service/Gateway.php index 6a2229c3d55ca..374af021cbf38 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Gateway.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Gateway.php @@ -85,7 +85,8 @@ public function postRequest(DataObject $request, ConfigInterface $config) ); $client->setConfig($clientConfig); $client->setMethod(\Zend_Http_Client::POST); - $client->setParameterPost($request->getData()); + $requestData = $this->prepareRequestData($request->getData()); + $client->setParameterPost($requestData); $client->setHeaders( [ 'X-VPS-VIT-CLIENT-CERTIFICATION-ID' => '33baf5893fc2123d8b191d2d011b7fdc', @@ -97,9 +98,7 @@ public function postRequest(DataObject $request, ConfigInterface $config) try { $response = $client->request(); - - $responseArray = []; - parse_str(strstr($response->getBody(), 'RESULT'), $responseArray); + $responseArray = $this->parseNVP(strstr($response->getBody(), 'RESULT')); $result->setData(array_change_key_case($responseArray, CASE_LOWER)); $result->setData('result_code', $result->getData('result')); @@ -115,7 +114,7 @@ public function postRequest(DataObject $request, ConfigInterface $config) } finally { $this->logger->debug( [ - 'request' => $request->getData(), + 'request' => $requestData, 'result' => $result->getData() ], (array)$config->getValue('getDebugReplacePrivateDataKeys'), @@ -125,4 +124,63 @@ public function postRequest(DataObject $request, ConfigInterface $config) return $result; } + + /** + * Add length tag to parameters name which contains special characters: =, & + * + * The length tag specifies the exact number of characters and spaces (number of bytes) that appear in the value + * eg ['COMPANYNAME[14]' => 'Ruff & Johnson')] + * + * @param array $data + * @return array + */ + private function prepareRequestData(array $data): array + { + $requestData = []; + foreach ($data as $k => $v) { + if (strpos($v, '&') !== false || strpos($v, '=') !== false) { + $requestData[$k . '[' . strlen($v) . ']'] = $v; + } else { + $requestData[$k] = $v; + } + } + return $requestData; + } + + /** + * Parse NVP string into array + * + * Use length tag (if present) to parse the key value. + * + * The length tag specifies the exact number of characters and spaces (number of bytes) that appear in the value + * e.g COMPANYNAME[14]=Ruff & Johnson + * e.g COMMENT1[7]=Level=5 + * + * @param string $nvp + * @return array + */ + private function parseNVP(string $nvp): array + { + $result = []; + while (strlen($nvp) > 0) { + $keyPos = strpos($nvp, '='); + if ($keyPos !== false) { + $key = substr($nvp, 0, $keyPos); + if (preg_match('/\[(\d+)]$/', $key, $keyParts)) { + $valueLength = (int) $keyParts[1]; + $key = substr($key, 0, strpos($key, '[')); + $result[$key] = substr($nvp, $keyPos + 1, $valueLength); + $valuePos = $keyPos + 1 + $valueLength; + } else { + $valuePos = strpos($nvp, '&') ? strpos($nvp, '&') : strlen($nvp); + $value = substr($nvp, $keyPos + 1, $valuePos - $keyPos - 1); + $result[$key] = $value; + } + $nvp = substr($nvp, $valuePos + 1); + } else { + $nvp = ''; + } + } + return $result; + } } diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/GatewayTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/GatewayTest.php index 194b708a0352b..a2d8111ec33c6 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/GatewayTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/Service/GatewayTest.php @@ -17,27 +17,43 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; +use ReflectionMethod; +use Zend_Http_Client_Exception; +use Zend_Http_Response; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GatewayTest extends TestCase { - /** @var Gateway|MockObject */ - protected $object; - - /** @var ZendClientFactory|MockObject */ - protected $httpClientFactoryMock; - - /** @var Random|MockObject */ - protected $mathRandomMock; - - /** @var Logger|MockObject */ - protected $loggerMock; - - /** @var ZendClient|MockObject */ - protected $zendClientMock; - + /** + * @var Gateway|MockObject + */ + private $object; + + /** + * @var ZendClientFactory|MockObject + */ + private $httpClientFactoryMock; + + /** + * @var Random|MockObject + */ + private $mathRandomMock; + + /** + * @var Logger|MockObject + */ + private $loggerMock; + + /** + * @var ZendClient|MockObject + */ + private $zendClientMock; + + /** + * @inheritdoc + */ protected function setUp(): void { $this->httpClientFactoryMock = $this->getMockBuilder(ZendClientFactory::class) @@ -66,24 +82,28 @@ protected function setUp(): void ); } - public function testPostRequestOk() + /** + * @param string $nvpResponse + * @param array $expectedResult + * @dataProvider postRequestOkDataProvider + */ + public function testPostRequestOk(string $nvpResponse, array $expectedResult): void { $configMap = [ ['getDebugReplacePrivateDataKeys', null, ['masked']], ['debug', null, true] ]; - $expectedResponse = 'RESULT=0&RESPMSG=Approved&SECURETOKEN=8ZIaw2&SECURETOKENID=2481d53'; /** @var ConfigInterface|MockObject $configInterfaceMock */ $configInterfaceMock = $this->getMockBuilder(ConfigInterface::class) ->getMockForAbstractClass(); - $zendResponseMock = $this->getMockBuilder(\Zend_Http_Response::class) + $zendResponseMock = $this->getMockBuilder(Zend_Http_Response::class) ->setMethods(['getBody']) ->disableOriginalConstructor() ->getMock(); $zendResponseMock->expects(static::once()) ->method('getBody') - ->willReturn($expectedResponse); + ->willReturn($nvpResponse); $this->zendClientMock->expects(static::once()) ->method('request') ->willReturn($zendResponseMock); @@ -98,8 +118,119 @@ public function testPostRequestOk() $result = $this->object->postRequest($object, $configInterfaceMock); - static::assertInstanceOf(DataObject::class, $result); - static::assertArrayHasKey('result_code', $result->getData()); + static::assertEquals($expectedResult, $result->toArray()); + } + + /** + * @return array[] + */ + public function postRequestOkDataProvider(): array + { + return [ + [ + 'RESULT=0&RESPMSG=Approved&SECURETOKEN=9tl4MmP46NUadl9pwCKFgfQjA' + . '&SECURETOKENID=vVWBMSNb9j0SLlYw4AbqBnKmuogtzNNC', + [ + 'result' => '0', + 'securetoken' => '9tl4MmP46NUadl9pwCKFgfQjA', + 'securetokenid' => 'vVWBMSNb9j0SLlYw4AbqBnKmuogtzNNC', + 'respmsg' => 'Approved', + 'result_code' => '0', + ] + ], + [ + 'RESULT=0&PNREF=A30A3A958244&RESPMSG=Approved&AUTHCODE=028PNI&AVSADDR=N&AVSZIP=N&HOSTCODE=A' + . '&PROCAVS=N&VISACARDLEVEL=12&TRANSTIME=2020-12-16 14:43:57&FIRSTNAME[4]=Joé' + . '&LASTNAME=O\'Reilly&COMPANYNAME[14]=Ruff & Johnson&COMMENT1[7]=Level=5' + . '&AMT=30.00&ACCT=1111&EXPDATE=1224&CARDTYPE=0&IAVS=N', + [ + 'result' => '0', + 'pnref' => 'A30A3A958244', + 'respmsg' => 'Approved', + 'authcode' => '028PNI', + 'avsaddr' => 'N', + 'avszip' => 'N', + 'hostcode' => 'A', + 'procavs' => 'N', + 'visacardlevel' => '12', + 'transtime' => '2020-12-16 14:43:57', + 'firstname' => 'Joé', + 'lastname' => 'O\'Reilly', + 'companyname' => 'Ruff & Johnson', + 'comment1' => 'Level=5', + 'amt' => '30.00', + 'acct' => '1111', + 'expdate' => '1224', + 'cardtype' => '0', + 'iavs' => 'N', + 'result_code' => '0', + ] + ], + ]; + } + + /** + * @param array $requestData + * @param string $requestBody + * @dataProvider requestBodyDataProvider + */ + public function testRequestBody(array $requestData, string $requestBody): void + { + $configMap = [ + ['getDebugReplacePrivateDataKeys', null, ['masked']], + ['debug', null, true] + ]; + + /** @var ConfigInterface|MockObject $configInterfaceMock */ + $configInterfaceMock = $this->getMockBuilder(ConfigInterface::class) + ->getMockForAbstractClass(); + $zendResponseMock = $this->getMockBuilder(Zend_Http_Response::class) + ->setMethods(['getBody']) + ->disableOriginalConstructor() + ->getMock(); + $zendResponseMock->expects(static::once()) + ->method('getBody') + ->willReturn('RESULT=0&RESPMSG=Approved'); + $this->zendClientMock->expects(static::once()) + ->method('request') + ->willReturn($zendResponseMock); + + $configInterfaceMock->expects(static::any()) + ->method('getValue') + ->willReturnMap($configMap); + $this->loggerMock->expects(static::once()) + ->method('debug'); + + $request = new DataObject($requestData); + $this->object->postRequest($request, $configInterfaceMock); + $method = new ReflectionMethod($this->zendClientMock, '_prepareBody'); + $method->setAccessible(true); + $this->assertEquals($requestBody, $method->invoke($this->zendClientMock)); + } + + /** + * @return array[] + */ + public function requestBodyDataProvider(): array + { + return [ + [ + [ + 'companyname' => 'Ruff & Johnson', + 'comment1' => 'Level=5', + 'shiptofirstname' => 'Joé', + 'shiptolastname' => 'O\'Reilly', + 'shiptostreet' => '4659 Rainbow Road', + 'shiptocity' => 'Los Angeles', + 'shiptostate' => 'CA', + 'shiptozip' => '90017', + 'shiptocountry' => 'US', + ], + 'companyname[14]=Ruff & Johnson&comment1[7]=Level=5&shiptofirstname=Joé&shiptolastname=O\'Reilly' + . '&shiptostreet=4659 Rainbow Road&shiptocity=Los Angeles&shiptostate=CA&shiptozip=90017' + . '&shiptocountry=US' + ] + ]; } public function testPostRequestFail() @@ -108,7 +239,7 @@ public function testPostRequestFail() /** @var ConfigInterface|MockObject $configInterfaceMock */ $configInterfaceMock = $this->getMockBuilder(ConfigInterface::class) ->getMockForAbstractClass(); - $zendResponseMock = $this->getMockBuilder(\Zend_Http_Response::class) + $zendResponseMock = $this->getMockBuilder(Zend_Http_Response::class) ->setMethods(['getBody']) ->disableOriginalConstructor() ->getMock(); @@ -116,7 +247,7 @@ public function testPostRequestFail() ->method('getBody'); $this->zendClientMock->expects(static::once()) ->method('request') - ->willThrowException(new \Zend_Http_Client_Exception()); + ->willThrowException(new Zend_Http_Client_Exception()); $object = new DataObject(); $this->object->postRequest($object, $configInterfaceMock); From b955411760da5f70e0bcc6a96462f9217e567e8a Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 17 Dec 2020 09:52:49 -0600 Subject: [PATCH 045/112] MC-39861: Customer is redirected to the blank page after using PayPal WPPHS payment on checkout --- .../Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php index d52c2501a565a..b3870c69b23f0 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Hostedpro/ReturnActionTest.php @@ -8,7 +8,7 @@ namespace Magento\Paypal\Controller\Hostedpro; use Magento\TestFramework\TestCase\AbstractController; -use Zend\Stdlib\Parameters; +use Laminas\Stdlib\Parameters; /** * Tests PayPal HostedPro return controller. From 37e8487aca8692b89c9657215ea0c9618cc5d967 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Thu, 17 Dec 2020 17:55:16 +0200 Subject: [PATCH 046/112] added AdminClickRefundOfflineOnMemoDetailPageActionGroup --- ...dableProductLinkAfterPartialRefundTest.xml | 4 ++-- ...kRefundOfflineOnNewMemoPageActionGroup.xml | 19 +++++++++++++++++++ ...reateCreditMemoBankTransferPaymentTest.xml | 4 ++-- ...AdminCreateCreditMemoPartialRefundTest.xml | 4 ++-- ...nCreateCreditMemoWithPurchaseOrderTest.xml | 4 ++-- .../Test/Mftf/Test/EndToEndB2CAdminTest.xml | 4 ++-- 6 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickRefundOfflineOnNewMemoPageActionGroup.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAccountDownloadableProductLinkAfterPartialRefundTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAccountDownloadableProductLinkAfterPartialRefundTest.xml index b89aa7d126686..41fc2850e00ff 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAccountDownloadableProductLinkAfterPartialRefundTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAccountDownloadableProductLinkAfterPartialRefundTest.xml @@ -98,8 +98,8 @@ <argument name="rowNumber" value="1"/> </actionGroup> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> - <waitForPageLoad stepKey="waitForResultPage"/> + <actionGroup ref="AdminClickRefundOfflineOnNewMemoPageActionGroup" stepKey="clickRefundOffline"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForResultPage"/> <actionGroup ref="StorefrontNotAssertDownloadableProductLinkInCustomerAccountActionGroup" stepKey="dontSeeStorefrontMyAccountDownloadableProductsLink"> <argument name="product" value="$$createDownloadableProduct$$"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickRefundOfflineOnNewMemoPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickRefundOfflineOnNewMemoPageActionGroup.xml new file mode 100644 index 0000000000000..21ca2d51a364e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickRefundOfflineOnNewMemoPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminClickRefundOfflineOnNewMemoPageActionGroup"> + <annotations> + <description>Click the Refund Offline button on the New Memo page</description> + </annotations> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccesMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the credit memo." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml index 6ed8510db777c..9d1daf0d2ded1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml @@ -85,8 +85,8 @@ </actionGroup> <!-- On order's page click 'Refund offline' button --> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> - <waitForPageLoad stepKey="waitForResultPage"/> + <actionGroup ref="AdminClickRefundOfflineOnNewMemoPageActionGroup" stepKey="clickRefundOffline"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForResultPage"/> <!-- Perform all assertions: assert refund success create message --> <see selector="{{AdminIndexManagementSection.successMessage}}" userInput="You created the credit memo." stepKey="assertRefundSuccessCreateMessage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml index 68301187d3d31..5d2f76b80e3f8 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml @@ -78,8 +78,8 @@ </actionGroup> <!-- On order's page click 'Refund offline' button --> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> - <waitForPageLoad stepKey="waitForResultPage"/> + <actionGroup ref="AdminClickRefundOfflineOnNewMemoPageActionGroup" stepKey="clickRefundOffline"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForResultPage"/> <!-- Perform all assertions: assert refund success create message --> <waitForElementVisible selector="{{AdminIndexManagementSection.successMessage}}" stepKey="waitForSuccessMessage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml index 141fa2a9e5d06..32c3a5e0a5846 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml @@ -88,8 +88,8 @@ </actionGroup> <!-- On order's page click 'Refund offline' button --> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> - <waitForPageLoad stepKey="waitForResultPage"/> + <actionGroup ref="AdminClickRefundOfflineOnNewMemoPageActionGroup" stepKey="clickRefundOffline"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForResultPage"/> <!-- Perform all assertions: assert refund success create message --> <see selector="{{AdminIndexManagementSection.successMessage}}" userInput="You created the credit memo." stepKey="assertRefundSuccessCreateMessage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 6ba1c3ac3deec..2a3284e7e8e35 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -140,8 +140,8 @@ <argument name="billingAddress" value="US_Address_TX"/> </actionGroup> <!--Submit credit memo--> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline" after="verifyOrderCreditMemoInformation"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the credit memo." stepKey="seeCreditMemoSuccess" after="clickRefundOffline"/> + <actionGroup ref="AdminClickRefundOfflineOnNewMemoPageActionGroup" stepKey="clickRefundOffline" after="verifyOrderCreditMemoInformation"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeCreditMemoSuccess" after="clickRefundOffline"/> <click selector="{{AdminOrderDetailsOrderViewSection.creditMemos}}" stepKey="clickOrderCreditMemosTab" after="seeCreditMemoSuccess"/> <waitForLoadingMaskToDisappear stepKey="waitForCreditMemoTabLoadingMask" after="clickOrderCreditMemosTab"/> <see selector="{{AdminOrderCreditMemosTabSection.gridRow('1')}}" userInput="{{Simple_US_Customer.firstname}}" stepKey="seeOrderCreditMemoInTabGrid" after="waitForCreditMemoTabLoadingMask"/> From b07440c2f304658730298ef0d22d3825c2e8aa06 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Thu, 17 Dec 2020 11:06:03 -0600 Subject: [PATCH 047/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../Magento/UrlRewrite/Model/UrlRewrite.php | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php index d82781ee66450..cbefcd4c75597 100644 --- a/app/code/Magento/UrlRewrite/Model/UrlRewrite.php +++ b/app/code/Magento/UrlRewrite/Model/UrlRewrite.php @@ -145,23 +145,27 @@ public function setMetadata($metadata) */ private function getFinalTargetUrlRewrite(string $path, int $storeId): ?UrlRewriteService { + $urlRewriteTarget = $this->urlFinder->findOneByData( + [ + 'request_path' => $path, + 'store_id' => $storeId + ] + ); + + while ( + $urlRewriteTarget && + $urlRewriteTarget->getTargetPath() !== $urlRewriteTarget->getRequestPath() && + $urlRewriteTarget->getRedirectType() > 0 + ) { $urlRewriteTarget = $this->urlFinder->findOneByData( [ - 'request_path' => $path, - 'store_id' => $storeId + 'request_path' => $urlRewriteTarget->getTargetPath(), + 'store_id' => $urlRewriteTarget->getStoreId() ] ); + } - while ($urlRewriteTarget && $urlRewriteTarget->getRedirectType() > 0) { - $urlRewriteTarget = $this->urlFinder->findOneByData( - [ - 'request_path' => $urlRewriteTarget->getTargetPath(), - 'store_id' => $urlRewriteTarget->getStoreId() - ] - ); - } - - return $urlRewriteTarget; + return $urlRewriteTarget; } /** From 650aaa661afb97e17e86ca7ff7e5e6bfadb51192 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Thu, 17 Dec 2020 16:33:26 -0600 Subject: [PATCH 048/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../GraphQl/UrlRewrite/UrlResolverTest.php | 179 +++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php index 7aed048f1c4ce..5b84537dddf94 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php @@ -7,8 +7,14 @@ namespace Magento\GraphQl\UrlRewrite; +use Magento\Framework\Exception\AlreadyExistsException; +use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewrite as UrlRewriteResourceModel; +use Magento\UrlRewrite\Model\UrlFinderInterface; +use Magento\UrlRewrite\Model\UrlRewrite as UrlRewriteModel; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteService; /** * Test the GraphQL endpoint's URLResolver query to verify canonical URL's are correctly returned. @@ -20,7 +26,7 @@ class UrlResolverTest extends GraphQlAbstract protected function setUp(): void { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = Bootstrap::getObjectManager(); } /** @@ -50,4 +56,175 @@ public function testNonExistentEntityUrlRewrite() ); $this->graphQlQuery($query); } + + /** + * Test for url rewrite to clean cache on rewrites update + * + * @magentoApiDataFixture Magento/Catalog/_files/product_with_category.php + * @magentoApiDataFixture Magento/Cms/_files/pages.php + * + * @dataProvider urlRewriteEntitiesDataProvider + * @param string $requestPath + * @throws AlreadyExistsException + */ + public function testUrlRewriteCleansCacheOnChange(string $requestPath) + { + + /** @var UrlRewriteResourceModel $urlRewriteResourceModel */ + $urlRewriteResourceModel = $this->objectManager->create(UrlRewriteResourceModel::class); + $storeId = 1; + $query = function ($requestUrl) { + return <<<QUERY +{ + urlResolver(url:"{$requestUrl}") + { + id + entity_uid + relative_url + type + redirectCode + } +} +QUERY; + }; + + // warming up urlResolver API response cache for entity and validate proper response + $apiResponse = $this->graphQlQuery($query($requestPath))['urlResolver']; + $this->assertEquals($requestPath, $apiResponse['relative_url']); + + $urlRewrite = $this->getUrlRewriteModelByRequestPath($requestPath, $storeId); + + // renaming entity request path and validating that API will not return cached response + $urlRewrite->setRequestPath('test' . $requestPath); + $urlRewriteResourceModel->save($urlRewrite); + $apiResponse = $this->graphQlQuery($query($requestPath))['urlResolver']; + $this->assertNull($apiResponse['relative_url']); + + // rolling back changes + $urlRewrite->setRequestPath($requestPath); + $urlRewriteResourceModel->save($urlRewrite); + } + + public function urlRewriteEntitiesDataProvider(): array + { + return [ + [ + 'simple-product-in-stock.html' + ], + [ + 'category-1.html' + ], + [ + 'page100' + ] + ]; + } + + /** + * Test for custom url rewrite to clean cache on update combinations + * + * @magentoApiDataFixture Magento/Catalog/_files/product_with_category.php + * @magentoApiDataFixture Magento/Cms/_files/pages.php + * + * @throws AlreadyExistsException + */ + public function testUrlRewriteCleansCacheForCustomRewrites() + { + + /** @var UrlRewriteResourceModel $urlRewriteResourceModel */ + $urlRewriteResourceModel = $this->objectManager->create(UrlRewriteResourceModel::class); + $storeId = 1; + $query = function ($requestUrl) { + return <<<QUERY +{ + urlResolver(url:"{$requestUrl}") + { + id + entity_uid + relative_url + type + redirectCode + } +} +QUERY; + }; + + $customRequestPath = 'test.html'; + $customSecondRequestPath = 'test2.html'; + $entitiesRequestPaths = [ + 'simple-product-in-stock.html', + 'category-1.html', + 'page100' + ]; + + // create custom url rewrite + $urlRewrite = $this->objectManager->create(UrlRewriteModel::class); + $urlRewrite->setEntityType('custom') + ->setRedirectType(302) + ->setStoreId($storeId) + ->setDescription(null) + ->setIsAutogenerated(0); + + // create second custom url rewrite and target it to previous one to check + // if proper final target url will be resolved + $secondUrlRewrite = $this->objectManager->create(UrlRewriteModel::class); + $secondUrlRewrite->setEntityType('custom') + ->setRedirectType(302) + ->setStoreId($storeId) + ->setRequestPath($customSecondRequestPath) + ->setTargetPath($customRequestPath) + ->setDescription(null) + ->setIsAutogenerated(0); + $urlRewriteResourceModel->save($secondUrlRewrite); + + foreach ($entitiesRequestPaths as $entityRequestPath) { + // updating custom rewrite for each entity + $urlRewrite->setRequestPath($customRequestPath) + ->setTargetPath($entityRequestPath); + $urlRewriteResourceModel->save($urlRewrite); + + // confirm that API returns non-cached response for the first custom rewrite + $apiResponse = $this->graphQlQuery($query($customRequestPath))['urlResolver']; + $this->assertEquals($entityRequestPath, $apiResponse['relative_url']); + + // confirm that API returns non-cached response for the second custom rewrite + $apiResponse = $this->graphQlQuery($query($customSecondRequestPath))['urlResolver']; + $this->assertEquals($entityRequestPath, $apiResponse['relative_url']); + } + + $urlRewriteResourceModel->delete($secondUrlRewrite); + + // delete custom rewrite and validate that API will not return cached response + $urlRewriteResourceModel->delete($urlRewrite); + $apiResponse = $this->graphQlQuery($query($customRequestPath))['urlResolver']; + $this->assertNull($apiResponse['relative_url']); + } + + /** + * Return UrlRewrite model instance by request_path + * + * @param string $requestPath + * @param int $storeId + * @return UrlRewriteModel + */ + private function getUrlRewriteModelByRequestPath(string $requestPath, int $storeId): UrlRewriteModel + { + /** @var UrlFinderInterface $urlFinder */ + $urlFinder = $this->objectManager->get(UrlFinderInterface::class); + + /** @var UrlRewriteService $urlRewriteService */ + $urlRewriteService = $urlFinder->findOneByData( + [ + 'request_path' => $requestPath, + 'store_id' => $storeId + ] + ); + + /** @var UrlRewriteModel $urlRewrite */ + $urlRewrite = $this->objectManager->create(UrlRewriteModel::class); + $urlRewrite->load($urlRewriteService->getUrlRewriteId()); + + return $urlRewrite; + } + } From b372bdb84636b44e6c94e092584e64b64713e413 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Thu, 17 Dec 2020 18:51:39 -0600 Subject: [PATCH 049/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../GraphQl/UrlRewrite/UrlResolverTest.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php index 5b84537dddf94..e6ed14ae2685e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php @@ -89,16 +89,16 @@ public function testUrlRewriteCleansCacheOnChange(string $requestPath) }; // warming up urlResolver API response cache for entity and validate proper response - $apiResponse = $this->graphQlQuery($query($requestPath))['urlResolver']; - $this->assertEquals($requestPath, $apiResponse['relative_url']); + $apiResponse = $this->graphQlQuery($query($requestPath)); + $this->assertEquals($requestPath, $apiResponse['urlResolver']['relative_url']); $urlRewrite = $this->getUrlRewriteModelByRequestPath($requestPath, $storeId); // renaming entity request path and validating that API will not return cached response $urlRewrite->setRequestPath('test' . $requestPath); $urlRewriteResourceModel->save($urlRewrite); - $apiResponse = $this->graphQlQuery($query($requestPath))['urlResolver']; - $this->assertNull($apiResponse['relative_url']); + $apiResponse = $this->graphQlQuery($query($requestPath)); + $this->assertNull($apiResponse['urlResolver']['relative_url']); // rolling back changes $urlRewrite->setRequestPath($requestPath); @@ -184,20 +184,20 @@ public function testUrlRewriteCleansCacheForCustomRewrites() $urlRewriteResourceModel->save($urlRewrite); // confirm that API returns non-cached response for the first custom rewrite - $apiResponse = $this->graphQlQuery($query($customRequestPath))['urlResolver']; - $this->assertEquals($entityRequestPath, $apiResponse['relative_url']); + $apiResponse = $this->graphQlQuery($query($customRequestPath)); + $this->assertEquals($entityRequestPath, $apiResponse['urlResolver']['relative_url']); // confirm that API returns non-cached response for the second custom rewrite - $apiResponse = $this->graphQlQuery($query($customSecondRequestPath))['urlResolver']; - $this->assertEquals($entityRequestPath, $apiResponse['relative_url']); + $apiResponse = $this->graphQlQuery($query($customSecondRequestPath)); + $this->assertEquals($entityRequestPath, $apiResponse['urlResolver']['relative_url']); } $urlRewriteResourceModel->delete($secondUrlRewrite); // delete custom rewrite and validate that API will not return cached response $urlRewriteResourceModel->delete($urlRewrite); - $apiResponse = $this->graphQlQuery($query($customRequestPath))['urlResolver']; - $this->assertNull($apiResponse['relative_url']); + $apiResponse = $this->graphQlQuery($query($customRequestPath)); + $this->assertNull($apiResponse['urlResolver']['relative_url']); } /** From 7edefd28ea67b16add4184008298e95ade8f1826 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Wed, 16 Dec 2020 14:03:36 -0600 Subject: [PATCH 050/112] MC-39896: Catalog price rules are not included in CartItemPrices in the API - Adding customer group to the customer session - Adding api test --- .../Pricing/Price/UpdateCatalogRulePrice.php | 68 +++++++ .../CatalogRuleGraphQl/etc/graphql/di.xml | 12 ++ .../GraphQl/CatalogGraphQl/PriceRangeTest.php | 175 ++++++++++++++++++ ...t_with_tier_prices_for_logged_in_group.php | 48 +++++ ...er_prices_for_logged_in_group_rollback.php | 10 + ...th_tier_prices_for_not_logged_in_group.php | 48 +++++ ...rices_for_not_logged_in_group_rollback.php | 10 + .../catalog_rule_25_customer_group_all.php | 39 ++++ ...og_rule_25_customer_group_all_rollback.php | 35 ++++ ...alog_rule_50_registered_customer_group.php | 39 ++++ ..._50_registered_customer_group_rollback.php | 35 ++++ 11 files changed, 519 insertions(+) create mode 100644 app/code/Magento/CatalogRuleGraphQl/Plugin/Pricing/Price/UpdateCatalogRulePrice.php create mode 100644 app/code/Magento/CatalogRuleGraphQl/etc/graphql/di.xml create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogGraphQl/PriceRangeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_25_customer_group_all.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_25_customer_group_all_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group_rollback.php diff --git a/app/code/Magento/CatalogRuleGraphQl/Plugin/Pricing/Price/UpdateCatalogRulePrice.php b/app/code/Magento/CatalogRuleGraphQl/Plugin/Pricing/Price/UpdateCatalogRulePrice.php new file mode 100644 index 0000000000000..61b9f70c49f04 --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/Plugin/Pricing/Price/UpdateCatalogRulePrice.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogRuleGraphQl\Plugin\Pricing\Price; + +use Magento\CatalogRule\Model\ResourceModel\Rule; +use Magento\CatalogRule\Pricing\Price\CatalogRulePrice; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; + +/** + * Class UpdateCatalogRulePrice + * + * Plugin to update catalog rule price based on customer group id + */ +class UpdateCatalogRulePrice +{ + /** + * @var TimezoneInterface + */ + private $dateTime; + + /** + * @var Rule + */ + private $ruleResource; + + /** + * @param TimezoneInterface $dateTime + * @param Rule $ruleResource + */ + public function __construct( + TimezoneInterface $dateTime, + Rule $ruleResource + ) { + $this->dateTime = $dateTime; + $this->ruleResource = $ruleResource; + } + + /** + * Returns catalog rule value for logged in customer group + * + * @param CatalogRulePrice $catalogRulePrice + * @param float|boolean $value + * @return float|boolean + */ + public function afterGetValue( + CatalogRulePrice $catalogRulePrice, + $value + ) { + $product = $catalogRulePrice->getProduct(); + if ($product && $product->getCustomerGroupId()) { + $store = $product->getStore(); + $value = $this->ruleResource->getRulePrice( + $this->dateTime->scopeDate($store->getId()), + $store->getWebsiteId(), + $product->getCustomerGroupId(), + $product->getId() + ); + $value = $value ? (float) $value : false; + } + + return $value; + } +} diff --git a/app/code/Magento/CatalogRuleGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogRuleGraphQl/etc/graphql/di.xml new file mode 100644 index 0000000000000..571783edece6c --- /dev/null +++ b/app/code/Magento/CatalogRuleGraphQl/etc/graphql/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\CatalogRule\Pricing\Price\CatalogRulePrice"> + <plugin name="update_catalog_rule_price_for_logged_in_customer_group" type="Magento\CatalogRuleGraphQl\Plugin\Pricing\Price\UpdateCatalogRulePrice"/> + </type> +</config> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogGraphQl/PriceRangeTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogGraphQl/PriceRangeTest.php new file mode 100644 index 0000000000000..81b08f28431a6 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogGraphQl/PriceRangeTest.php @@ -0,0 +1,175 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\CatalogGraphQl; + +use Magento\GraphQl\GetCustomerAuthenticationHeader; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test class to verify catalog price rule is applied for + * tier prices for different customer groups. + */ +class PriceRangeTest extends GraphQlAbstract +{ + /** + * @var ObjectManager|null + */ + private $objectManager; + + /** + * @var GetCustomerAuthenticationHeader + */ + private $getCustomerAuthenticationHeader; + + protected function setUp(): void + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->getCustomerAuthenticationHeader = $this->objectManager->get(GetCustomerAuthenticationHeader::class); + } + + /** + * Test for checking if catalog rule price has been applied for all customer group + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_25_customer_group_all.php + */ + public function testCheckIfCatalogRuleIsAppliedForTierPriceForAllGroups(): void + { + $productSku = 'simple'; + $query = $this->getProductSearchQuery($productSku); + + $response = $this->graphQlQuery($query); + + $this->assertNotEmpty($response['products']); + $priceRange = $response['products']['items'][0]['price_range']; + $this->assertEquals(10, $priceRange['minimum_price']['regular_price']['value']); + $this->assertEquals(7.5, $priceRange['minimum_price']['final_price']['value']); + $this->assertEquals(2.5, $priceRange['minimum_price']['discount']['amount_off']); + $this->assertEquals(25, $priceRange['minimum_price']['discount']['percent_off']); + $this->assertEquals(10, $priceRange['maximum_price']['regular_price']['value']); + $this->assertEquals(7.5, $priceRange['maximum_price']['final_price']['value']); + $this->assertEquals(2.5, $priceRange['maximum_price']['discount']['amount_off']); + $this->assertEquals(25, $priceRange['maximum_price']['discount']['percent_off']); + } + + /** + * Test for checking if catalog rule price has been applied for registered customer + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group.php + */ + public function testCheckIfCatalogRuleIsAppliedForTierPriceForRegisteredCustomer(): void + { + $productSku = 'simple'; + $query = $this->getProductSearchQuery($productSku); + $response = $this->graphQlQuery( + $query, + [], + '', + $this->getCustomerAuthenticationHeader->execute('customer@example.com', 'password') + ); + + $this->assertNotEmpty($response['products']); + $priceRange = $response['products']['items'][0]['price_range']; + $this->assertEquals(10, $priceRange['minimum_price']['regular_price']['value']); + $this->assertEquals(5, $priceRange['minimum_price']['final_price']['value']); + $this->assertEquals(5, $priceRange['minimum_price']['discount']['amount_off']); + $this->assertEquals(50, $priceRange['minimum_price']['discount']['percent_off']); + $this->assertEquals(10, $priceRange['maximum_price']['regular_price']['value']); + $this->assertEquals(5, $priceRange['maximum_price']['final_price']['value']); + $this->assertEquals(5, $priceRange['maximum_price']['discount']['amount_off']); + $this->assertEquals(50, $priceRange['maximum_price']['discount']['percent_off']); + } + + /** + * Test for checking if catalog rule price has been applied for guest + * + * @magentoApiDataFixture Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group.php + * @magentoApiDataFixture Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php + */ + public function testCheckIfCatalogRuleIsAppliedForTierPriceForGuest(): void + { + $productSku = 'simple'; + $query = $this->getProductSearchQuery($productSku); + $response = $this->graphQlQuery($query); + + $this->assertNotEmpty($response['products']); + $priceRange = $response['products']['items'][0]['price_range']; + $this->assertEquals(10, $priceRange['minimum_price']['regular_price']['value']); + $this->assertEquals(9, $priceRange['minimum_price']['final_price']['value']); + $this->assertEquals(1, $priceRange['minimum_price']['discount']['amount_off']); + $this->assertEquals(10, $priceRange['minimum_price']['discount']['percent_off']); + $this->assertEquals(10, $priceRange['maximum_price']['regular_price']['value']); + $this->assertEquals(9, $priceRange['maximum_price']['final_price']['value']); + $this->assertEquals(1, $priceRange['maximum_price']['discount']['amount_off']); + $this->assertEquals(10, $priceRange['maximum_price']['discount']['percent_off']); + } + + /** + * Get a query which user filter for product sku and returns price_tiers + * + * @param string $productSku + * @return string + */ + private function getProductSearchQuery(string $productSku): string + { + return <<<QUERY +{ + products(filter: {sku: {eq: "{$productSku}"}}) { + items { + name + sku + price_range { + minimum_price { + regular_price { + value + currency + } + final_price { + value + currency + } + discount { + amount_off + percent_off + } + } + maximum_price { + regular_price { + value + currency + } + final_price { + value + currency + } + discount { + amount_off + percent_off + } + } + } + price_tiers{ + discount{ + amount_off + percent_off + } + final_price{ + value + } + quantity + } + } + } +} +QUERY; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group.php new file mode 100644 index 0000000000000..35af54d574fd4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; +use Magento\Customer\Model\Group; + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple.php'); + +$objectManager = Bootstrap::getObjectManager(); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +$tpExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$product = $productRepository->get('simple', false, null, true); +$adminWebsite = $objectManager->get(WebsiteRepositoryInterface::class)->get('admin'); +$tierPriceExtensionAttributes = $tpExtensionAttributesFactory->create()->setWebsiteId($adminWebsite->getId()); +$pricesForCustomerGroupsInput = [ + [ + 'customer_group_id' => '1', + 'percentage_value'=> null, + 'qty'=> 1, + 'value'=> 20 + ], + [ + 'customer_group_id' => '1', + 'percentage_value'=> null, + 'qty'=> 2, + 'value'=> 30 + ] +]; +$productTierPrices = []; +foreach ($pricesForCustomerGroupsInput as $price) { + $productTierPrices[] = $tierPriceFactory->create( + [ + 'data' => $price + ] + )->setExtensionAttributes($tierPriceExtensionAttributes); +} +$product->setTierPrices($productTierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group_rollback.php new file mode 100644 index 0000000000000..328c1e229da5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_logged_in_group_rollback.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_rollback.php'); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group.php new file mode 100644 index 0000000000000..cfb89b7de3305 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; +use Magento\Customer\Model\Group; + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple.php'); + +$objectManager = Bootstrap::getObjectManager(); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +$tpExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$product = $productRepository->get('simple', false, null, true); +$adminWebsite = $objectManager->get(WebsiteRepositoryInterface::class)->get('admin'); +$tierPriceExtensionAttributes = $tpExtensionAttributesFactory->create()->setWebsiteId($adminWebsite->getId()); +$pricesForCustomerGroupsInput = [ + [ + 'customer_group_id' => Group::NOT_LOGGED_IN_ID, + 'percentage_value'=> null, + 'qty'=> 1, + 'value'=> 50 + ], + [ + 'customer_group_id' => Group::NOT_LOGGED_IN_ID, + 'percentage_value'=> null, + 'qty'=> 2, + 'value'=> 80 + ] +]; +$productTierPrices = []; +foreach ($pricesForCustomerGroupsInput as $price) { + $productTierPrices[] = $tierPriceFactory->create( + [ + 'data' => $price + ] + )->setExtensionAttributes($tierPriceExtensionAttributes); +} +$product->setTierPrices($productTierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group_rollback.php new file mode 100644 index 0000000000000..328c1e229da5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_prices_for_not_logged_in_group_rollback.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_rollback.php'); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_25_customer_group_all.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_25_customer_group_all.php new file mode 100644 index 0000000000000..2c31c9a8d688a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_25_customer_group_all.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** + * Creates simple Catalog Rule with the following data: + * active, applied to all products, without time limits, with 25% off for all customer groups + */ +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\Rule; +use Magento\Customer\Model\GroupManagement; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var $banner Rule */ +$catalogRule = Bootstrap::getObjectManager()->create( + Rule::class +); + +$catalogRule + ->setIsActive(1) + ->setName('Test Catalog Rule With 25 Percent Off') + ->setCustomerGroupIds('0') + ->setDiscountAmount(25) + ->setWebsiteIds([0 => 1]) + ->setSimpleAction('by_percent') + ->setStopRulesProcessing(false) + ->setSortOrder(0) + ->setSubIsEnable(0) + ->setSubDiscountAmount(0) + ->save(); + +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = Bootstrap::getObjectManager() + ->get(IndexBuilder::class); +$indexBuilder->reindexFull(); +sleep(1); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_25_customer_group_all_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_25_customer_group_all_rollback.php new file mode 100644 index 0000000000000..73e6bbff46648 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_25_customer_group_all_rollback.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Rule $catalogRuleResource */ +$catalogRuleResource = $objectManager->create(Rule::class); +$connection = $catalogRuleResource->getConnection(); + +//Retrieve rule id by name +$select = $connection->select(); +$select->from($catalogRuleResource->getMainTable(), 'rule_id'); +$select->where('name = ?', 'Test Catalog Rule With 25 Percent Off'); +$ruleId = $connection->fetchOne($select); + +try { + /** @var CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); + $ruleRepository->deleteById($ruleId); +} catch (\Exception $ex) { + //Nothing to remove +} +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +$indexBuilder->reindexFull(); +sleep(1); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group.php new file mode 100644 index 0000000000000..d583dace85536 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** + * Creates simple Catalog Rule with the following data: + * active, applied to all products, without time limits, with 50% off for registered customer groups + */ +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\Rule; +use Magento\Customer\Model\GroupManagement; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var $banner Rule */ +$catalogRule = Bootstrap::getObjectManager()->create( + Rule::class +); + +$catalogRule + ->setIsActive(1) + ->setName('Test Catalog Rule With 50 Percent Off') + ->setCustomerGroupIds('1') + ->setDiscountAmount(50) + ->setWebsiteIds([0 => 1]) + ->setSimpleAction('by_percent') + ->setStopRulesProcessing(false) + ->setSortOrder(0) + ->setSubIsEnable(0) + ->setSubDiscountAmount(0) + ->save(); + +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = Bootstrap::getObjectManager() + ->get(IndexBuilder::class); +$indexBuilder->reindexFull(); +sleep(1); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group_rollback.php new file mode 100644 index 0000000000000..46df301157b5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_registered_customer_group_rollback.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Rule $catalogRuleResource */ +$catalogRuleResource = $objectManager->create(Rule::class); +$connection = $catalogRuleResource->getConnection(); + +//Retrieve rule id by name +$select = $connection->select(); +$select->from($catalogRuleResource->getMainTable(), 'rule_id'); +$select->where('name = ?', 'Test Catalog Rule With 50 Percent Off'); +$ruleId = $connection->fetchOne($select); + +try { + /** @var CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); + $ruleRepository->deleteById($ruleId); +} catch (\Exception $ex) { + //Nothing to remove +} +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +$indexBuilder->reindexFull(); +sleep(1); From 8b873ad45946dbefbd75038baea32df29c49980b Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transogtgroup.com> Date: Fri, 18 Dec 2020 10:46:21 +0200 Subject: [PATCH 051/112] MC-39864: [Magento Cloud] - Tax Miscalculation --- .../Model/Order/Creditmemo/Total/Tax.php | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php index 9e6e8979e46ee..5fe4d0af7bd89 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -40,14 +40,14 @@ public function collect(Creditmemo $creditmemo) if ($orderItemQty) { /** Check item tax amount */ - $tax = ($orderItemTax - $orderItem->getTaxRefunded()); - $baseTax = ($baseOrderItemTax - $orderItem->getBaseTaxRefunded()); - $discountTaxCompensation = ($orderItem->getDiscountTaxCompensationInvoiced() - - $orderItem->getDiscountTaxCompensationRefunded()); - $baseDiscountTaxCompensation = ($orderItem->getBaseDiscountTaxCompensationInvoiced() - - $orderItem->getBaseDiscountTaxCompensationRefunded()); + $tax = $orderItemTax - $orderItem->getTaxRefunded(); + $baseTax = $baseOrderItemTax - $orderItem->getBaseTaxRefunded(); + $discountTaxCompensation = $orderItem->getDiscountTaxCompensationInvoiced() + - $orderItem->getDiscountTaxCompensationRefunded(); + $baseDiscountTaxCompensation = $orderItem->getBaseDiscountTaxCompensationInvoiced() + - $orderItem->getBaseDiscountTaxCompensationRefunded(); if (!$item->isLast()) { - $availableQty = ($orderItemQty - $orderItem->getQtyRefunded()); + $availableQty = $orderItemQty - $orderItem->getQtyRefunded(); $tax = $creditmemo->roundPrice($tax / $availableQty * $item->getQty()); $baseTax = $creditmemo->roundPrice(($baseTax / $availableQty * $item->getQty()), 'base'); $discountTaxCompensation = $creditmemo->roundPrice( @@ -76,10 +76,10 @@ public function collect(Creditmemo $creditmemo) if ($invoice = $creditmemo->getInvoice()) { // recalculate tax amounts in case if refund shipping value was changed if ($baseOrderShippingAmount && $creditmemo->getBaseShippingAmount() !== null) { - $taxFactor = ($creditmemo->getBaseShippingAmount() / $baseOrderShippingAmount); - $shippingTaxAmount = ($invoice->getShippingTaxAmount() * $taxFactor); - $baseShippingTaxAmount = ($invoice->getBaseShippingTaxAmount() * $taxFactor); - $totalDiscountTaxCompensation += ($invoice->getShippingDiscountTaxCompensationAmount() * $taxFactor); + $taxFactor = $creditmemo->getBaseShippingAmount() / $baseOrderShippingAmount; + $shippingTaxAmount = $invoice->getShippingTaxAmount() * $taxFactor; + $baseShippingTaxAmount = $invoice->getBaseShippingTaxAmount() * $taxFactor; + $totalDiscountTaxCompensation += $invoice->getShippingDiscountTaxCompensationAmount() * $taxFactor; $baseTotalDiscountTaxCompensation += $invoice->getBaseShippingDiscountTaxCompensationAmnt() * $taxFactor; $shippingTaxAmount = $creditmemo->roundPrice($shippingTaxAmount); @@ -102,10 +102,10 @@ public function collect(Creditmemo $creditmemo) $shippingDelta = ($baseOrderShippingAmount - $baseOrderShippingRefundedAmount); if ($shippingDelta > $creditmemo->getBaseShippingAmount()) { - $part = ($creditmemo->getShippingAmount() / $orderShippingAmount); - $basePart = ($creditmemo->getBaseShippingAmount() / $baseOrderShippingAmount); - $shippingTaxAmount = ($order->getShippingTaxAmount() * $part); - $baseShippingTaxAmount = ($order->getBaseShippingTaxAmount() * $basePart); + $part = $creditmemo->getShippingAmount() / $orderShippingAmount; + $basePart = $creditmemo->getBaseShippingAmount() / $baseOrderShippingAmount; + $shippingTaxAmount = $order->getShippingTaxAmount() * $part; + $baseShippingTaxAmount = $order->getBaseShippingTaxAmount() * $basePart; $shippingDiscountTaxCompensationAmount = $order->getShippingDiscountTaxCompensationAmount() * $part; $baseShippingDiscountTaxCompensationAmount = $order->getBaseShippingDiscountTaxCompensationAmnt() * $basePart; From 7d9d478b7c6b5dac52c7f4544b89612a0859d24b Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transogtgroup.com> Date: Fri, 18 Dec 2020 10:53:08 +0200 Subject: [PATCH 052/112] MC-39864: [Magento Cloud] - Tax Miscalculation --- app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php index 5fe4d0af7bd89..0d1382a48e065 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -99,7 +99,7 @@ public function collect(Creditmemo $creditmemo) $baseShippingTaxAmount = 0; $shippingDiscountTaxCompensationAmount = 0; $baseShippingDiscountTaxCompensationAmount = 0; - $shippingDelta = ($baseOrderShippingAmount - $baseOrderShippingRefundedAmount); + $shippingDelta = $baseOrderShippingAmount - $baseOrderShippingRefundedAmount; if ($shippingDelta > $creditmemo->getBaseShippingAmount()) { $part = $creditmemo->getShippingAmount() / $orderShippingAmount; From acac6b2f488a29c8d23f6361ef64d66c43beed42 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Thu, 10 Dec 2020 15:13:32 -0600 Subject: [PATCH 053/112] MC-39737: Reports: Review by Products - MFTF test and the solution --- .../reports_report_review_product_grid.xml | 4 +- ...inFilterProductReviewByNameActionGroup.xml | 20 +++++ .../Test/Mftf/Data/ProductReviewData.xml | 16 ++++ .../Section/AdminCreateNewReviewSection.xml | 5 ++ ...viewDateForReviewsByProductsReportTest.xml | 83 +++++++++++++++++++ 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterProductReviewByNameActionGroup.xml create mode 100644 app/code/Magento/Review/Test/Mftf/Test/AdminValidateLastReviewDateForReviewsByProductsReportTest.xml diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml index 26d0e8b13659d..b9d4572cd4868 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml @@ -90,8 +90,8 @@ <arguments> <argument name="header" xsi:type="string" translate="true">Last Review</argument> <argument name="type" xsi:type="string">datetime</argument> - <argument name="id" xsi:type="string">created_at</argument> - <argument name="index" xsi:type="string">created_at</argument> + <argument name="id" xsi:type="string">last_review</argument> + <argument name="index" xsi:type="string">last_review</argument> <argument name="column_css_class" xsi:type="string">col-date</argument> <argument name="header_css_class" xsi:type="string">col-date</argument> </arguments> diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterProductReviewByNameActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterProductReviewByNameActionGroup.xml new file mode 100644 index 0000000000000..b0544081980bb --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminFilterProductReviewByNameActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFilterProductReviewByNameActionGroup"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <waitForPageLoad stepKey="waitForGridToAppear"/> + <fillField userInput="{{productName}}" selector="{{AdminCreateNewReviewSection.filterProductName}}" stepKey="searchReview"/> + <click selector="{{AdminCreateNewReviewSection.searchButton}}" stepKey="startSearch"/> + <waitForPageLoad stepKey="waitForResults"/> + <see userInput="{{productName}}" selector="{{AdminCreateNewReviewSection.gridProductColumn}}" stepKey="assertReviewColumn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/Data/ProductReviewData.xml b/app/code/Magento/Review/Test/Mftf/Data/ProductReviewData.xml index f66decd1b7bd0..ba2102d0ee1f1 100644 --- a/app/code/Magento/Review/Test/Mftf/Data/ProductReviewData.xml +++ b/app/code/Magento/Review/Test/Mftf/Data/ProductReviewData.xml @@ -16,4 +16,20 @@ <item>Default Store View</item> </array> </entity> + <entity name="firstSimpleProductReview"> + <data key="nickname" unique="suffix">user1</data> + <data key="title">Review title 1</data> + <data key="detail">Simple product review 1</data> + <array key="select_stores"> + <item>Default Store View</item> + </array> + </entity> + <entity name="secondSimpleProductReview"> + <data key="nickname" unique="suffix">user2</data> + <data key="title">Review title 2</data> + <data key="detail">Simple product review 2</data> + <array key="select_stores"> + <item>Default Store View</item> + </array> + </entity> </entities> diff --git a/app/code/Magento/Review/Test/Mftf/Section/AdminCreateNewReviewSection.xml b/app/code/Magento/Review/Test/Mftf/Section/AdminCreateNewReviewSection.xml index 3b17b20e9da1b..28eb3112f259a 100644 --- a/app/code/Magento/Review/Test/Mftf/Section/AdminCreateNewReviewSection.xml +++ b/app/code/Magento/Review/Test/Mftf/Section/AdminCreateNewReviewSection.xml @@ -18,8 +18,13 @@ <element name="submitReview" type="button" selector="#save_button"/> <element name="SuccessMessage" type="button" selector="div.message-success"/> <element name="gridProducts_filter_review_cnt" type="button" selector="#gridProducts_filter_review_cnt"/> + <element name="filterProductName" type="button" selector="#gridProducts_filter_name"/> <element name="searchButton" type="button" selector="//*[@id='gridProducts']//button[contains(@title, 'Search')]"/> <element name="gridReviewColumn" type="text" selector="//tbody//td[@data-column='review_cnt']"/> + <element name="gridLastReviewColumn" type="text" selector="//tbody//td[@data-column='created_at']"/> + <element name="gridProductColumn" type="text" selector="//tbody//td[@data-column='name']"/> + <element name="showReviewsButton" type="text" selector="//tbody//td[@data-column='action']"/> + <element name="grabLatestUserReviewDate" type="text" selector="//table[@class='data-grid']//tbody//tr[position()=1]//td[position()=3]"/> <element name="gridCustomer_filter_review_cnt" type="button" selector="#customers_grid_filter_review_cnt"/> <element name="CustomerSearchButton" type="button" selector="//*[@id='customers_grid']//button[contains(@title, 'Search')]"/> </section> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminValidateLastReviewDateForReviewsByProductsReportTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminValidateLastReviewDateForReviewsByProductsReportTest.xml new file mode 100644 index 0000000000000..3405314f24f78 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminValidateLastReviewDateForReviewsByProductsReportTest.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminValidateLastReviewDateForReviewsByProductsReportTest"> + <annotations> + <features value="Review"/> + <stories value="Reports: Review by Products"/> + <title value="Admin Validate Last Review Date For Review by Products Reports"/> + <description value="Admin Validate Last Review Date For Review by Products Reports"/> + <severity value="MAJOR"/> + <useCaseId value="MC-39737"/> + <testCaseId value="MC-39838"/> + </annotations> + <before> + <!--Step1. Login as admin--> + <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/> + <!--Step2. Create product and Category--> + <createData stepKey="category" entity="SimpleSubCategory"/> + <createData stepKey="createProduct" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + </before> + <after> + <!--Step9. Delete newly created product reviews --> + <actionGroup ref="AdminOpenReviewsPageActionGroup" stepKey="openAllReviewsPage"/> + <actionGroup ref="AdminDeleteReviewsByUserNicknameActionGroup" stepKey="deleteFirstCustomerReview"> + <argument name="nickname" value="{{firstSimpleProductReview.nickname}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteReviewsByUserNicknameActionGroup" stepKey="deleteSecondCustomerReview"> + <argument name="nickname" value="{{secondSimpleProductReview.nickname}}"/> + </actionGroup> + <!--Step10. delete Category and Products --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <!--Step11. Admin Logout--> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Step3. Navigate to Marketing > User Content> All Review --> + <amOnPage url="{{AdminReviewsPage.url}}" stepKey="openReviewsPage"/> + <waitForPageLoad time="30" stepKey="waitForPageLoadCreatedReviewOne"/> + + <!--Step4. Add First and Second Review For Same Product--> + <actionGroup ref="AdminAddProductReviewActionGroup" stepKey="addFirstReview"> + <argument name="review" value="firstSimpleProductReview"/> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <amOnPage url="{{AdminReviewsPage.url}}" stepKey="openReviewsPageAgain"/> + <waitForPageLoad time="30" stepKey="waitForPageLoadCreatedReviewTwo"/> + <actionGroup ref="AdminAddProductReviewActionGroup" stepKey="addSecondReview"> + <argument name="review" value="secondSimpleProductReview"/> + <argument name="sku" value="$$createProduct.sku$$"/> + </actionGroup> + <!--Step5. Navigate to Reports > Reviews > By Products --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToReportsByProductsPage"> + <argument name="menuUiId" value="{{AdminMenuReports.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuReportsReviewsByProducts.dataUiId}}"/> + </actionGroup> + <!--Step6. Search product review by product name --> + <actionGroup ref="AdminFilterProductReviewByNameActionGroup" stepKey="navigateToReportsReview"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + <!--Step7. Click 'Show Reviews' to see review details--> + <grabTextFrom selector="{{AdminCreateNewReviewSection.gridLastReviewColumn}}" stepKey="grabLastReviewDate"/> + <click selector="{{AdminCreateNewReviewSection.showReviewsButton}}" stepKey="showReviewsPage"/> + <waitForPageLoad stepKey="waitForReviewListPageToLoad"/> + <!--Step8. Assert product last review date matches latest user review date--> + <fillField selector="{{AdminReviewGridSection.nickname}}" userInput="{{secondSimpleProductReview.nickname}}" stepKey="fillNickname"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForPageLoad stepKey="waitForGridViewPageToLoad"/> + <grabTextFrom selector="{{AdminCreateNewReviewSection.grabLatestUserReviewDate}}" stepKey="grabLatestUserReviewDate"/> + <assertEquals stepKey="assertReviewDate"> + <actualResult type="string">$grabLastReviewDate</actualResult> + <expectedResult type="string">$grabLatestUserReviewDate</expectedResult> + </assertEquals> + </test> +</tests> From 01eb68279d3f49adb66c14c351c5dd9576c39b6f Mon Sep 17 00:00:00 2001 From: Oleg Aleksin <olega@ven.com> Date: Fri, 18 Dec 2020 10:45:17 +0200 Subject: [PATCH 054/112] Fix issue with logging each cookie as separate context --- .../Magento/Framework/Stdlib/Cookie/PhpCookieManager.php | 5 ++++- .../Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/Cookie/PhpCookieManager.php b/lib/internal/Magento/Framework/Stdlib/Cookie/PhpCookieManager.php index a5fe6f6c61506..64f04c62bbf64 100644 --- a/lib/internal/Magento/Framework/Stdlib/Cookie/PhpCookieManager.php +++ b/lib/internal/Magento/Framework/Stdlib/Cookie/PhpCookieManager.php @@ -212,7 +212,10 @@ private function checkAbilityToSendCookie($name, $value) if ($numCookies > static::MAX_NUM_COOKIES) { $this->logger->warning( new Phrase('Unable to send the cookie. Maximum number of cookies would be exceeded.'), - array_merge($_COOKIE, ['user-agent' => $this->httpHeader->getHttpUserAgent()]) + [ + 'cookies' => $_COOKIE, + 'user-agent' => $this->httpHeader->getHttpUserAgent() + ] ); } diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php index e41cbdfe51638..87e10981c802c 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/Cookie/PhpCookieManagerTest.php @@ -565,7 +565,10 @@ public function testSetTooManyCookies() ->method('warning') ->with( new Phrase('Unable to send the cookie. Maximum number of cookies would be exceeded.'), - array_merge($_COOKIE, ['user-agent' => $userAgent]) + [ + 'cookies' => $_COOKIE, + 'user-agent' => $userAgent + ] ); $this->cookieManager->setPublicCookie( From a391a9fe2833030f7a25730c289cc7f6746b41fe Mon Sep 17 00:00:00 2001 From: SmVladyslav <vlatame.tsg@gmail.com> Date: Fri, 18 Dec 2020 17:49:56 +0200 Subject: [PATCH 055/112] MC-35725: Price range of a Bundle Product is displayed incorrectly in the case of options with tier price --- .../Pricing/Price/BundleSelectionFactory.php | 5 +- .../Bundle/Pricing/Price/FinalPriceTest.php | 72 +++++++++++++++++ ...dle_product_with_tier_price_selections.php | 81 +++++++++++++++++++ ...ct_with_tier_price_selections_rollback.php | 33 ++++++++ .../three_simple_products_with_tier_price.php | 68 ++++++++++++++++ ...mple_products_with_tier_price_rollback.php | 31 +++++++ 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/Pricing/Price/FinalPriceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_with_tier_price_selections.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_with_tier_price_selections_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/three_simple_products_with_tier_price.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/three_simple_products_with_tier_price_rollback.php diff --git a/app/code/Magento/Bundle/Pricing/Price/BundleSelectionFactory.php b/app/code/Magento/Bundle/Pricing/Price/BundleSelectionFactory.php index a28d721cc9a4e..52a024dc9fac3 100644 --- a/app/code/Magento/Bundle/Pricing/Price/BundleSelectionFactory.php +++ b/app/code/Magento/Bundle/Pricing/Price/BundleSelectionFactory.php @@ -52,9 +52,12 @@ public function create( $quantity, array $arguments = [] ) { + $quantity = $quantity ? (float)$quantity : 1.; + $selection->setQty($quantity); + $arguments['bundleProduct'] = $bundleProduct; $arguments['saleableItem'] = $selection; - $arguments['quantity'] = $quantity ? (float)$quantity : 1.; + $arguments['quantity'] = $quantity; return $this->objectManager->create(self::SELECTION_CLASS_DEFAULT, $arguments); } diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Pricing/Price/FinalPriceTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Pricing/Price/FinalPriceTest.php new file mode 100644 index 0000000000000..6b1ab58035568 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Pricing/Price/FinalPriceTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Pricing\Price; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * 'Final Price' model integration tests. + * + * @magentoDbIsolation disabled + */ +class FinalPriceTest extends TestCase +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + } + + /** + * Check minimal and maximal prices are calculated correctly for Bundle product selections with Tier Prices. + * + * @return void + * @magentoDataFixture Magento/Bundle/_files/bundle_product_with_tier_price_selections.php + */ + public function testGetPriceForBundleSelectionsWithTierPrices(): void + { + $priceModel = $this->getPriceModel('bundle_with_tier_price_selections'); + $this->assertEquals(15.0, $priceModel->getMinimalPrice()->getValue()); + $this->assertEquals(45.0, $priceModel->getMaximalPrice()->getValue()); + } + + /** + * Create and retrieve Price Model for provided Product SKU. + * + * @param string $productSku + * @return FinalPrice + */ + private function getPriceModel(string $productSku): FinalPrice + { + $bundleProduct = $this->productRepository->get($productSku); + + return $this->objectManager->create( + FinalPrice::class, + [ + 'saleableItem' => $bundleProduct, + 'quantity' => 0., + ] + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_with_tier_price_selections.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_with_tier_price_selections.php new file mode 100644 index 0000000000000..5ef6daa5d11da --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_with_tier_price_selections.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/three_simple_products_with_tier_price.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductInterfaceFactory $productFactory */ +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); +$defaultWebsiteId = $storeManager->getWebsite('base')->getId(); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle_with_tier_price_selections') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setSkuType(0) + ->setPriceView(0) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(null) + ->setWeightType(0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'select', + 'required' => 1, + ], +]; +$bundleSelectionsData = [ + [ + [ + 'sku' => 'simple_1', + 'selection_qty' => 3, + ], + [ + 'sku' => 'simple_2', + 'selection_qty' => 3, + ], + [ + 'sku' => 'simple_3', + 'selection_qty' => 3, + ], + ] +]; +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, $bundleSelectionsData); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_with_tier_price_selections_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_with_tier_price_selections_rollback.php new file mode 100644 index 0000000000000..e8d0ed513e839 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_with_tier_price_selections_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance() + ->requireDataFixture('Magento/Catalog/_files/three_simple_products_with_tier_price_rollback.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +try { + $product = $productRepository->get('bundle_with_tier_price_selections', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/three_simple_products_with_tier_price.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/three_simple_products_with_tier_price.php new file mode 100644 index 0000000000000..2b04935e46941 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/three_simple_products_with_tier_price.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Customer\Model\Group; +use Magento\Store\Api\Data\WebsiteInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributes */ +$tpExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); + +/** @var WebsiteInterface $adminWebsite */ +$adminWebsite = $objectManager->get(WebsiteRepositoryInterface::class)->get('admin'); +$tierPriceExtensionAttributes = $tpExtensionAttributesFactory->create() + ->setWebsiteId($adminWebsite->getId()) + ->setPercentageValue(50); + +$tierPrice = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::CUST_GROUP_ALL, + 'qty' => 2, + ], + ] +)->setExtensionAttributes($tierPriceExtensionAttributes); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productNumber = 1; +foreach ([10, 20, 30] as $price) { + /** @var $product Product */ + $product = $objectManager->create(Product::class); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product ' . $productNumber) + ->setSku('simple_' . $productNumber) + ->setPrice($price) + ->setWeight(1) + ->setTierPrices([$tierPrice]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); + + $productRepository->save($product); + $productNumber++; +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/three_simple_products_with_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/three_simple_products_with_tier_price_rollback.php new file mode 100644 index 0000000000000..9dbf7a38371b6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/three_simple_products_with_tier_price_rollback.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +foreach (['simple_1', 'simple_2', 'simple_3'] as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From ed57b0ffdd696649a6b9d91d8f7dec5c615b6d8d Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Fri, 18 Dec 2020 10:51:14 -0600 Subject: [PATCH 056/112] MC-38810: Product Export CSV not parsed correctly in excel due to text area attributes content - fixed export - fixed import - added tests for export and import --- .../Model/Export/ProductTest.php | 58 ++++++++++++++++++ .../Model/Import/ProductTest.php | 61 +++++++++++++++++++ ...import_with_json_and_markup_attributes.csv | 3 + .../DependenciesShowFrameworkCommandTest.php | 4 +- .../expected/framework-dependencies.csv | 2 +- lib/internal/Magento/Framework/File/Csv.php | 7 ++- .../Framework/Filesystem/Driver/File.php | 7 ++- .../Filesystem/Driver/StatefulFile.php | 2 +- .../Framework/Filesystem/DriverInterface.php | 2 +- .../Framework/Filesystem/File/Read.php | 2 +- .../Filesystem/File/ReadInterface.php | 2 +- 11 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_json_and_markup_attributes.csv diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index dd36f90757398..4d844a7c6f229 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -108,6 +108,64 @@ public function testExport(): void $this->assertEquals(1, $occurrencesCount); } + /** + * Verify successful export of the product with custom attributes containing json and markup + * + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDbIsolation enabled + * @dataProvider exportWithJsonAndMarkupTextAttributeDataProvider + * @param string $attributeData + * @param string $expectedResult + * @return void + */ + public function testExportWithJsonAndMarkupTextAttribute(string $attributeData, string $expectedResult): void + { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get('simple2'); + + /** @var \Magento\Eav\Model\Config $eavConfig */ + $eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); + $eavConfig->clear(); + $attribute = $eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'text_attribute'); + $attribute->setDefaultValue($attributeData); + /** @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface $productAttributeRepository */ + $productAttributeRepository = $objectManager->get(\Magento\Catalog\Api\ProductAttributeRepositoryInterface::class); + $productAttributeRepository->save($attribute); + $product->setCustomAttribute('text_attribute', $attribute->getDefaultValue()); + $productRepository->save($product); + + $this->model->setWriter( + $this->objectManager->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class + ) + ); + $exportData = $this->model->export(); + $this->assertStringContainsString('Simple Product2', $exportData); + $this->assertStringContainsString($expectedResult, $exportData); + } + + /** + * @return array + */ + public function exportWithJsonAndMarkupTextAttributeDataProvider(): array + { + return [ + 'json' => [ + '{"type": "basic", "unit": "inch", "sign": "(\")", "size": "1.5\""}', + '"text_attribute={""type"": ""basic"", ""unit"": ""inch"", ""sign"": ""(\"")"", ""size"": ""1.5\""""}"' + ], + 'markup' => [ + '<div data-content>Element type is basic, measured in inches ' . + '(marked with sign (\")) with size 1.5\", mid-price range</div>', + '"text_attribute=<div data-content>Element type is basic, measured in inches ' . + '(marked with sign (\"")) with size 1.5\"", mid-price range</div>"' + ], + ]; + } + /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data_special_chars.php * @magentoDbIsolation enabled diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 3ca6754c77767..82d75cdce29c7 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -2344,6 +2344,67 @@ public function testProductWithWrappedAdditionalAttributes() ); } + /** + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @dataProvider importWithJsonAndMarkupTextAttributeDataProvider + * @param string $productSku + * @param string $expectedResult + * @return void + */ + public function testImportWithJsonAndMarkupTextAttribute(string $productSku, string $expectedResult): void + { + // added by _files/product_import_with_json_and_markup_attributes.csv + $this->importedProducts = [ + 'SkuProductWithJson', + 'SkuProductWithMarkup', + ]; + + $importParameters =[ + 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product', + \Magento\ImportExport\Model\Import::FIELDS_ENCLOSURE => 0 + ]; + $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + \Magento\ImportExport\Model\Import\Source\Csv::class, + [ + 'file' => __DIR__ . '/_files/products_to_import_with_json_and_markup_attributes.csv', + 'directory' => $directory + ] + ); + $this->_model->setParameters($importParameters); + $this->_model->setSource($source); + $errors = $this->_model->validateData(); + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Api\ProductRepositoryInterface::class + ); + $product = $productRepository->get($productSku); + $this->assertEquals($expectedResult, $product->getData('text_attribute')); + } + + /** + * @return array + */ + public function importWithJsonAndMarkupTextAttributeDataProvider(): array + { + return [ + 'import of attribute with json' => [ + 'SkuProductWithJson', + '{"type": "basic", "unit": "inch", "sign": "(\")", "size": "1.5\""}' + ], + 'import of attribute with markup' => [ + 'SkuProductWithMarkup', + '<div data-content>Element type is basic, measured in inches ' . + '(marked with sign (\")) with size 1.5\", mid-price range</div>' + ], + ]; + } + /** * Import and check data from file. * diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_json_and_markup_attributes.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_json_and_markup_attributes.csv new file mode 100644 index 0000000000000..e8372c19c8ff2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_json_and_markup_attributes.csv @@ -0,0 +1,3 @@ +sku,product_type,name,price,attribute_set_code,categories,additional_attributes +SkuProductWithJson,simple,"Product With Json Attribute",100,Default,"Default Category/Category 1","text_attribute={""type"": ""basic"", ""unit"": ""inch"", ""sign"": ""(\"")"", ""size"": ""1.5\""""}" +SkuProductWithMarkup,simple,"Product With Markup Attribute",100,Default,"Default Category/Category 1","text_attribute=<div data-content>Element type is basic, measured in inches (marked with sign (\"")) with size 1.5\"", mid-price range</div>" diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/DependenciesShowFrameworkCommandTest.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/DependenciesShowFrameworkCommandTest.php index d07ec84b6eddc..b1c7843dfad32 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/DependenciesShowFrameworkCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/DependenciesShowFrameworkCommandTest.php @@ -70,11 +70,11 @@ public function testExecute() ); $this->assertStringContainsString('"Dependencies for each module:",' . PHP_EOL, $fileContents); $this->assertStringContainsString( - '"Magento\A",1' . PHP_EOL . '" -- Magento\Framework",2' . PHP_EOL, + 'Magento\A,1' . PHP_EOL . '" -- Magento\Framework",2' . PHP_EOL, $fileContents ); $this->assertStringContainsString( - '"Magento\B",1' . PHP_EOL . '" -- Magento\Framework",2' . PHP_EOL, + 'Magento\B,1' . PHP_EOL . '" -- Magento\Framework",2' . PHP_EOL, $fileContents ); } diff --git a/dev/tests/integration/testsuite/Magento/Setup/Module/Dependency/_files/expected/framework-dependencies.csv b/dev/tests/integration/testsuite/Magento/Setup/Module/Dependency/_files/expected/framework-dependencies.csv index e1c5732b94dcb..9f358f3fa7a25 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Module/Dependency/_files/expected/framework-dependencies.csv +++ b/dev/tests/integration/testsuite/Magento/Setup/Module/Dependency/_files/expected/framework-dependencies.csv @@ -2,7 +2,7 @@ ,3 "Dependencies for each module:", -"Magento\FirstModule",3 +Magento\FirstModule,3 " -- Magento\LibFirst",1 " -- Magento\LibSecond",2 " -- Magento\Third",1 diff --git a/lib/internal/Magento/Framework/File/Csv.php b/lib/internal/Magento/Framework/File/Csv.php index 1b1decdb5327c..e33b38562bf57 100644 --- a/lib/internal/Magento/Framework/File/Csv.php +++ b/lib/internal/Magento/Framework/File/Csv.php @@ -30,6 +30,11 @@ class Csv */ protected $_enclosure = '"'; + /** + * @var string + */ + private $escape = "\0"; + /** * @var File */ @@ -96,7 +101,7 @@ public function getData($file) } $fh = fopen($file, 'r'); - while ($rowData = fgetcsv($fh, $this->_lineLength, $this->_delimiter, $this->_enclosure)) { + while ($rowData = fgetcsv($fh, $this->_lineLength, $this->_delimiter, $this->_enclosure, $this->escape)) { $data[] = $rowData; } fclose($fh); diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/File.php b/lib/internal/Magento/Framework/Filesystem/Driver/File.php index bc08f67228849..7b508942c107d 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/File.php @@ -647,7 +647,7 @@ public function fileRead($resource, $length) * @return array|bool|null * @throws FileSystemException */ - public function fileGetCsv($resource, $length = 0, $delimiter = ',', $enclosure = '"', $escape = '\\') + public function fileGetCsv($resource, $length = 0, $delimiter = ',', $enclosure = '"', $escape = "\0") { $result = @fgetcsv($resource, $length, $delimiter, $enclosure, $escape); if ($result === null) { @@ -801,7 +801,10 @@ public function filePutCsv($resource, array $data, $delimiter = ',', $enclosure } } - $result = @fputcsv($resource, $data, $delimiter, $enclosure); + // Escape symbol is needed to fix known issue in PHP broken fputcsv escaping functionality + // where backslash followed by double quote breaks file consistency + $escape = "\0"; + $result = @fputcsv($resource, $data, $delimiter, $enclosure, $escape); if (!$result) { throw new FileSystemException( new Phrase( diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/StatefulFile.php b/lib/internal/Magento/Framework/Filesystem/Driver/StatefulFile.php index beeb1e928262c..9f69a38527ab3 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/StatefulFile.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/StatefulFile.php @@ -642,7 +642,7 @@ public function fileRead($resource, $length) * @return array|bool|null * @throws FileSystemException */ - public function fileGetCsv($resource, $length = 0, $delimiter = ',', $enclosure = '"', $escape = '\\') + public function fileGetCsv($resource, $length = 0, $delimiter = ',', $enclosure = '"', $escape = "\0") { $result = @fgetcsv($resource, $length, $delimiter, $enclosure, $escape); if ($result === null) { diff --git a/lib/internal/Magento/Framework/Filesystem/DriverInterface.php b/lib/internal/Magento/Framework/Filesystem/DriverInterface.php index afea4d3bc7b07..706077522c675 100644 --- a/lib/internal/Magento/Framework/Filesystem/DriverInterface.php +++ b/lib/internal/Magento/Framework/Filesystem/DriverInterface.php @@ -276,7 +276,7 @@ public function fileRead($resource, $length); * @return array|bool|null * @throws FileSystemException */ - public function fileGetCsv($resource, $length = 0, $delimiter = ',', $enclosure = '"', $escape = '\\'); + public function fileGetCsv($resource, $length = 0, $delimiter = ',', $enclosure = '"', $escape = "\0"); /** * Returns position of read/write pointer diff --git a/lib/internal/Magento/Framework/Filesystem/File/Read.php b/lib/internal/Magento/Framework/Filesystem/File/Read.php index f48e50b7d693d..41e2cd255dd5d 100644 --- a/lib/internal/Magento/Framework/Filesystem/File/Read.php +++ b/lib/internal/Magento/Framework/Filesystem/File/Read.php @@ -124,7 +124,7 @@ public function readLine($length, $ending = null) * @param string $escape [optional] * @return array|bool|null */ - public function readCsv($length = 0, $delimiter = ',', $enclosure = '"', $escape = '\\') + public function readCsv($length = 0, $delimiter = ',', $enclosure = '"', $escape = "\0") { return $this->driver->fileGetCsv($this->resource, $length, $delimiter, $enclosure, $escape); } diff --git a/lib/internal/Magento/Framework/Filesystem/File/ReadInterface.php b/lib/internal/Magento/Framework/Filesystem/File/ReadInterface.php index e27086709d8b1..4277e2b5693e7 100644 --- a/lib/internal/Magento/Framework/Filesystem/File/ReadInterface.php +++ b/lib/internal/Magento/Framework/Filesystem/File/ReadInterface.php @@ -46,7 +46,7 @@ public function readLine($length, $ending = null); * @param string $escape [optional] * @return array|bool false on end of file */ - public function readCsv($length = 0, $delimiter = ',', $enclosure = '"', $escape = '\\'); + public function readCsv($length = 0, $delimiter = ',', $enclosure = '"', $escape = "\0"); /** * Returns the current position From 876c8052f96d6770254277ff4dd39bce5e935d50 Mon Sep 17 00:00:00 2001 From: mastiuhin-olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Fri, 18 Dec 2020 22:26:18 +0200 Subject: [PATCH 057/112] MC-24495: Cannot save order with "file"-type address attribute. --- .../Model/Address/AbstractAddress.php | 7 +-- .../Customer/Model/Metadata/Form/File.php | 20 +++++++- .../Unit/Model/Metadata/Form/FileTest.php | 16 +++--- .../Adminhtml/Order/AddressSave.php | 50 ++++++++++++++++++- .../Magento/Sales/Model/AdminOrder/Create.php | 43 +++++++++++++++- 5 files changed, 119 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php index 8421fc92f8c4a..d1364dc0aeba6 100644 --- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php +++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php @@ -331,10 +331,11 @@ protected function _implodeArrayValues($value) return ''; } - $isScalar = false; + $isScalar = true; foreach ($value as $val) { - if (is_scalar($val)) { - $isScalar = true; + if (!is_scalar($val)) { + $isScalar = false; + break; } } if ($isScalar) { diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index 17cfc0325ef41..1add044c50c9e 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -100,6 +100,7 @@ public function __construct( FileProcessorFactory $fileProcessorFactory = null, IoFile $ioFile = null ) { + $value = $this->prepareFileValue($value); parent::__construct($localeDate, $logger, $attribute, $localeResolver, $value, $entityTypeCode, $isAjax); $this->urlEncoder = $urlEncoder; $this->_fileValidator = $fileValidator; @@ -302,11 +303,11 @@ public function validateValue($value) public function compactValue($value) { if ($this->getIsAjaxRequest()) { - return $this; + return ''; } // Remove outdated file (in the case of file uploader UI component) - if (empty($value) && !empty($this->_value)) { + if (!empty($this->_value) && !empty($value['delete'])) { $this->fileProcessor->removeUploadedFile($this->_value); return $value; } @@ -420,4 +421,19 @@ protected function getFileProcessor() { return $this->fileProcessor; } + + /** + * Prepare File value. + * + * @param array|string $value + * @return array|string + */ + private function prepareFileValue($value) + { + if (is_array($value) && isset($value['value'])) { + $value = $value['value']; + } + + return $value; + } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/FileTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/FileTest.php index 3c5016df230f9..b0e9805bb3d2a 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/FileTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/FileTest.php @@ -347,7 +347,7 @@ public function testCompactValueIsAjax() ] ); - $this->assertSame($model, $model->compactValue('aValue')); + $this->assertSame('', $model->compactValue('aValue')); } public function testCompactValueNoDelete() @@ -362,12 +362,12 @@ public function testCompactValueNoDelete() ] ); - $this->fileProcessorMock->expects($this->once()) + $this->fileProcessorMock->expects($this->any()) ->method('removeUploadedFile') ->with('value') ->willReturnSelf(); - $this->assertSame([], $model->compactValue([])); + $this->assertSame('value', $model->compactValue([])); } public function testCompactValueDelete() @@ -377,11 +377,11 @@ public function testCompactValueDelete() $mediaDirMock = $this->getMockForAbstractClass( \Magento\Framework\Filesystem\Directory\WriteInterface::class ); - $mediaDirMock->expects($this->once()) + $mediaDirMock->expects($this->any()) ->method('delete') ->with(self::ENTITY_TYPE . '/' . 'value'); - $this->fileSystemMock->expects($this->once()) + $this->fileSystemMock->expects($this->any()) ->method('getDirectoryWrite') ->with(DirectoryList::MEDIA) ->will($this->returnValue($mediaDirMock)); @@ -394,7 +394,7 @@ public function testCompactValueDelete() ] ); - $this->assertSame('', $model->compactValue(['delete' => true])); + $this->assertIsArray($model->compactValue(['delete' => true])); } public function testCompactValueTmpFile() @@ -589,12 +589,12 @@ public function testCompactValueRemoveUiComponentValue() ] ); - $this->fileProcessorMock->expects($this->once()) + $this->fileProcessorMock->expects($this->any()) ->method('removeUploadedFile') ->with($value) ->willReturnSelf(); - $this->assertEquals([], $model->compactValue([])); + $this->assertEquals($value, $model->compactValue([])); } public function testCompactValueNoAction() diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php index 5633e16d7d3d0..03ff35d17775e 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php @@ -27,6 +27,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Customer\Model\AttributeMetadataDataProvider; /** * Sales address save @@ -52,6 +53,11 @@ class AddressSave extends Order implements HttpPostActionInterface */ private $orderAddressRepository; + /** + * @var AttributeMetadataDataProvider + */ + private $attributeMetadataDataProvider; + /** * @param Context $context * @param Registry $coreRegistry @@ -82,7 +88,8 @@ public function __construct( OrderRepositoryInterface $orderRepository, LoggerInterface $logger, RegionFactory $regionFactory = null, - OrderAddressRepositoryInterface $orderAddressRepository = null + OrderAddressRepositoryInterface $orderAddressRepository = null, + AttributeMetadataDataProvider $attributeMetadataDataProvider = null ) { $this->regionFactory = $regionFactory ?: ObjectManager::getInstance()->get(RegionFactory::class); $this->orderAddressRepository = $orderAddressRepository ?: ObjectManager::getInstance() @@ -100,6 +107,8 @@ public function __construct( $orderRepository, $logger ); + $this->attributeMetadataDataProvider = $attributeMetadataDataProvider ?: ObjectManager::getInstance() + ->get(AttributeMetadataDataProvider::class); } /** @@ -115,6 +124,7 @@ public function execute() OrderAddressInterface::class )->load($addressId); $data = $this->getRequest()->getPostValue(); + $data = $this->truncateCustomFileAttributes($data); $data = $this->updateRegionData($data); $resultRedirect = $this->resultRedirectFactory->create(); if ($data && $address->getId()) { @@ -139,7 +149,7 @@ public function execute() return $resultRedirect->setPath('sales/*/'); } } - + /** * Update region data * @@ -155,4 +165,40 @@ private function updateRegionData($attributeValues) } return $attributeValues; } + + /** + * Truncates custom file attributes from a request. + * + * As custom file type attributes are not working workaround is introduced. + * + * @param array $data + * @return array + */ + private function truncateCustomFileAttributes(array $data): array + { + $foundedArrays = []; + + foreach ($data as $value) { + if (is_array($value)) { + $foundedArrays = $value; + } + } + + if (empty($foundedArrays)) { + return $data; + } + + $attributesList = $this->attributeMetadataDataProvider->loadAttributesCollection( + 'customer_address', + 'adminhtml_customer_address' + ); + $attributesList->addFieldToFilter('is_user_defined', 1); + $attributesList->addFieldToFilter('frontend_input', 'file'); + + foreach ($attributesList as $customFileAttribute) { + unset($data[$customFileAttribute->getAttributeCode()]); + } + + return $data; + } } diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 5d621f1632837..c8d3a506c2e67 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -7,10 +7,12 @@ namespace Magento\Sales\Model\AdminOrder; use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Model\Metadata\Form as CustomerForm; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\App\ObjectManager; use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\Quote\Address\CustomAttributeListInterface; use Magento\Quote\Model\Quote\Item; use Magento\Sales\Api\Data\OrderAddressInterface; use Magento\Sales\Model\Order; @@ -250,6 +252,11 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ */ private $storeManager; + /** + * @var CustomAttributeListInterface + */ + private $customAttributeList; + /** * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -282,6 +289,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @param ExtensibleDataObjectConverter|null $dataObjectConverter * @param StoreManagerInterface $storeManager + * @param CustomAttributeListInterface|null $customAttributeList * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -315,7 +323,8 @@ public function __construct( array $data = [], \Magento\Framework\Serialize\Serializer\Json $serializer = null, ExtensibleDataObjectConverter $dataObjectConverter = null, - StoreManagerInterface $storeManager = null + StoreManagerInterface $storeManager = null, + CustomAttributeListInterface $customAttributeList = null ) { $this->_objectManager = $objectManager; $this->_eventManager = $eventManager; @@ -350,6 +359,8 @@ public function __construct( $this->dataObjectConverter = $dataObjectConverter ?: ObjectManager::getInstance() ->get(ExtensibleDataObjectConverter::class); $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); + $this->customAttributeList = $customAttributeList ?: ObjectManager::getInstance() + ->get(CustomAttributeListInterface::class); } /** @@ -1530,7 +1541,8 @@ public function setBillingAddress($address) $billingAddress->setData('save_in_address_book', $saveInAddressBook); $quote = $this->getQuote(); - if (!$quote->isVirtual() && $this->getShippingAddress()->getSameAsBilling()) { + $shippingAddress = $this->getShippingAddress(); + if (!$quote->isVirtual() && $shippingAddress->getSameAsBilling()) { $address['save_in_address_book'] = 0; $this->setShippingAddress($address); } @@ -1543,9 +1555,36 @@ public function setBillingAddress($address) } $quote->setBillingAddress($billingAddress); + if ($shippingAddress->getSameAsBilling()) { + $this->synchronizeAddressesFileAttributes(); + } + return $this; } + /** + * Synchronizes addresses file attributes. + * + * @return void + */ + private function synchronizeAddressesFileAttributes(): void + { + $billingAddress = $this->getBillingAddress(); + $shippingAddress = $this->getShippingAddress(); + + /** @var AttributeMetadataInterface[] $customAttributes */ + $customAttributes = $this->customAttributeList->getAttributes(); + foreach ($customAttributes as $attribute) { + $attributeCode = $attribute->getAttributeCode(); + if ($attribute->getFrontendInput() === 'file' && + !empty($billingAddress->getData($attributeCode)) && + empty($shippingAddress->getData($attributeCode)) + ) { + $shippingAddress->setData($attributeCode, $billingAddress->getData($attributeCode)); + } + } + } + /** * Set shipping method * From 5d69b324e160493f50dfb66e04721be1a97abc62 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Fri, 18 Dec 2020 16:32:25 -0600 Subject: [PATCH 058/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php index e6ed14ae2685e..e3f8cadb5f94a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php @@ -98,7 +98,7 @@ public function testUrlRewriteCleansCacheOnChange(string $requestPath) $urlRewrite->setRequestPath('test' . $requestPath); $urlRewriteResourceModel->save($urlRewrite); $apiResponse = $this->graphQlQuery($query($requestPath)); - $this->assertNull($apiResponse['urlResolver']['relative_url']); + $this->assertNull($apiResponse); // rolling back changes $urlRewrite->setRequestPath($requestPath); @@ -197,7 +197,7 @@ public function testUrlRewriteCleansCacheForCustomRewrites() // delete custom rewrite and validate that API will not return cached response $urlRewriteResourceModel->delete($urlRewrite); $apiResponse = $this->graphQlQuery($query($customRequestPath)); - $this->assertNull($apiResponse['urlResolver']['relative_url']); + $this->assertNull($apiResponse); } /** From e24dcb909b78bda488e85ab7cbe78439207cc058 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Fri, 18 Dec 2020 22:11:45 -0600 Subject: [PATCH 059/112] MC-39477: Cart Price rule not working where condition has Category "IS NOT" - Testing operator and the test --- app/code/Magento/Rule/Model/Condition/AbstractCondition.php | 2 +- .../Test/Unit/Model/Condition/AbstractConditionTest.php | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php index 67fc3590ac501..d5d3b80755360 100644 --- a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php +++ b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php @@ -838,7 +838,7 @@ public function validateAttribute($validatedValue) } } } elseif (is_array($value)) { - if (!is_array($validatedValue)) { + if (!is_array($validatedValue) || empty($validatedValue)) { return false; } $result = array_intersect($value, $validatedValue); diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php index 3c55eacaff559..91f1c4b942f75 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php @@ -91,6 +91,10 @@ public function validateAttributeDataProvider() [1, '>=', '1', true], [1, '>=', 0, false], [0, '<', [1], false], + + [[1], '!{}', [], false], + [[1], '!{}', [1], false], + [[1], '!{}', [0], false], ]; } @@ -176,6 +180,8 @@ public function validateAttributeArrayInputTypeDataProvider() [[3], '{}', [], false, 'grid'], [1, '{}', 1, false, 'grid'], [1, '!{}', [1, 2, 3], false, 'grid'], + [1, '!{}', [], false, 'grid'], + [[1], '!{}', [], false, 'grid'], [[1], '{}', null, false, 'grid'], [null, '{}', null, true, 'input'], [null, '!{}', null, false, 'input'], From 9bb960fdda8d541073a45b8009206836ec4f0f31 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Sat, 19 Dec 2020 08:57:07 -0600 Subject: [PATCH 060/112] MC-39463: GraphQL caches urlResolver response and can return the old value after the url rewrite update --- .../testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php index e3f8cadb5f94a..4a4b617d7c28e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php @@ -98,7 +98,7 @@ public function testUrlRewriteCleansCacheOnChange(string $requestPath) $urlRewrite->setRequestPath('test' . $requestPath); $urlRewriteResourceModel->save($urlRewrite); $apiResponse = $this->graphQlQuery($query($requestPath)); - $this->assertNull($apiResponse); + $this->assertNull($apiResponse['urlResolver']); // rolling back changes $urlRewrite->setRequestPath($requestPath); @@ -197,7 +197,7 @@ public function testUrlRewriteCleansCacheForCustomRewrites() // delete custom rewrite and validate that API will not return cached response $urlRewriteResourceModel->delete($urlRewrite); $apiResponse = $this->graphQlQuery($query($customRequestPath)); - $this->assertNull($apiResponse); + $this->assertNull($apiResponse['urlResolver']); } /** From d2745c59c545dff90a371528dab648b1df021dce Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Sat, 19 Dec 2020 21:45:23 -0600 Subject: [PATCH 061/112] MC-39763: Ratings data not rendering on review detail page - Adding review id with rating --- app/code/Magento/Review/Block/View.php | 3 +- .../Review/view/frontend/templates/view.phtml | 24 ++- .../Magento/Review/Block/ViewTest.php | 143 ++++++++++++++++++ .../_files/product_review_with_rating.php | 86 +++++++++++ .../product_review_with_rating_rollback.php | 11 ++ 5 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Review/Block/ViewTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Review/_files/product_review_with_rating.php create mode 100644 dev/tests/integration/testsuite/Magento/Review/_files/product_review_with_rating_rollback.php diff --git a/app/code/Magento/Review/Block/View.php b/app/code/Magento/Review/Block/View.php index fcfa11faa169d..bbdd246835f9e 100644 --- a/app/code/Magento/Review/Block/View.php +++ b/app/code/Magento/Review/Block/View.php @@ -103,9 +103,10 @@ public function getBackUrl() */ public function getRating() { + $reviewId = $this->getReviewId() ?: $this->getReviewData()->getId(); if (!$this->getRatingCollection()) { $ratingCollection = $this->_voteFactory->create()->getResourceCollection()->setReviewFilter( - $this->getReviewId() + $reviewId )->setStoreFilter( $this->_storeManager->getStore()->getId() )->addRatingInfo( diff --git a/app/code/Magento/Review/view/frontend/templates/view.phtml b/app/code/Magento/Review/view/frontend/templates/view.phtml index b51353b7df685..3fd2fc85f8853 100644 --- a/app/code/Magento/Review/view/frontend/templates/view.phtml +++ b/app/code/Magento/Review/view/frontend/templates/view.phtml @@ -33,16 +33,26 @@ <caption class="table-caption"><?= $block->escapeHtml(__('Product Rating')) ?></caption> <?php foreach ($block->getRating() as $_rating): ?> <?php if ($_rating->getPercent()): ?> + <?php $rating = ceil($_rating->getPercent()) ?> <tr> - <td class="label"><?= $block->escapeHtml(__($_rating->getRatingCode())) ?></td> + <td class="label" width="10%"> + <?= $block->escapeHtml(__($_rating->getRatingCode())) ?> + </td> <td class="value"> - <div class="rating-box"> - <div class="rating"/> + <?php $ratingId = $_rating->getRatingId() ?> + <div class="rating-summary item" + id="rating-div-<?= $block->escapeHtml($ratingId) ?>"> + <div class="rating-result" title="<?= /* @noEscape */ $rating ?>%"> + <span> + <span><?= /* @noEscape */ $rating ?>%</span> + </span> + </div> + <?= /* @noEscape */ $secureRenderer->renderStyleAsTag( + "width:" . /* @noEscape */ $rating . "%", + 'div#rating-div-'.$_rating->getRatingId(). + '>div.rating-result>span:first-child' + ) ?> </div> - <?= /* @noEscape */ $secureRenderer->renderStyleAsTag( - "width:" . /* @noEscape */ ceil($_rating->getPercent()) . "%;", - 'div.rating-box div.rating' - ) ?> </td> </tr> <?php endif; ?> diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/ViewTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/ViewTest.php new file mode 100644 index 0000000000000..74bb31d7cb48d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/Block/ViewTest.php @@ -0,0 +1,143 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Block; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Customer\Model\Session; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\Review\Model\ResourceModel\Review\Product\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Test for displaying product review block. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class ViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Session */ + private $customerSession; + + /** @var CollectionFactory */ + private $collectionFactory; + + /** @var Registry */ + private $registry; + + /** @var View */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerSession = $this->objectManager->get(Session::class); + $this->collectionFactory = $this->objectManager->get(CollectionFactory::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(View::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->registry->unregister('current_review'); + $this->registry->unregister('current_product'); + $this->registry->unregister('product'); + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * Test product review block + * + * @magentoDataFixture Magento/Review/_files/product_review_with_rating.php + * + * @return void + * @throws NoSuchEntityException + */ + public function testProductReviewBlock(): void + { + $this->customerSession->setCustomerId(1); + $review = $this->collectionFactory->create()->addCustomerFilter(1)->getFirstItem(); + $this->registerReview($review); + $this->assertNotNull($review->getReviewId()); + + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + /** @var ProductInterface $product */ + $product = $productRepository->get('simple', false, null, true); + $this->registerProduct($product); + + $blockHtml = $this->block->setReviewId($review->getReviewId())->toHtml(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//div[contains(@class, 'details')]/h3[contains(text(), '%s')]", $review->getName()), + $blockHtml + ), + 'Product name wasn\'t found.' + ); + $ratings = $this->block->getRating(); + $this->assertCount(2, $ratings); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + "//a[contains(@class, 'action back')]/span[contains(text(), '%s')]", + __('Back to Product Reviews') + ), + $blockHtml + ), + sprintf('%s button wasn\'t found.', __('Back to Product Reviews')) + ); + } + + /** + * Register the product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('current_product'); + $this->registry->unregister('product'); + $this->registry->register('current_product', $product); + $this->registry->register('product', $product); + } + + /** + * Register the current review + * + * @param Product $review + * @return void + */ + private function registerReview(Product $review): void + { + $this->registry->unregister('current_review'); + $this->registry->register('current_review', $review); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Review/_files/product_review_with_rating.php b/dev/tests/integration/testsuite/Magento/Review/_files/product_review_with_rating.php new file mode 100644 index 0000000000000..47a402ae44fc4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/_files/product_review_with_rating.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Backend\App\Area\FrontNameResolver; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Registry; +use Magento\Review\Model\Rating; +use Magento\Review\Model\Rating\Option; +use Magento\Review\Model\ResourceModel\Review\Collection; +use Magento\Review\Model\Review; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Bootstrap::getInstance()->loadArea( + FrontNameResolver::AREA_CODE +); +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer.php'); +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var CustomerRegistry $customerRegistry */ +$customerRegistry = $objectManager->create(CustomerRegistry::class); +$customer = $customerRegistry->retrieve(1); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$product = $productRepository->get('simple'); +$storeId = $objectManager->get( + StoreManagerInterface::class +)->getStore()->getId(); + +$review = $objectManager->create( + Review::class, + ['data' => [ + 'customer_id' => $customer->getId(), + 'title' => 'Review Summary', + 'detail' => 'Review text', + 'nickname' => 'Nickname', + ]] +); + +$review + ->setEntityId($review->getEntityIdByCode(Review::ENTITY_PRODUCT_CODE)) + ->setEntityPkValue($product->getId()) + ->setStatusId(Review::STATUS_APPROVED) + ->setStoreId($storeId) + ->setStores([$storeId]) + ->save(); + +$objectManager->get(Registry::class)->register( + 'review_data', + $review +); + +/** @var Collection $ratingCollection */ +$ratingCollection = $objectManager->create( + Rating::class +)->getCollection() + ->setPageSize(2) + ->setCurPage(1); + +foreach ($ratingCollection as $rating) { + $rating->setStores([$storeId])->setIsActive(1)->save(); +} + +foreach ($ratingCollection as $rating) { + $ratingOption = $objectManager + ->create(Option::class) + ->getCollection() + ->setPageSize(1) + ->setCurPage(2) + ->addRatingFilter($rating->getId()) + ->getFirstItem(); + $rating->setReviewId($review->getId()) + ->addOptionVote($ratingOption->getId(), $product->getId()); +} + +$objectManager->get(Registry::class)->register( + 'rating_data', + $ratingCollection->getFirstItem() +); diff --git a/dev/tests/integration/testsuite/Magento/Review/_files/product_review_with_rating_rollback.php b/dev/tests/integration/testsuite/Magento/Review/_files/product_review_with_rating_rollback.php new file mode 100644 index 0000000000000..0931d881a6fdc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/_files/product_review_with_rating_rollback.php @@ -0,0 +1,11 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php'); +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_rollback.php'); From 2a712558d59eac9d4474c3e62142976b5c53dab1 Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Mon, 21 Dec 2020 12:02:19 +0200 Subject: [PATCH 062/112] refactored AdminSortProductsGridByActionGroup --- ...ngeWebSiteAssignedToProductActionGroup.xml | 21 ++++++++++ .../AdminSortProductsGridByActionGroup.xml | 22 +++++++++++ .../Mftf/Test/AdminSortingByWebsitesTest.xml | 39 +++++++++---------- 3 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminChangeWebSiteAssignedToProductActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSortProductsGridByActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminChangeWebSiteAssignedToProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminChangeWebSiteAssignedToProductActionGroup.xml new file mode 100644 index 0000000000000..76a0af8f63fd5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminChangeWebSiteAssignedToProductActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminChangeWebSiteAssignedToProductActionGroup" extends="AddWebsiteToProductActionGroup"> + <annotations> + <description>Extends AddWebsiteToProductActionGroup. Changes website assigned to product from websiteToDeselect to website</description> + </annotations> + <arguments> + <argument name="websiteToDeselect" type="string"/> + </arguments> + + <uncheckOption selector="{{ProductInWebsitesSection.website(websiteToDeselect)}}" stepKey="uncheckWebsite" after="checkWebsite"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSortProductsGridByActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSortProductsGridByActionGroup.xml new file mode 100644 index 0000000000000..92c714c2478b0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSortProductsGridByActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSortProductsGridByActionGroup"> + <annotations> + <description>Sorts the Product Grid by field</description> + </annotations> + <arguments> + <argument name="field" type="string"/> + </arguments> + + <click selector="{{AdminProductGridSection.columnHeader(field)}}" stepKey="clickWebsitesHeaderToSort"/> + <waitForLoadingMaskToDisappear stepKey="waitForApplyingChanges"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml index 73aeed3af4fb0..76843b3a40fcb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -25,7 +25,7 @@ </createData> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - <!--Create new website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> <argument name="websiteCode" value="{{customWebsite.code}}"/> @@ -54,31 +54,30 @@ <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> - <!--Assign Custom Website to Simple Product --> - <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToCatalogProductGrid"/> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="navigateToCatalogProductGrid"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickClearFiltersInitial"/> <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="assignCustomWebsiteToProduct"> <argument name="product" value="$$productAssignedToCustomWebsite$$"/> </actionGroup> - <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> - <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{AdminProductContentSection.sectionHeaderShow}}" visible="false" stepKey="expandSection"/> - <waitForPageLoad stepKey="waitForPageOpened"/> - <uncheckOption selector="{{ProductInWebsitesSection.website(_defaultWebsite.name)}}" stepKey="deselectMainWebsite"/> - <checkOption selector="{{ProductInWebsitesSection.website(customWebsite.name)}}" stepKey="selectWebsite"/> - - <actionGroup ref="AdminProductFormSaveActionGroup" stepKey="clickSave"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageAgain"/> + <actionGroup ref="AdminChangeWebSiteAssignedToProductActionGroup" stepKey="scrollToWebsites"> + <argument name="website" value="{{customWebsite.name}}"/> + <argument name="websiteToDeselect" value="{{_defaultWebsite.name}}"/> + </actionGroup> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="expandSection"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitForPageOpened"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="deselectMainWebsite"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="selectWebsite"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="clickSave"/> + <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="seeSaveProductMessageAgain"/> - <!--Navigate To Product Grid To Check Website Sorting--> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToCatalogProductGridToSortByWebsite"/> - - <!--Sorting works (By Websites) ASC--> - <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortAsc"/> + <actionGroup ref="AdminSortProductsGridByActionGroup" stepKey="clickWebsitesHeaderToSortAsc"> + <argument name="field" value="Websites"/> + </actionGroup> <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Main Website" stepKey="checkIfProduct1WebsitesAsc"/> - - <!--Sorting works (By Websites) DESC--> - <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortDesc"/> + <actionGroup ref="AdminSortProductsGridByActionGroup" stepKey="clickWebsitesHeaderToSortDesc"> + <argument name="field" value="Websites"/> + </actionGroup> <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="{{customWebsite.name}}" stepKey="checkIfProduct1WebsitesDesc"/> </test> </tests> From 38fc6c6e907f1bc574e8d8365195b8a04ab3d1b8 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transogtgroup.com> Date: Mon, 21 Dec 2020 12:21:08 +0200 Subject: [PATCH 063/112] MC-39864: [Magento Cloud] - Tax Miscalculation --- .../Model/Order/Creditmemo/Total/Tax.php | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php index 0d1382a48e065..eba84dcfb6364 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -182,9 +182,10 @@ private function calculateAllowedTax(Creditmemo $creditMemo): float { $invoice = $creditMemo->getInvoice(); $order = $creditMemo->getOrder(); - $amount = $invoice !== null ? $invoice->getTaxAmount() : $order->getTaxInvoiced(); + $amount = $invoice !== null ? $invoice->getTaxAmount() + : $order->getTaxInvoiced() - $order->getTaxRefunded(); - return (float) $amount - $order->getTaxRefunded() - $creditMemo->getTaxAmount(); + return (float) $amount - $creditMemo->getTaxAmount(); } /** @@ -197,9 +198,10 @@ private function calculateAllowedBaseTax(Creditmemo $creditMemo): float { $invoice = $creditMemo->getInvoice(); $order = $creditMemo->getOrder(); - $amount = $invoice !== null ? $invoice->getBaseTaxAmount() : $order->getBaseTaxInvoiced(); + $amount = $invoice !== null ? $invoice->getBaseTaxAmount() + : $order->getBaseTaxInvoiced() - $order->getBaseTaxRefunded(); - return (float) $amount - $order->getBaseTaxRefunded() - $creditMemo->getBaseTaxAmount(); + return (float) $amount - $creditMemo->getBaseTaxAmount(); } /** @@ -218,12 +220,12 @@ private function calculateAllowedDiscountTaxCompensation(Creditmemo $creditMemo) + $invoice->getShippingDiscountTaxCompensationAmount(); } else { $amount = $order->getDiscountTaxCompensationInvoiced() - + $order->getShippingDiscountTaxCompensationAmount(); + + $order->getShippingDiscountTaxCompensationAmount() + - $order->getDiscountTaxCompensationRefunded() + - $order->getShippingDiscountTaxCompensationRefunded(); } return (float) $amount - - $order->getDiscountTaxCompensationRefunded() - - $order->getShippingDiscountTaxCompensationRefunded() - $creditMemo->getDiscountTaxCompensationAmount() - $creditMemo->getShippingDiscountTaxCompensationAmount(); } @@ -244,12 +246,12 @@ private function calculateAllowedBaseDiscountTaxCompensation(Creditmemo $creditM + $invoice->getBaseShippingDiscountTaxCompensationAmnt(); } else { $amount = $order->getBaseDiscountTaxCompensationInvoiced() - + $order->getBaseShippingDiscountTaxCompensationAmnt(); + + $order->getBaseShippingDiscountTaxCompensationAmnt() + - $order->getBaseDiscountTaxCompensationRefunded() + - $order->getBaseShippingDiscountTaxCompensationRefunded(); } return (float) $amount - - $order->getBaseDiscountTaxCompensationRefunded() - - $order->getBaseShippingDiscountTaxCompensationRefunded() - $creditMemo->getBaseShippingDiscountTaxCompensationAmnt() - $creditMemo->getBaseDiscountTaxCompensationAmount(); } From e45a9447c469ca49489aa6314301140132571c43 Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@transoftgroup.com> Date: Mon, 21 Dec 2020 12:25:56 +0200 Subject: [PATCH 064/112] MC-39718: Process Manager always exits successfully if the amount of functions(i.e. indexer dimensions) is lower than the MAGE_INDEXER_THREADS_COUNT env variable --- .../Magento/Indexer/Model/ProcessManager.php | 5 +- .../Test/Unit/Model/ProcessManagerTest.php | 183 ++++++++++++++++++ 2 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Indexer/Test/Unit/Model/ProcessManagerTest.php diff --git a/app/code/Magento/Indexer/Model/ProcessManager.php b/app/code/Magento/Indexer/Model/ProcessManager.php index 2b25c8c6a3d15..b6fd158364dea 100644 --- a/app/code/Magento/Indexer/Model/ProcessManager.php +++ b/app/code/Magento/Indexer/Model/ProcessManager.php @@ -111,9 +111,12 @@ private function multiThreadsExecute($userFunctions) $this->startChildProcess($userFunction); } } - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock,Magento2.Functions.DiscouragedFunction + // phpcs:ignore Magento2.Functions.DiscouragedFunction while (pcntl_waitpid(0, $status) != -1) { //Waiting for the completion of child processes + if ($status > 0) { + $this->failInChildProcess = true; + } } if ($this->failInChildProcess) { diff --git a/app/code/Magento/Indexer/Test/Unit/Model/ProcessManagerTest.php b/app/code/Magento/Indexer/Test/Unit/Model/ProcessManagerTest.php new file mode 100644 index 0000000000000..9e9d2a5c81aba --- /dev/null +++ b/app/code/Magento/Indexer/Test/Unit/Model/ProcessManagerTest.php @@ -0,0 +1,183 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Indexer\Test\Unit\Model; + +use Magento\Framework\App\ResourceConnection; +use Magento\Indexer\Model\ProcessManager; +use PHPUnit\Framework\TestCase; + +/** + * Class covers process manager execution test logic + */ +class ProcessManagerTest extends TestCase +{ + /** + * @dataProvider functionsWithErrorProvider + * @param array $userFunctions + * @param int $threadsCount + * @return void + */ + public function testFailureInChildProcessHandleMultiThread(array $userFunctions, int $threadsCount): void + { + $connectionMock = $this->createMock(ResourceConnection::class); + $processManager = new ProcessManager( + $connectionMock, + null, + $threadsCount + ); + + try { + $processManager->execute($userFunctions); + $this->fail('Exception was not handled'); + } catch (\RuntimeException $exception) { + $this->assertEquals('Fail in child process', $exception->getMessage()); + } + } + + /** + * Closure functions data provider for multi thread execution + * + * @return array + * @SuppressWarnings(PHPMD.ExitExpression) + */ + public function functionsWithErrorProvider(): array + { + return [ + 'more_threads_than_functions' => [ + 'user_functions' => [ + // @codingStandardsIgnoreStart + function () { + exit(1); + }, + function () { + exit(0); + }, + function () { + exit(0); + }, + // @codingStandardsIgnoreEnd + ], + 'threads_count' => 4, + ], + 'less_threads_than_functions' => [ + 'user_functions' => [ + // @codingStandardsIgnoreStart + function () { + exit(1); + }, + function () { + exit(0); + }, + function () { + exit(0); + }, + // @codingStandardsIgnoreEnd + ], + 'threads_count' => 2, + ], + 'equal_threads_and_functions' => [ + 'user_functions' => [ + // @codingStandardsIgnoreStart + function () { + exit(1); + }, + function () { + exit(0); + }, + function () { + exit(0); + }, + // @codingStandardsIgnoreEnd + ], + 'threads_count' => 3, + ], + ]; + } + + /** + * @dataProvider successFunctionsProvider + * @param array $userFunctions + * @param int $threadsCount + * @return void + */ + public function testSuccessChildProcessHandleMultiThread(array $userFunctions, int $threadsCount): void + { + $connectionMock = $this->createMock(ResourceConnection::class); + $processManager = new ProcessManager( + $connectionMock, + null, + $threadsCount + ); + + try { + $processManager->execute($userFunctions); + } catch (\RuntimeException $exception) { + $this->fail('Exception was not handled'); + } + } + + /** + * Closure functions data provider for multi thread execution + * + * @return array + * @SuppressWarnings(PHPMD.ExitExpression) + */ + public function successFunctionsProvider(): array + { + return [ + 'more_threads_than_functions' => [ + 'user_functions' => [ + // @codingStandardsIgnoreStart + function () { + exit(0); + }, + function () { + exit(0); + }, + function () { + exit(0); + }, + // @codingStandardsIgnoreEnd + ], + 'threads_count' => 4, + ], + 'less_threads_than_functions' => [ + 'user_functions' => [ + // @codingStandardsIgnoreStart + function () { + exit(0); + }, + function () { + exit(0); + }, + function () { + exit(0); + }, + // @codingStandardsIgnoreEnd + ], + 'threads_count' => 2, + ], + 'equal_threads_and_functions' => [ + 'user_functions' => [ + // @codingStandardsIgnoreStart + function () { + exit(0); + }, + function () { + exit(0); + }, + function () { + exit(0); + }, + // @codingStandardsIgnoreEnd + ], + 'threads_count' => 3, + ], + ]; + } +} From d12f17198aec95cce37442933fc24264e6f540e8 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 21 Dec 2020 14:19:11 +0200 Subject: [PATCH 065/112] MC-39037: Report tabs are not aligned and not able to view properly --- .../view/adminhtml/templates/widget/grid/extended.phtml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml index d4aa14250837f..4bdee469e2fa4 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml @@ -14,12 +14,13 @@ * getPagerVisibility() * getVarNamePage() */ -$numColumns = count($block->getColumns()); /** * @var \Magento\Backend\Block\Widget\Grid\Extended $block * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */ +$numColumns = count($block->getColumns()); + ?> <?php if ($block->getCollection()): ?> <?php if ($block->canDisplayContainer()): ?> @@ -285,7 +286,9 @@ $numColumns = count($block->getColumns()); </table> </div> + <?php if ($block->canDisplayContainer()): ?> </div> + <?php endif; ?> <?php /** @var \Magento\Framework\Json\Helper\Data $jsonHelper */ $jsonHelper = $block->getData('jsonHelper'); From a2764054a2e661d5c052610d6945d9963ca63d5e Mon Sep 17 00:00:00 2001 From: mastiuhin-olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 21 Dec 2020 18:10:46 +0200 Subject: [PATCH 066/112] MC-24495: Cannot save order with "file"-type address attribute. --- .../Sales/Controller/Adminhtml/Order/AddressSave.php | 6 +++--- app/code/Magento/Sales/Model/AdminOrder/Create.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php index 03ff35d17775e..879baa948e919 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php @@ -176,15 +176,15 @@ private function updateRegionData($attributeValues) */ private function truncateCustomFileAttributes(array $data): array { - $foundedArrays = []; + $foundArrays = []; foreach ($data as $value) { if (is_array($value)) { - $foundedArrays = $value; + $foundArrays = $value; } } - if (empty($foundedArrays)) { + if (empty($foundArrays)) { return $data; } diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index c8d3a506c2e67..6bd6e8e4e83e1 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -1576,9 +1576,9 @@ private function synchronizeAddressesFileAttributes(): void $customAttributes = $this->customAttributeList->getAttributes(); foreach ($customAttributes as $attribute) { $attributeCode = $attribute->getAttributeCode(); - if ($attribute->getFrontendInput() === 'file' && - !empty($billingAddress->getData($attributeCode)) && - empty($shippingAddress->getData($attributeCode)) + if ($attribute->getFrontendInput() === 'file' + && !empty($billingAddress->getData($attributeCode)) + && empty($shippingAddress->getData($attributeCode)) ) { $shippingAddress->setData($attributeCode, $billingAddress->getData($attributeCode)); } From 7b2440af23763655f6a9cd88fef8dde56f61d8a6 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Tue, 22 Dec 2020 05:13:34 -0600 Subject: [PATCH 067/112] MC-39617: Category Selector limit category upto 5 from the root - Adding JS code to process the tree --- .../templates/catalog/category/widget/tree.phtml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml index 6c92ddcf36243..cf64c57c720b7 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml @@ -77,6 +77,16 @@ $scriptString .= <<<script dataUrl: '{$block->escapeJs($block->escapeUrl($block->getLoadTreeUrl()))}' }); + categoryLoader.processResponse = function (response, parent, callback) { + var config = JSON.parse(response.responseText); + + this.buildCategoryTree(parent, config); + + if (typeof callback == "function") { + callback(this, parent); + } + }; + categoryLoader.buildCategoryTree = function(parent, config) { if (!config) return null; @@ -164,8 +174,10 @@ $scriptString .= <<<script }; categoryLoader.on("beforeload", function(treeLoader, node) { - $('{$block->escapeJs($_divId)}').fire('category:beforeLoad', {treeLoader:treeLoader}); treeLoader.baseParams.id = node.attributes.id; + treeLoader.baseParams.store = node.attributes.store; + treeLoader.baseParams.form_key = FORM_KEY; + $('{$block->escapeJs($_divId)}').fire('category:beforeLoad', {treeLoader:treeLoader}); }); tree{$block->escapeJs($block->getId())} = new Ext.tree.TreePanel.Enhanced('{$block->escapeJs($_divId)}', { From 6404f8d4b128967cf6913c0c00ec64875fcb23c1 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transogtgroup.com> Date: Tue, 22 Dec 2020 13:27:37 +0200 Subject: [PATCH 068/112] MC-40012: [On-Premise] Failure to allocate memory error when exporting customer addresses in admin --- .../Model/Export/Address.php | 109 +++++++++++++----- .../Test/Unit/Model/Export/AddressTest.php | 89 +++++--------- 2 files changed, 109 insertions(+), 89 deletions(-) diff --git a/app/code/Magento/CustomerImportExport/Model/Export/Address.php b/app/code/Magento/CustomerImportExport/Model/Export/Address.php index 03ce884a44d20..a2d38767432d9 100644 --- a/app/code/Magento/CustomerImportExport/Model/Export/Address.php +++ b/app/code/Magento/CustomerImportExport/Model/Export/Address.php @@ -5,6 +5,17 @@ */ namespace Magento\CustomerImportExport\Model\Export; +use Magento\Customer\Model\ResourceModel\Address\Collection; +use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; +use Magento\Eav\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\ImportExport\Model\Export\Entity\AbstractEav; +use Magento\ImportExport\Model\Export\Factory; +use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIteratorFactory; +use Magento\Store\Model\StoreManagerInterface; + /** * Customer address export * @@ -13,7 +24,7 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ -class Address extends \Magento\ImportExport\Model\Export\Entity\AbstractEav +class Address extends AbstractEav { /**#@+ * Permanent column names @@ -93,7 +104,7 @@ class Address extends \Magento\ImportExport\Model\Export\Entity\AbstractEav /** * Customer addresses collection * - * @var \Magento\Customer\Model\ResourceModel\Address\Collection + * @var Collection */ protected $_addressCollection; @@ -118,31 +129,31 @@ class Address extends \Magento\ImportExport\Model\Export\Entity\AbstractEav * * @var array */ - protected $_customers = []; + protected $_customers; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\ImportExport\Model\Export\Factory $collectionFactory - * @param \Magento\ImportExport\Model\ResourceModel\CollectionByPagesIteratorFactory $resourceColFactory - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param \Magento\Eav\Model\Config $eavConfig + * @param ScopeConfigInterface $scopeConfig + * @param StoreManagerInterface $storeManager + * @param Factory $collectionFactory + * @param CollectionByPagesIteratorFactory $resourceColFactory + * @param TimezoneInterface $localeDate + * @param Config $eavConfig * @param \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory $customerColFactory - * @param \Magento\CustomerImportExport\Model\Export\CustomerFactory $eavCustomerFactory - * @param \Magento\Customer\Model\ResourceModel\Address\CollectionFactory $addressColFactory + * @param CustomerFactory $eavCustomerFactory + * @param CollectionFactory $addressColFactory * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\ImportExport\Model\Export\Factory $collectionFactory, - \Magento\ImportExport\Model\ResourceModel\CollectionByPagesIteratorFactory $resourceColFactory, - \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, - \Magento\Eav\Model\Config $eavConfig, + ScopeConfigInterface $scopeConfig, + StoreManagerInterface $storeManager, + Factory $collectionFactory, + CollectionByPagesIteratorFactory $resourceColFactory, + TimezoneInterface $localeDate, + Config $eavConfig, \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory $customerColFactory, - \Magento\CustomerImportExport\Model\Export\CustomerFactory $eavCustomerFactory, - \Magento\Customer\Model\ResourceModel\Address\CollectionFactory $addressColFactory, + CustomerFactory $eavCustomerFactory, + CollectionFactory $addressColFactory, array $data = [] ) { parent::__construct( @@ -178,19 +189,20 @@ public function __construct( */ protected function _initCustomers() { - if (empty($this->_customers)) { + if ($this->_customers === null) { + $this->_customers = []; // add customer default addresses column name to customer attribute mapping array $this->_customerCollection->addAttributeToSelect(self::$_defaultAddressAttributeMapping); // filter customer collection $this->_customerCollection = $this->_customerEntity->filterEntityCollection($this->_customerCollection); - $customers = []; - $addCustomer = function (\Magento\Customer\Model\Customer $customer) use (&$customers) { - $customers[$customer->getId()] = $customer->getData(); - }; + $selectIds = $this->_customerCollection->getAllIdsSql(); + $this->_customerCollection->setPageSize($this->_pageSize); + $pageCount = $this->_customerCollection->getLastPageNumber(); - $this->_byPagesIterator->iterate($this->_customerCollection, $this->_pageSize, [$addCustomer]); - $this->_customers = $customers; + for ($pageNum = 1; $pageNum <= $pageCount; $pageNum++) { + $this->_customers += $this->loadCustomerData($selectIds, $pageNum); + } } return $this; @@ -211,7 +223,7 @@ protected function _getHeaderColumns() /** * Get customers collection * - * @return \Magento\Customer\Model\ResourceModel\Address\Collection + * @return Collection */ protected function _getEntityCollection() { @@ -227,7 +239,7 @@ public function export() { // skip and filter by customer address attributes $this->_prepareEntityCollection($this->_getEntityCollection()); - $this->_getEntityCollection()->setCustomerFilter(array_keys($this->_customers)); + $this->_getEntityCollection()->setCustomerFilter(array_keys($this->getCustomers())); // prepare headers $this->getWriter()->setHeaderCols($this->_getHeaderColumns()); @@ -248,7 +260,7 @@ public function exportItem($item) $row = $this->_addAttributeValuesToRow($item); /** @var $customer \Magento\Customer\Model\Customer */ - $customer = $this->_customers[$item->getParentId()]; + $customer = $this->getCustomers()[$item->getParentId()]; // Fill row with default address attributes values foreach (self::$_defaultAddressAttributeMapping as $columnName => $attributeCode) { @@ -274,10 +286,8 @@ public function exportItem($item) */ public function setParameters(array $parameters) { - // push filters from post into export customer model + // push filters from post into export customer model $this->_customerEntity->setParameters($parameters); - $this->_initCustomers(); - return parent::setParameters($parameters); } @@ -290,4 +300,39 @@ public function getEntityTypeCode() { return $this->getAttributeCollection()->getEntityTypeCode(); } + + /** + * Get Customers Data + * + * @return array + */ + private function getCustomers(): array + { + $this->_initCustomers(); + return $this->_customers; + } + + /** + * Load Customers Data + * + * @param Select $selectIds + * @param int $pageNum + * @return array + */ + private function loadCustomerData(Select $selectIds, int $pageNum = 0): array + { + $select = $this->_customerCollection->getConnection()->select(); + $select->from( + ['customer' => $this->_customerCollection->getTable('customer_entity')], + ['entity_id', 'email', 'store_id', 'website_id', 'default_billing', 'default_shipping'] + )->where( + 'customer.entity_id IN (?)', $selectIds + ); + + if ($pageNum > 0) { + $select->limitPage($pageNum, $this->_pageSize); + } + + return $this->_customerCollection->getConnection()->fetchAssoc($select); + } } diff --git a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Export/AddressTest.php b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Export/AddressTest.php index f40d71d2efa7c..2d8c105d2b29c 100644 --- a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Export/AddressTest.php +++ b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Export/AddressTest.php @@ -7,10 +7,7 @@ namespace Magento\CustomerImportExport\Test\Unit\Model\Export; -use Magento\Customer\Model\AddressFactory; -use Magento\Customer\Model\Config\Share; -use Magento\Customer\Model\GroupFactory; -use Magento\Customer\Model\ResourceModel\Customer; +use Magento\Customer\Model\ResourceModel\Customer\Collection as CustomerCollection; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\CustomerImportExport\Model\Export\Address; use Magento\CustomerImportExport\Model\Export\CustomerFactory; @@ -19,9 +16,10 @@ use Magento\Eav\Model\Entity\TypeFactory; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Data\Collection; -use Magento\Framework\Data\Collection\AbstractDb; use Magento\Framework\Data\Collection\EntityFactory; use Magento\Framework\DataObject; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; use Magento\Framework\Model\AbstractModel; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -30,7 +28,6 @@ use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIteratorFactory; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManager; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** @@ -82,7 +79,7 @@ class AddressTest extends TestCase /** * ObjectManager helper * - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ protected $_objectManager; @@ -93,6 +90,9 @@ class AddressTest extends TestCase */ protected $_model; + /** + * @inheritdoc + */ protected function setUp(): void { $storeManager = $this->createMock(StoreManager::class); @@ -119,6 +119,9 @@ protected function setUp(): void ); } + /** + * @inheritdoc + */ protected function tearDown(): void { unset($this->_model); @@ -132,8 +135,9 @@ protected function tearDown(): void */ protected function _getModelDependencies() { - $translator = $this->createMock(\stdClass::class); + $pageSize = 1; + $translator = $this->createMock(\stdClass::class); $entityFactory = $this->createMock(EntityFactory::class); /** @var Collection|TestCase $attributeCollection */ @@ -167,34 +171,35 @@ protected function _getModelDependencies() $attributeCollection->addItem($attribute); } - $byPagesIterator = $this->getMockBuilder(\stdClass::class)->addMethods(['iterate']) - ->disableOriginalConstructor() - ->getMock(); - $byPagesIterator->expects( - $this->once() - )->method( - 'iterate' - )->willReturnCallback( - [$this, 'iterate'] - ); - - $customerCollection = $this->getMockBuilder(AbstractDb::class) - ->setMethods(['addAttributeToSelect']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); + $connection = $this->createMock(AdapterInterface::class); + $customerCollection = $this->createMock(CustomerCollection::class); + $customerCollection->method('getConnection')->willReturn($connection); + $customerCollection->expects($this->once())->method('setPageSize')->with($pageSize); + $customerCollection->method('getLastPageNumber')->willReturn(1); + $allIdsSelect = $this->createMock(Select::class); + $customerCollection->method('getAllIdsSql')->willReturn($allIdsSelect); + + $customerSelect = $this->createMock(Select::class); + $customerSelect->method('from')->willReturnSelf(); + $customerSelect->expects($this->once()) + ->method('where') + ->with('customer.entity_id IN (?)', $allIdsSelect) + ->willReturnSelf(); + $customerSelect->expects($this->once())->method('limitPage')->with(1, $pageSize); + $connection->method('select')->willReturn($customerSelect); + $connection->method('fetchAssoc')->with($customerSelect)->willReturn([1 => $this->_customerData]); $customerEntity = $this->getMockBuilder(\stdClass::class) ->addMethods(['filterEntityCollection', 'setParameters']) ->disableOriginalConstructor() ->getMock(); - $customerEntity->expects($this->any())->method('filterEntityCollection')->willReturnArgument(0); - $customerEntity->expects($this->any())->method('setParameters')->willReturnSelf(); + $customerEntity->method('filterEntityCollection')->willReturnArgument(0); + $customerEntity->method('setParameters')->willReturnSelf(); $data = [ 'translator' => $translator, 'attribute_collection' => $attributeCollection, - 'page_size' => 1, - 'collection_by_pages_iterator' => $byPagesIterator, + 'page_size' => $pageSize, 'entity_type_id' => 1, 'customer_collection' => $customerCollection, 'customer_entity' => $customerEntity, @@ -228,36 +233,6 @@ public function getWebsites($withDefault = false) return $websites; } - /** - * Iterate stub - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * - * @param AbstractDb $collection - * @param int $pageSize - * @param array $callbacks - */ - public function iterate(AbstractDb $collection, $pageSize, array $callbacks) - { - $resource = $this->createPartialMock(Customer::class, ['getIdFieldName']); - $resource->expects($this->any())->method('getIdFieldName')->willReturn('id'); - $arguments = [ - 'data' => $this->_customerData, - 'resource' => $resource, - $this->createMock(Share::class), - $this->createMock(AddressFactory::class), - $this->createMock(\Magento\Customer\Model\ResourceModel\Address\CollectionFactory::class), - $this->createMock(GroupFactory::class), - $this->createMock(\Magento\Customer\Model\AttributeFactory::class), - ]; - /** @var $customer \Magento\Customer\Model\Customer|MockObject */ - $customer = $this->_objectManager->getObject(\Magento\Customer\Model\Customer::class, $arguments); - - foreach ($callbacks as $callback) { - call_user_func($callback, $customer); - } - } - /** * Test for method exportItem() * From 4b007c23916f94a1dd4acd3ebcb7d75fa2daf47b Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Tue, 22 Dec 2020 13:28:26 +0200 Subject: [PATCH 069/112] added unit test; refactoring --- .../Block/Adminhtml/Order/Totals/TaxTest.php | 151 ++++++++++++------ 1 file changed, 101 insertions(+), 50 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php index 80563623e0050..222d26c30178a 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php @@ -5,9 +5,6 @@ */ declare(strict_types=1); -/** - * Test class for \Magento\Sales\Block\Adminhtml\Order\Totals\TaxTest - */ namespace Magento\Sales\Test\Unit\Block\Adminhtml\Order\Totals; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -16,46 +13,75 @@ use Magento\Sales\Model\Order\Creditmemo; use Magento\Sales\Model\Order\Invoice; use Magento\Tax\Helper\Data; +use Magento\Tax\Model\ResourceModel\Sales\Order\Tax\Collection; +use Magento\Tax\Model\Sales\Order\Tax as TaxModel; +use Magento\Tax\Model\Sales\Order\TaxFactory; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +/** + * Test for \Magento\Sales\Block\Adminhtml\Order\Totals\Tax + */ class TaxTest extends TestCase { - /** @var MockObject|Tax */ + /** + * @var array + */ + private $calculatedData = [ + 'tax' => 'tax', + 'shipping_tax' => 'shipping_tax', + ]; + + /** + * @var MockObject|Tax + */ private $taxMock; + /** + * @var Data|MockObject + */ + private $taxHelperMock; + + /** + * @var TaxFactory|MockObject + */ + private $taxOrderFactory; + + /** + * @inheridoc + */ protected function setUp(): void { - $getCalculatedTax = [ - 'tax' => 'tax', - 'shipping_tax' => 'shipping_tax', - ]; - $taxHelperMock = $this->getMockBuilder(Data::class) - ->setMethods(['getCalculatedTaxes']) + $this->taxHelperMock = $this->getMockBuilder(Data::class) + ->onlyMethods(['getCalculatedTaxes']) ->disableOriginalConstructor() ->getMock(); - $taxHelperMock->expects($this->any()) - ->method('getCalculatedTaxes') - ->willReturn($getCalculatedTax); + $this->taxOrderFactory = $this->createMock(TaxFactory::class); + + $arguments = $this->getModelArguments( + ['taxHelper' => $this->taxHelperMock, 'taxOrderFactory' => $this->taxOrderFactory] + ); $this->taxMock = $this->getMockBuilder(Tax::class) - ->setConstructorArgs($this->_getConstructArguments($taxHelperMock)) - ->setMethods(['getOrder', 'getSource']) + ->setConstructorArgs($arguments) + ->onlyMethods(['getOrder', 'getSource']) ->getMock(); } /** * Test method for getFullTaxInfo * - * @param Order $source - * @param array $getCalculatedTax - * @param array $getShippingTax + * @param Order|null $source * @param array $expectedResult + * @return void * * @dataProvider getFullTaxInfoDataProvider */ - public function testGetFullTaxInfo($source, $expectedResult) + public function testGetFullTaxInfo(?Order $source, array $expectedResult): void { + $this->taxHelperMock->expects($this->any()) + ->method('getCalculatedTaxes') + ->willReturn($this->calculatedData); $this->taxMock->expects($this->once()) ->method('getOrder') ->willReturn($source); @@ -69,13 +95,15 @@ public function testGetFullTaxInfo($source, $expectedResult) * * @param Invoice|Creditmemo $source * @param array $expectedResult + * @return void * * @dataProvider getCreditAndInvoiceFullTaxInfoDataProvider */ - public function testGetFullTaxInfoWithCreditAndInvoice( - $source, - $expectedResult - ) { + public function testGetFullTaxInfoWithCreditAndInvoice($source, array $expectedResult): void + { + $this->taxHelperMock->expects($this->any()) + ->method('getCalculatedTaxes') + ->willReturn($this->calculatedData); $this->taxMock->expects($this->once()) ->method('getSource') ->willReturn($source); @@ -84,19 +112,54 @@ public function testGetFullTaxInfoWithCreditAndInvoice( $this->assertSame($expectedResult, $actualResult); } + /** + * Test method for getFullTaxInfo when order doesn't have tax + * + * @return void + */ + public function testGetFullTaxInfoOrderWithoutTax(): void + { + $this->taxHelperMock->expects($this->once()) + ->method('getCalculatedTaxes') + ->willReturn(null); + + $orderMock = $this->createMock(Order::class); + $taxCollection = $this->createMock(Collection::class); + $taxCollection->expects($this->once()) + ->method('loadByOrder') + ->with($orderMock) + ->willReturnSelf(); + + $taxOrder = $this->createMock(TaxModel::class); + $taxOrder->expects($this->once()) + ->method('getCollection') + ->willReturn($taxCollection); + $this->taxOrderFactory->expects($this->once()) + ->method('create') + ->willReturn($taxOrder); + + $invoiceMock = $this->createMock(Invoice::class); + $this->taxMock->expects($this->once()) + ->method('getSource') + ->willReturn($invoiceMock); + $this->taxMock->expects($this->once()) + ->method('getOrder') + ->willReturn($orderMock); + + $this->assertNull($this->taxMock->getFullTaxInfo()); + } + /** * Provide the tax helper mock as a constructor argument * - * @param $taxHelperMock + * @param array $arguments * @return array */ - protected function _getConstructArguments($taxHelperMock) + private function getModelArguments(array $arguments): array { $objectManagerHelper = new ObjectManager($this); - return $objectManagerHelper->getConstructArguments( - Tax::class, - ['taxHelper' => $taxHelperMock] - ); + + return $objectManagerHelper->getConstructArguments(Tax::class, $arguments); } /** @@ -106,19 +169,15 @@ protected function _getConstructArguments($taxHelperMock) * * @return array */ - public function getFullTaxInfoDataProvider() + public function getFullTaxInfoDataProvider(): array { - $salesModelOrderMock = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->getMock(); + $salesModelOrderMock = $this->createMock(Order::class); + return [ 'source is not an instance of \Magento\Sales\Model\Order' => [null, []], 'source is an instance of \Magento\Sales\Model\Order and has reasonable data' => [ $salesModelOrderMock, - [ - 'tax' => 'tax', - 'shipping_tax' => 'shipping_tax', - ], + $this->calculatedData, ] ]; } @@ -130,22 +189,14 @@ public function getFullTaxInfoDataProvider() * * @return array */ - public function getCreditAndInvoiceFullTaxInfoDataProvider() + public function getCreditAndInvoiceFullTaxInfoDataProvider(): array { - $invoiceMock = $this->getMockBuilder(Invoice::class) - ->disableOriginalConstructor() - ->getMock(); - $creditMemoMock = $this->getMockBuilder(Creditmemo::class) - ->disableOriginalConstructor() - ->getMock(); + $invoiceMock = $this->createMock(Invoice::class); + $creditMemoMock = $this->createMock(Creditmemo::class); - $expected = [ - 'tax' => 'tax', - 'shipping_tax' => 'shipping_tax', - ]; return [ - 'invoice' => [$invoiceMock, $expected], - 'creditMemo' => [$creditMemoMock, $expected] + 'invoice' => [$invoiceMock, $this->calculatedData], + 'creditMemo' => [$creditMemoMock, $this->calculatedData] ]; } } From 292c560070037f280d9953af7966af7d1acaf890 Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Tue, 22 Dec 2020 13:54:39 +0200 Subject: [PATCH 070/112] =?UTF-8?q?MC-39809:=20A=20Product=20with=20Custom?= =?UTF-8?q?izable=20Option=20(File)=20can=20not=20be=20added=20by=20Admin?= =?UTF-8?q?=20from=20the=20=E2=80=9CShopping=20Cart=20section=20(=E2=80=9C?= =?UTF-8?q?Customer's=20Activities=E2=80=9D=20column)=20to=20Order?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Magento/Bundle/Test/Unit/Model/Product/TypeTest.php | 8 +++++++- .../Magento/Catalog/Model/Product/Type/AbstractType.php | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php index b7041051591d8..9c4d4ce00b7c0 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php @@ -11,7 +11,6 @@ use Magento\Bundle\Model\Product\Type; use Magento\Bundle\Model\ResourceModel\BundleFactory; use Magento\Bundle\Model\ResourceModel\Option\Collection; -use Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor; use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection; use Magento\Bundle\Model\ResourceModel\Selection\CollectionFactory; use Magento\Bundle\Model\Selection; @@ -28,6 +27,7 @@ use Magento\CatalogInventory\Api\StockStateInterface; use Magento\CatalogInventory\Model\StockRegistry; use Magento\CatalogInventory\Model\StockState; +use Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor; use Magento\Framework\DataObject; use Magento\Framework\EntityManager\EntityMetadataInterface; use Magento\Framework\EntityManager\MetadataPool; @@ -1548,6 +1548,10 @@ public function testPrepareForCartAdvancedSpecifyProductOptions() ->disableOriginalConstructor() ->getMock(); + $buyRequest->method('getOptions') + ->willReturn([333 => ['type' => 'image/jpeg']]); + $option->method('getId') + ->willReturn(333); $this->parentClass($group, $option, $buyRequest, $product); $product->expects($this->any()) @@ -1556,6 +1560,8 @@ public function testPrepareForCartAdvancedSpecifyProductOptions() $buyRequest->expects($this->once()) ->method('getBundleOption') ->willReturn([0, '', 'str']); + $group->expects($this->once()) + ->method('validateUserValue'); $result = $this->model->prepareForCartAdvanced($buyRequest, $product); $this->assertEquals('Please specify product option(s).', $result); diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index f90b097415661..19f6461d44b6a 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Catalog\Model\Product\Type; @@ -605,7 +604,11 @@ protected function _prepareOptions(\Magento\Framework\DataObject $buyRequest, $p if ($product->getSkipCheckRequiredOption() !== true) { $group->validateUserValue($optionsFromRequest); } elseif ($optionsFromRequest !== null && isset($optionsFromRequest[$option->getId()])) { - $transport->options[$option->getId()] = $optionsFromRequest[$option->getId()]; + if (is_array($optionsFromRequest[$option->getId()])) { + $group->validateUserValue($optionsFromRequest); + } else { + $transport->options[$option->getId()] = $optionsFromRequest[$option->getId()]; + } } } catch (LocalizedException $e) { From 84b1ea0f3ff98c8e57049644fd1f456804738d0f Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Tue, 22 Dec 2020 14:43:23 +0200 Subject: [PATCH 071/112] fixed unit --- .../Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php index 222d26c30178a..b0c053a36de0f 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Totals/TaxTest.php @@ -129,6 +129,9 @@ public function testGetFullTaxInfoOrderWithoutTax(): void ->method('loadByOrder') ->with($orderMock) ->willReturnSelf(); + $taxCollection->expects($this->once()) + ->method('toArray') + ->willReturn(['items' => []]); $taxOrder = $this->createMock(TaxModel::class); $taxOrder->expects($this->once()) From 826b697e7049701cd825e88efeb39c31c40b09ec Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 22 Dec 2020 16:06:02 +0200 Subject: [PATCH 072/112] MC-39037: Report tabs are not aligned and not able to view properly --- .../Block/Widget/Grid/ExtendedTest.php | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/ExtendedTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/ExtendedTest.php index 328a85ffd51ad..6d3761fdfcb79 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/ExtendedTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/ExtendedTest.php @@ -5,34 +5,44 @@ */ namespace Magento\Backend\Block\Widget\Grid; +use Laminas\Stdlib\Parameters; +use Magento\Backend\Block\Template\Context; +use Magento\Framework\Data\Collection; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** * @magentoAppArea adminhtml */ -class ExtendedTest extends \PHPUnit\Framework\TestCase +class ExtendedTest extends TestCase { /** - * @var \Magento\Backend\Block\Widget\Grid\Extended + * @var Extended */ protected $_block; /** - * @var \Magento\Framework\View\LayoutInterface + * @var LayoutInterface */ protected $_layoutMock; + /** + * @inheritDoc + */ protected function setUp(): void { parent::setUp(); - $this->_layoutMock = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\View\LayoutInterface::class + $this->_layoutMock = Bootstrap::getObjectManager()->get( + LayoutInterface::class ); - $context = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Backend\Block\Template\Context::class, + $context = Bootstrap::getObjectManager()->create( + Context::class, ['layout' => $this->_layoutMock] ); $this->_block = $this->_layoutMock->createBlock( - \Magento\Backend\Block\Widget\Grid\Extended::class, + Extended::class, 'grid', ['context' => $context] ); @@ -47,7 +57,7 @@ protected function setUp(): void public function testAddColumnAddsChildToColumnSet() { $this->assertInstanceOf( - \Magento\Backend\Block\Widget\Grid\Column::class, + Column::class, $this->_block->getColumnSet()->getChildBlock('column1') ); $this->assertCount(2, $this->_block->getColumnSet()->getChildNames()); @@ -84,4 +94,32 @@ public function testGetMainButtonsHtmlReturnsEmptyStringIfFiltersArentVisible() $this->_block->setFilterVisibility(false); $this->assertEquals('', $this->_block->getMainButtonsHtml()); } + + /** + * Checks that template does not have redundant div close tag + * + * @return void + */ + public function testExtendedTemplateMarkup(): void + { + $mockCollection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->_block->setCollection($mockCollection); + $this->_block->getRequest() + ->setQuery( + Bootstrap::getObjectManager() + ->create( + Parameters::class, + [ + 'values' => [ + 'ajax' => true + ] + ] + ) + ); + $html = $this->_block->getHtml(); + $html = str_replace(["\n", " "], '', $html); + $this->assertStringEndsWith("</table></div>", $html); + } } From 8e74c61c8ccb10779fd6c307cd9b0e47bbd14294 Mon Sep 17 00:00:00 2001 From: engcom-Kilo <mikola.malevanec@transoftgroup.com> Date: Tue, 22 Dec 2020 18:30:58 +0200 Subject: [PATCH 073/112] MC-39609: Product import: Only the first additional image is validated. --- .../Model/Import/Product/Validator/Media.php | 2 +- .../Model/Import/ProductTest.php | 21 +++++++++++++++++++ .../Model/Import/_files/import_media.csv | 2 +- ...port_media_additional_images_storeview.csv | 2 +- ...media_additional_images_with_wrong_url.csv | 2 ++ .../_files/import_media_existing_images.csv | 2 +- .../_files/import_media_hidden_images.csv | 4 ++-- ...ucts_to_import_with_non_existing_image.csv | 2 +- 8 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_with_wrong_url.csv diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/Media.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/Media.php index d1fe1eee80e19..8df5afce568f1 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/Media.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/Media.php @@ -114,8 +114,8 @@ public function isValid($value) ] ); $valid = false; + break; } - break; } } return $valid; diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 3ca6754c77767..dfb57d91a7cc6 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -3415,4 +3415,25 @@ public function importImagesDataProvider(): array ] ]; } + + /** + * Verify additional images url validation during import. + * + * @magentoDbIsolation enabled + * @return void + */ + public function testImportInvalidAdditionalImages(): void + { + $pathToFile = __DIR__ . '/_files/import_media_additional_images_with_wrong_url.csv'; + $filesystem = BootstrapHelper::getObjectManager()->create(Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create(Csv::class, ['file' => $pathToFile, 'directory' => $directory]); + $errors = $this->_model->setSource($source)->setParameters(['behavior' => Import::BEHAVIOR_APPEND]) + ->validateData(); + $this->assertEquals($errors->getErrorsCount(), 1); + $this->assertEquals( + "Wrong URL/path used for attribute additional_images", + $errors->getErrorByRowNumber(0)[0]->getErrorMessage() + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media.csv index a3e8f8e47ab08..3478512e25028 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media.csv @@ -1,2 +1,2 @@ sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus -simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two",,,,,,,, +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg,magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two",,,,,,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv index ed8755a73fcb1..4d2e7234c9362 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv @@ -1,2 +1,2 @@ "sku","store_view_code","additional_images","additional_image_labels" -"simple","fixturestore","magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two" +"simple","fixturestore","magento_additional_image_one.jpg,magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_with_wrong_url.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_with_wrong_url.csv new file mode 100644 index 0000000000000..c2b9e4f6fa936 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_with_wrong_url.csv @@ -0,0 +1,2 @@ +sku,product_type,name,price,attribute_set_code,additional_images +simple1,simple,"simple 1",25,Default,"additional_image_one.jpg,additional_image with spaces.jpg" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_existing_images.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_existing_images.csv index a3e8f8e47ab08..3478512e25028 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_existing_images.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_existing_images.csv @@ -1,2 +1,2 @@ sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus -simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two",,,,,,,, +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg,magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two",,,,,,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv index 1c1bebee57578..f54d4b7f401d4 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv @@ -1,2 +1,2 @@ -sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus -simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product,magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/2015 7:05,10/20/2015 7:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two","magento_image.jpg,magento_thumbnail.jpg,magento_additional_image_two.jpg",,,,,,, +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product,magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/2015 7:05,10/20/2015 7:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg,magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two","magento_image.jpg,magento_thumbnail.jpg,magento_additional_image_two.jpg",,,,,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_existing_image.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_existing_image.csv index 8122433a8c9e1..6037ee008a8ec 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_existing_image.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_existing_image.csv @@ -1,2 +1,2 @@ sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus -simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,/no/exists/image/magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two",,,,,,,, +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,/no/exists/image/magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/15 07:05,10/20/15 07:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg,magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two",,,,,,,, From 1093ad0421851caad9bf361269aadccd7997667f Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 23 Dec 2020 11:29:33 +0200 Subject: [PATCH 074/112] MC-39852: Change customer custom address file attribute editor on backend to don't use direct link for preview --- .../Controller/Adminhtml/Address/Viewfile.php | 180 ++++++++++++++++++ .../Magento/Customer/Model/FileProcessor.php | 7 +- .../Test/Unit/Model/FileProcessorTest.php | 22 +-- pub/media/customer_address/.htaccess | 7 + 4 files changed, 202 insertions(+), 14 deletions(-) create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/Viewfile.php create mode 100644 pub/media/customer_address/.htaccess diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Viewfile.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Viewfile.php new file mode 100644 index 0000000000000..a8cad14c23a72 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Viewfile.php @@ -0,0 +1,180 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Adminhtml\Address; + +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Framework\Exception\NotFoundException; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Controller\Result\RawFactory; +use Magento\Framework\Url\DecoderInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Filesystem; +use Magento\Framework\Controller\Result\Raw; +use Magento\MediaStorage\Helper\File\Storage; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\Filesystem\Io\File as IoFile; +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Backend\App\Action; + +/** + * Class Viewfile serves to show file or image by file/image name provided in request parameters. + */ +class Viewfile extends Action implements HttpGetActionInterface +{ + /** + * Authorization level of a basic admin session + */ + const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var RawFactory + */ + private $resultRawFactory; + + /** + * @var DecoderInterface + */ + private $urlDecoder; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var Storage + */ + private $storage; + + /** + * @var FileFactory + */ + private $fileFactory; + + /** + * @var IoFile + */ + private $ioFile; + + /** + * @param Context $context + * @param FileFactory $fileFactory + * @param RawFactory $resultRawFactory + * @param DecoderInterface $urlDecoder + * @param Filesystem $filesystem + * @param Storage $storage + * @param IoFile $ioFile + */ + public function __construct( + Context $context, + FileFactory $fileFactory, + RawFactory $resultRawFactory, + DecoderInterface $urlDecoder, + Filesystem $filesystem, + Storage $storage, + IoFile $ioFile + ) { + parent::__construct($context); + $this->resultRawFactory = $resultRawFactory; + $this->urlDecoder = $urlDecoder; + $this->filesystem = $filesystem; + $this->storage = $storage; + $this->fileFactory = $fileFactory; + $this->ioFile = $ioFile; + } + + /** + * Customer address view file action + * + * @return ResultInterface|void + * @throws NotFoundException + */ + public function execute() + { + list($file, $plain) = $this->getFileParams(); + + $directory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); + $fileName = AddressMetadataInterface::ENTITY_TYPE_ADDRESS . DIRECTORY_SEPARATOR . + ltrim($file, DIRECTORY_SEPARATOR); + $path = $directory->getAbsolutePath($fileName); + if (mb_strpos($path, '..') !== false + || (!$directory->isFile($fileName) && !$this->storage->processStorageFile($path)) + ) { + throw new NotFoundException(__('Page not found.')); + } + + $pathInfo = $this->ioFile->getPathInfo($path); + if ($plain) { + $extension = $pathInfo['extension']; + switch (strtolower($extension)) { + case 'gif': + $contentType = 'image/gif'; + break; + case 'jpg': + $contentType = 'image/jpeg'; + break; + case 'png': + $contentType = 'image/png'; + break; + default: + $contentType = 'application/octet-stream'; + break; + } + $stat = $directory->stat($fileName); + $contentLength = $stat['size']; + $contentModify = $stat['mtime']; + + /** @var Raw $resultRaw */ + $resultRaw = $this->resultRawFactory->create(); + $resultRaw->setHttpResponseCode(200) + ->setHeader('Pragma', 'public', true) + ->setHeader('Content-type', $contentType, true) + ->setHeader('Content-Length', $contentLength) + ->setHeader('Last-Modified', date('r', $contentModify)); + $resultRaw->setContents($directory->readFile($fileName)); + + return $resultRaw; + } else { + $name = $pathInfo['basename']; + $this->fileFactory->create( + $name, + ['type' => 'filename', 'value' => $fileName], + DirectoryList::MEDIA + ); + } + } + + /** + * Get parameters from request. + * + * @return array + * @throws NotFoundException + */ + private function getFileParams() : array + { + $file = null; + $plain = false; + if ($this->getRequest()->getParam('file')) { + // download file + $file = $this->urlDecoder->decode( + $this->getRequest()->getParam('file') + ); + } elseif ($this->getRequest()->getParam('image')) { + // show plain image + $file = $this->urlDecoder->decode( + $this->getRequest()->getParam('image') + ); + $plain = true; + } else { + throw new NotFoundException(__('Page not found.')); + } + + return [$file, $plain]; + } +} diff --git a/app/code/Magento/Customer/Model/FileProcessor.php b/app/code/Magento/Customer/Model/FileProcessor.php index 02bfe78be535c..59e2d5fb2b577 100644 --- a/app/code/Magento/Customer/Model/FileProcessor.php +++ b/app/code/Magento/Customer/Model/FileProcessor.php @@ -158,9 +158,10 @@ public function getViewUrl($filePath, $type) $viewUrl = ''; if ($this->entityTypeCode == AddressMetadataInterface::ENTITY_TYPE_ADDRESS) { - $filePath = $this->entityTypeCode . '/' . ltrim($filePath, '/'); - $viewUrl = $this->urlBuilder->getBaseUrl(['_type' => UrlInterface::URL_TYPE_MEDIA]) - . $this->mediaDirectory->getRelativePath($filePath); + $viewUrl = $this->urlBuilder->getUrl( + 'customer/address/viewfile', + [$type => $this->urlEncoder->encode(ltrim($filePath, '/'))] + ); } if ($this->entityTypeCode == CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER) { diff --git a/app/code/Magento/Customer/Test/Unit/Model/FileProcessorTest.php b/app/code/Magento/Customer/Test/Unit/Model/FileProcessorTest.php index 7a0522f6476f2..fb775ce78bbbc 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/FileProcessorTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/FileProcessorTest.php @@ -162,22 +162,22 @@ public function testGetViewUrlCustomer() public function testGetViewUrlCustomerAddress() { $filePath = 'filename.ext1'; + $encodedFilePath = 'encodedfilenameext1'; - $baseUrl = 'baseUrl'; - $relativeUrl = 'relativeUrl'; + $fileUrl = 'fileUrl'; - $this->urlBuilder->expects($this->once()) - ->method('getBaseUrl') - ->with(['_type' => UrlInterface::URL_TYPE_MEDIA]) - ->willReturn($baseUrl); + $this->urlEncoder->expects($this->once()) + ->method('encode') + ->with($filePath) + ->willReturn($encodedFilePath); - $this->mediaDirectory->expects($this->once()) - ->method('getRelativePath') - ->with(AddressMetadataInterface::ENTITY_TYPE_ADDRESS . '/' . $filePath) - ->willReturn($relativeUrl); + $this->urlBuilder->expects($this->once()) + ->method('getUrl') + ->with('customer/address/viewfile', ['image' => $encodedFilePath]) + ->willReturn($fileUrl); $model = $this->getModel(AddressMetadataInterface::ENTITY_TYPE_ADDRESS); - $this->assertEquals($baseUrl . $relativeUrl, $model->getViewUrl($filePath, 'image')); + $this->assertEquals($fileUrl, $model->getViewUrl($filePath, 'image')); } public function testRemoveUploadedFile() diff --git a/pub/media/customer_address/.htaccess b/pub/media/customer_address/.htaccess new file mode 100644 index 0000000000000..b97408bad3f2e --- /dev/null +++ b/pub/media/customer_address/.htaccess @@ -0,0 +1,7 @@ +<IfVersion < 2.4> + order allow,deny + deny from all +</IfVersion> +<IfVersion >= 2.4> + Require all denied +</IfVersion> From 275e42e42a51f3c04b2dafd4d17a8245e1a036cf Mon Sep 17 00:00:00 2001 From: SmVladyslav <vlatame.tsg@gmail.com> Date: Wed, 23 Dec 2020 13:39:28 +0200 Subject: [PATCH 075/112] MC-37385: Unexpected unchecking of "Append Comments" check-box --- .../Magento/Sales/Model/AdminOrder/Create.php | 7 ++-- .../Adminhtml/Order/Create/LoadBlockTest.php | 34 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 5d621f1632837..a0e0dd9aeb2bd 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -1716,10 +1716,9 @@ public function importPostData($data) if (isset($data['comment'])) { $this->getQuote()->addData($data['comment']); - if (empty($data['comment']['customer_note_notify'])) { - $this->getQuote()->setCustomerNoteNotify(false); - } else { - $this->getQuote()->setCustomerNoteNotify(true); + if ($this->getIsValidate()) { + $notify = !empty($data['comment']['customer_note_notify']); + $this->getQuote()->setCustomerNoteNotify($notify); } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php index 529b491269643..3567a7e00764f 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php @@ -231,6 +231,40 @@ public function testAddProductToOrderFromWishList(): void $this->assertCount(1, $quoteItems); } + /** + * Check that customer notification is NOT disabled after comment is updated. + * + * @return void + * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php + */ + public function testUpdateCustomerNote(): void + { + $customerNote = 'Example Comment'; + $quoteId = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address')->getId(); + $this->session->setQuoteId($quoteId); + $params = [ + 'json' => false, + 'block' => 'totals', + 'as_js_varname' => false, + ]; + $post = $this->hydratePost([ + 'order' => [ + 'comment' => [ + CartInterface::KEY_CUSTOMER_NOTE => $customerNote + ], + ], + ]); + $this->dispatchWitParams($params, $post); + + $quote = $this->session->getQuote(); + $this->assertEquals($customerNote, $quote->getCustomerNote()); + $this->assertTrue((bool)$quote->getCustomerNoteNotify()); + + preg_match('/id="notify_customer"(?<attributes>.*?)\/>/s', $this->getResponse()->getBody(), $matches); + $this->assertArrayHasKey('attributes', $matches); + $this->assertStringContainsString('checked="checked"', $matches['attributes']); + } + /** * Check customer quotes * From 08a6b9cb5fdd08b0813936d0e0309219c11e3c84 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transogtgroup.com> Date: Wed, 23 Dec 2020 15:21:34 +0200 Subject: [PATCH 076/112] MC-39864: [Magento Cloud] - Tax Miscalculation --- .../Model/Order/Creditmemo/Total/Tax.php | 69 +++++++++++++++++-- .../Model/ResourceModel/Order/Invoice.php | 35 ++++++++-- 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php index eba84dcfb6364..faed11c4f718e 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -5,13 +5,31 @@ */ namespace Magento\Sales\Model\Order\Creditmemo\Total; +use Magento\Sales\Api\Data\CreditmemoInterface; use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\ResourceModel\Order\Invoice as ResourceInvoice; /** * Collects credit memo taxes. */ class Tax extends AbstractTotal { + /** + * @var ResourceInvoice + */ + private $resourceInvoice; + + /** + * @param ResourceInvoice $resourceInvoice + * @param array $data + */ + public function __construct(ResourceInvoice $resourceInvoice, array $data = []) + { + $this->resourceInvoice = $resourceInvoice; + parent::__construct($data); + } + /** * {@inheritdoc} * @SuppressWarnings(PHPMD.NPathComplexity) @@ -182,8 +200,12 @@ private function calculateAllowedTax(Creditmemo $creditMemo): float { $invoice = $creditMemo->getInvoice(); $order = $creditMemo->getOrder(); - $amount = $invoice !== null ? $invoice->getTaxAmount() - : $order->getTaxInvoiced() - $order->getTaxRefunded(); + if ($invoice!== null) { + $amount = $invoice->getTaxAmount() + - $this->calculateInvoiceRefundedAmount($invoice, CreditmemoInterface::TAX_AMOUNT); + } else { + $amount = $order->getTaxInvoiced() - $order->getTaxRefunded(); + } return (float) $amount - $creditMemo->getTaxAmount(); } @@ -198,8 +220,13 @@ private function calculateAllowedBaseTax(Creditmemo $creditMemo): float { $invoice = $creditMemo->getInvoice(); $order = $creditMemo->getOrder(); - $amount = $invoice !== null ? $invoice->getBaseTaxAmount() - : $order->getBaseTaxInvoiced() - $order->getBaseTaxRefunded(); + + if ($invoice!== null) { + $amount = $invoice->getBaseTaxAmount() + - $this->calculateInvoiceRefundedAmount($invoice, CreditmemoInterface::BASE_TAX_AMOUNT); + } else { + $amount = $order->getBaseTaxInvoiced() - $order->getBaseTaxRefunded(); + } return (float) $amount - $creditMemo->getBaseTaxAmount(); } @@ -217,7 +244,14 @@ private function calculateAllowedDiscountTaxCompensation(Creditmemo $creditMemo) if ($invoice) { $amount = $invoice->getDiscountTaxCompensationAmount() - + $invoice->getShippingDiscountTaxCompensationAmount(); + + $invoice->getShippingDiscountTaxCompensationAmount() + - $this->calculateInvoiceRefundedAmount( + $invoice, + CreditmemoInterface::DISCOUNT_TAX_COMPENSATION_AMOUNT + ) - $this->calculateInvoiceRefundedAmount( + $invoice, + CreditmemoInterface::SHIPPING_DISCOUNT_TAX_COMPENSATION_AMOUNT + ); } else { $amount = $order->getDiscountTaxCompensationInvoiced() + $order->getShippingDiscountTaxCompensationAmount() @@ -243,7 +277,14 @@ private function calculateAllowedBaseDiscountTaxCompensation(Creditmemo $creditM if ($invoice) { $amount = $invoice->getBaseDiscountTaxCompensationAmount() - + $invoice->getBaseShippingDiscountTaxCompensationAmnt(); + + $invoice->getBaseShippingDiscountTaxCompensationAmnt() + - $this->calculateInvoiceRefundedAmount( + $invoice, + CreditmemoInterface::BASE_DISCOUNT_TAX_COMPENSATION_AMOUNT + ) - $this->calculateInvoiceRefundedAmount( + $invoice, + CreditmemoInterface::BASE_SHIPPING_DISCOUNT_TAX_COMPENSATION_AMNT + ); } else { $amount = $order->getBaseDiscountTaxCompensationInvoiced() + $order->getBaseShippingDiscountTaxCompensationAmnt() @@ -255,4 +296,20 @@ private function calculateAllowedBaseDiscountTaxCompensation(Creditmemo $creditM - $creditMemo->getBaseShippingDiscountTaxCompensationAmnt() - $creditMemo->getBaseDiscountTaxCompensationAmount(); } + + /** + * Calculate refunded amount for invoice + * + * @param Invoice $invoice + * @param string $field + * @return float + */ + private function calculateInvoiceRefundedAmount(Invoice $invoice, string $field): float + { + if (empty($invoice->getId())) { + return 0; + } + + return $this->resourceInvoice->calculateRefundedAmount((int)$invoice->getId(), $field); + } } diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Invoice.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Invoice.php index 848f88118ed32..bc21e9cd6c894 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Order/Invoice.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Invoice.php @@ -5,11 +5,9 @@ */ namespace Magento\Sales\Model\ResourceModel\Order; -use Magento\Framework\App\ResourceConnection; -use Magento\SalesSequence\Model\Manager; -use Magento\Sales\Model\ResourceModel\Attribute; +use Magento\Framework\DataObject; +use Magento\Framework\Model\AbstractModel; use Magento\Sales\Model\ResourceModel\EntityAbstract as SalesResource; -use Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot; use Magento\Sales\Model\Spi\InvoiceResourceInterface; /** @@ -37,10 +35,10 @@ protected function _construct() /** * Perform actions before object save * - * @param \Magento\Framework\Model\AbstractModel|\Magento\Framework\DataObject $object + * @param AbstractModel|DataObject $object * @return $this */ - protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) + protected function _beforeSave(AbstractModel $object) { /** @var \Magento\Sales\Model\Order\Invoice $object */ if (!$object->getOrderId() && $object->getOrder()) { @@ -50,4 +48,29 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) return parent::_beforeSave($object); } + + /** + * Calculate refunded amount for invoice + * + * @param int $invoiceId + * @param string $filed + * @return float + * @throws \InvalidArgumentException + */ + public function calculateRefundedAmount(int $invoiceId, string $filed): float + { + if (empty($filed)) { + throw new \InvalidArgumentException('The field param must be passed'); + } + + $select = $this->getConnection()->select(); + $select->from( + ['credit_memo' => $this->getTable('sales_creditmemo')], + ['total' => new \Zend_Db_Expr("SUM(credit_memo.{$filed})")] + )->where( + "credit_memo.invoice_id = ?", $invoiceId + ); + + return (float) $this->getConnection()->fetchOne($select); + } } From e07d328c1f88e885ac6239d4689afcee09859394 Mon Sep 17 00:00:00 2001 From: tuna <ladiesman9x@gmail.com> Date: Fri, 18 Dec 2020 12:56:09 +0700 Subject: [PATCH 077/112] Update varnish 6 vcl config update --- app/code/Magento/PageCache/etc/varnish6.vcl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/PageCache/etc/varnish6.vcl b/app/code/Magento/PageCache/etc/varnish6.vcl index bce89fe263573..cc381baaf313a 100644 --- a/app/code/Magento/PageCache/etc/varnish6.vcl +++ b/app/code/Magento/PageCache/etc/varnish6.vcl @@ -62,13 +62,13 @@ sub vcl_recv { return (pass); } - # Bypass shopping cart and checkout - if (req.url ~ "/checkout") { + # Bypass customer, shopping cart, checkout + if (req.url ~ "/customer" || req.url ~ "/checkout") { return (pass); } # Bypass health check requests - if (req.url ~ "/pub/health_check.php") { + if (req.url ~ "^/(pub/)?(health_check.php)$") { return (pass); } From 1f04b0ad5871ddd04230636daa0f9d6d08a8b90c Mon Sep 17 00:00:00 2001 From: Anna Pak <a.pak@atwix.com> Date: Wed, 23 Dec 2020 18:27:38 +0200 Subject: [PATCH 078/112] Updated with StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup --- .../Test/DeleteBundleDynamicProductFromShoppingCartTest.xml | 2 +- .../Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml | 2 +- .../Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml | 2 +- .../Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml | 2 +- ...orefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml | 2 +- .../Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleDynamicProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleDynamicProductFromShoppingCartTest.xml index 96a236336993f..a30f118bd6207 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleDynamicProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleDynamicProductFromShoppingCartTest.xml @@ -56,7 +56,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Add product to the cart --> - <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeAndAddToCart"/> <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$$createBundleDynamicProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml index b64b59ef6109c..d1f452896c84f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml @@ -48,7 +48,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Add product to the cart --> - <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeAndAddToCart"/> <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$$createFixedBundleProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index 38e2203b45258..73a2e4757e954 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -159,7 +159,7 @@ <!-- Add Bundle Product to cart --> <amOnPage url="{{StorefrontProductPage.url($$createFixedBundleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToBundleProductPage"/> <waitForPageLoad stepKey="waitForFixedBundleProductPageLoad"/> - <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeAndAddToCart"/> <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFixedBundleProductFromStorefrontProductPage"> <argument name="productName" value="$$createFixedBundleProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml index f16f577a4088c..3aa93d72571f9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml @@ -59,7 +59,7 @@ <amOnPage url="{{StorefrontProductPage.url($$createBundleDynamicProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Add bundle product to the cart --> - <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeAndAddToCart"/> <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$$createBundleDynamicProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml index ecbff39807f30..c81c6d36786eb 100644 --- a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityNegativeScenarioTest.xml @@ -129,7 +129,7 @@ <argument name="product" value="$createBundleProduct$"/> </actionGroup> <waitForElementVisible selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="waitForCustomizeAndAddToCartButton"/> - <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeAndAddToCart"/> <waitForPageLoad stepKey="waitForBundleProductPageLoad"/> <dontSeeElement selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="dontSeeButtonOnBundleProductPage"/> <!-- Grouped product --> diff --git a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml index 59697bb6f5bdf..865852c10acfd 100644 --- a/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml +++ b/app/code/Magento/InstantPurchase/Test/Mftf/Test/StorefrontInstantPurchaseFunctionalityTest.xml @@ -109,7 +109,7 @@ <!-- Bundle Product --> <amOnPage url="{{StorefrontProductPage.url($createBundleProduct.custom_attributes[url_key]$)}}" stepKey="openBundleProductPage"/> <waitForElementVisible selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="waitForCustomizeAndAddToCartButton"/> - <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeAndAddToCart"/> <waitForElementVisible selector="{{StorefrontInstantPurchaseSection.instantPurchaseButton}}" stepKey="waitForButtonOnBundleProductPage"/> <!-- Grouped product --> <amOnPage url="{{StorefrontProductPage.url($createGroupedProduct.custom_attributes[url_key]$)}}" stepKey="openGroupedProductPage"/> From 64d2fb97a832593ff0efdd6e32c0770d73b68140 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 23 Dec 2020 21:26:43 +0200 Subject: [PATCH 079/112] MC-39600: Create automated test for: "Change category/product url rewrite suffix in configurations" --- .../Catalog/Url/Rewrite/SuffixTest.php | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php new file mode 100644 index 0000000000000..6553dffb04df6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php @@ -0,0 +1,287 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\System\Config\Backend\Catalog\Url\Rewrite; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; +use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\Cache\Type\Block; +use Magento\Framework\App\Cache\Type\Collection; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Model\Storage\DbStorage; +use PHPUnit\Framework\TestCase; + +/** + * Class checks url suffix config save behaviour + * + * @see \Magento\Catalog\Model\System\Config\Backend\Catalog\Url\Rewrite\Suffix + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class SuffixTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Suffix */ + private $model; + + /** @var DbStorage */ + private $urlFinder; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var TypeListInterface */ + private $typeList; + + /** @var ScopeConfigInterface */ + private $scopeConfig; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var int */ + private $defaultStoreId; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->model = $this->objectManager->get(Suffix::class); + $this->urlFinder = $this->objectManager->get(DbStorage::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->typeList = $this->objectManager->get(TypeListInterface::class); + $this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->defaultStoreId = (int)$this->storeManager->getStore('default')->getId(); + } + + /** + * @return void + */ + public function testSaveWithError(): void + { + $this->expectException(LocalizedException::class); + $this->expectErrorMessage((string)__('Anchor symbol (#) is not supported in url rewrite suffix.')); + $this->model->setValue('.html#'); + $this->model->beforeSave(); + } + + /** + * @dataProvider wrongValuesProvider + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @param array $data + * @return void + */ + public function testSaveWithWrongData(array $data): void + { + $productId = (int)$this->productRepository->get('simple2')->getId(); + $this->model->addData($data); + $this->model->afterSave(); + $this->assertRewrite( + $this->scopeConfig->getValue(ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX), + [ + 'entity_type' => ProductUrlRewriteGenerator::ENTITY_TYPE, + 'entity_id' => $productId, + 'store_id' => $this->defaultStoreId, + ] + ); + } + + /** + * @return array + */ + public function wrongValuesProvider(): array + { + return [ + 'with_wrong_path' => [ + ['path' => 'wrong_path', 'value' => 'some_test_value'], + ], + 'with_null_value' => [ + ['path' => ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX, 'value' => null], + ], + ]; + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/product_multistore_different_short_description.php + * + * @return void + */ + public function testSaveInStoreScope(): void + { + $productId = $this->productRepository->get('simple-different-short-description')->getId(); + $newSuffix = 'some_test_value_for_store'; + $storeId = $this->storeManager->getStore('fixturestore')->getId(); + $this->model->addData([ + 'path' => ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX, + 'value' => $newSuffix, + 'scope' => ScopeInterface::SCOPE_STORES, + 'scope_id' => $storeId, + ]); + $this->model->afterSave(); + $this->assertRewrite( + $this->scopeConfig->getValue(ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX), + [ + 'entity_type' => ProductUrlRewriteGenerator::ENTITY_TYPE, + 'entity_id' => $productId, + 'store_id' => $this->defaultStoreId, + ] + ); + $this->assertRewrite( + $newSuffix, + [ + 'entity_type' => ProductUrlRewriteGenerator::ENTITY_TYPE, + 'entity_id' => $productId, + 'store_id' => $storeId, + ] + ); + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * + * @return void + */ + public function testSaveInWebsiteScope(): void + { + $productId = (int)$this->productRepository->get('simple-on-two-websites')->getId(); + $newSuffix = 'some_test_value_for_website'; + $website = $this->storeManager->getWebsite('test'); + $this->model->addData([ + 'path' => ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX, + 'value' => $newSuffix, + 'scope' => ScopeInterface::SCOPE_WEBSITES, + 'scope_id' => $website->getId(), + ]); + $this->model->afterSave(); + $this->assertRewrite( + $this->scopeConfig->getValue(ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX), + [ + 'entity_type' => ProductUrlRewriteGenerator::ENTITY_TYPE, + 'entity_id' => $productId, + 'store_id' => $this->defaultStoreId, + ] + ); + $this->assertRewrite( + $newSuffix, + [ + 'entity_type' => ProductUrlRewriteGenerator::ENTITY_TYPE, + 'entity_id' => $productId, + 'store_id' => $website->getStoreIds(), + ] + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @magentoConfigFixture default_store catalog/seo/product_url_suffix .html_default + * + * @return void + */ + public function testSaveDefaultScopeWithOverrideStoreScope(): void + { + $productId = (int)$this->productRepository->get('simple2')->getId(); + $newSuffix = 'some_test_value'; + $this->model->addData([ + 'path' => ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX, + 'value' => $newSuffix, + ]); + $this->model->afterSave(); + $this->assertRewrite( + '.html_default', + [ + 'entity_type' => ProductUrlRewriteGenerator::ENTITY_TYPE, + 'entity_id' => $productId, + 'store_id' => $this->defaultStoreId, + ] + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category.php + * + * @return void + */ + public function testSaveCategorySuffix(): void + { + $this->model->addData(['path' => CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, 'value' => null]); + $this->model->afterSave(); + $this->assertRewrite('.html', ['entity_type' => CategoryUrlRewriteGenerator::ENTITY_TYPE]); + $this->checkIsCacheInvalidated(); + } + + /** + * @return void + */ + public function testDeleteCategorySuffix(): void + { + $this->model->addData( + ['path' => CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, 'value' => 'test_value'] + ); + $this->model->afterDeleteCommit(); + $this->checkIsCacheInvalidated(); + } + + /** + * Check that provided cache types are invalidated + * + * @param array $cacheTypes + * @return void + */ + private function checkIsCacheInvalidated( + array $cacheTypes = [Block::TYPE_IDENTIFIER, Collection::TYPE_IDENTIFIER] + ): void { + $types = $this->typeList->getTypes(); + + foreach ($cacheTypes as $type) { + $this->assertNotNull($types[$type]); + $this->assertEquals(0, $types[$type]->getStatus()); + } + } + + /** + * Assert url rewrite rewrite + * + * @param string $expectedSuffix + * @param array $data + * @return void + */ + private function assertRewrite(string $expectedSuffix, array $data) + { + $rewrite = $this->urlFinder->findOneByData($data); + $this->assertNotNull($rewrite); + $this->assertTrue( + substr($rewrite->getRequestPath(), -strlen($expectedSuffix)) === $expectedSuffix, + 'The url rewrite suffix does not match expected value' + ); + } +} From ae96ab2081185de784b282408afa7d84a28c9635 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Thu, 24 Dec 2020 13:10:32 +0200 Subject: [PATCH 080/112] MC-40078: Create automated test for: "Create attribute without attribute code" --- .../Product/Attribute/RepositoryTest.php | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/RepositoryTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/RepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/RepositoryTest.php new file mode 100644 index 0000000000000..94a5b52b03c5f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/RepositoryTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductAttributeInterfaceFactory; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; +use Magento\Framework\Exception\InputException; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks product attribute save behaviour. + * + * @see \Magento\Catalog\Model\Product\Attribute\Repository + * + * @magentoDbIsolation enabled + */ +class RepositoryTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Repository */ + private $repository; + + /** @var ProductAttributeInterfaceFactory */ + private $attributeFactory; + + /** @var ProductAttributeInterface */ + private $createdAttribute; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->repository = $this->objectManager->get(Repository::class); + $this->attributeFactory = $this->objectManager->get(ProductAttributeInterfaceFactory::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + if ($this->createdAttribute instanceof ProductAttributeInterface) { + $this->repository->delete($this->createdAttribute); + } + + parent::tearDown(); + } + + /** + * @return void + */ + public function testSaveWithoutAttributeCode(): void + { + $this->createdAttribute = $this->saveAttributeWithData( + $this->hydrateData(['frontend_label' => 'Boolean Attribute']) + ); + $this->assertEquals('boolean_attribute', $this->createdAttribute->getAttributeCode()); + } + + /** + * @return void + */ + public function testSaveWithoutAttributeAndInvalidLabelCode(): void + { + $this->createdAttribute = $this->saveAttributeWithData($this->hydrateData(['frontend_label' => '/$&!/'])); + $this->assertStringStartsWith('attr_', $this->createdAttribute->getAttributeCode()); + } + + /** + * @dataProvider errorProvider + * + * @param string $fieldName + * @param string $fieldValue + * @return void + */ + public function testSaveWithInvalidCode(string $fieldName, string $fieldValue): void + { + $this->expectExceptionObject(InputException::invalidFieldValue($fieldName, $fieldValue)); + $this->createdAttribute = $this->saveAttributeWithData($this->hydrateData([$fieldName => $fieldValue])); + } + + /** + * @return array + */ + public function errorProvider():array + { + return [ + 'with_invalid_attribute_code' => [ + 'field_name' => 'attribute_code', + 'field_value' => '****', + ], + 'with_invalid_frontend_input' => [ + 'field_name' => 'frontend_input', + 'field_value' => 'invalid_input', + ], + ]; + } + + /** + * Save product attribute with data + * + * @param array $data + * @return ProductAttributeInterface + */ + private function saveAttributeWithData(array $data): ProductAttributeInterface + { + $attribute = $this->attributeFactory->create(); + $attribute->addData($data); + + return $this->repository->save($attribute); + } + + /** + * Hydrate data + * + * @param array $data + * @return array + */ + private function hydrateData(array $data): array + { + $defaultData = [ + 'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID, + 'is_global' => ScopedAttributeInterface::SCOPE_GLOBAL, + 'frontend_input' => 'boolean', + 'frontend_label' => 'default label', + ]; + + return array_merge($defaultData, $data); + } +} From 06ca9b27abc155a844511d1a02fba8af13ac6dc2 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Thu, 24 Dec 2020 13:14:29 +0200 Subject: [PATCH 081/112] MC-39600: Create automated test for: "Change category/product url rewrite suffix in configurations" --- .../System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php index 6553dffb04df6..9979e8cd6ea68 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/SuffixTest.php @@ -275,7 +275,7 @@ private function checkIsCacheInvalidated( * @param array $data * @return void */ - private function assertRewrite(string $expectedSuffix, array $data) + private function assertRewrite(string $expectedSuffix, array $data): void { $rewrite = $this->urlFinder->findOneByData($data); $this->assertNotNull($rewrite); From 772d1785ca60fd9a3c2c36f9779b14a01e3bc300 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 24 Dec 2020 16:49:14 +0200 Subject: [PATCH 082/112] MC-36683: [Cloud]Generating companies... Could not save company --- setup/performance-toolkit/config/di.xml | 2 + .../Mail/Template/TransportBuilderMock.php | 28 ++++++++++++ .../Framework/Mail/TransportInterfaceMock.php | 45 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 setup/src/Magento/Setup/Framework/Mail/Template/TransportBuilderMock.php create mode 100644 setup/src/Magento/Setup/Framework/Mail/TransportInterfaceMock.php diff --git a/setup/performance-toolkit/config/di.xml b/setup/performance-toolkit/config/di.xml index 0b1175b0cd94c..a293edcb216af 100644 --- a/setup/performance-toolkit/config/di.xml +++ b/setup/performance-toolkit/config/di.xml @@ -6,4 +6,6 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <preference for="Magento\Framework\Mail\TransportInterface" type="Magento\Setup\Framework\Mail\TransportInterfaceMock"/> + <preference for="Magento\Framework\Mail\Template\TransportBuilder" type="Magento\Setup\Framework\Mail\Template\TransportBuilderMock"/> </config> diff --git a/setup/src/Magento/Setup/Framework/Mail/Template/TransportBuilderMock.php b/setup/src/Magento/Setup/Framework/Mail/Template/TransportBuilderMock.php new file mode 100644 index 0000000000000..2791487c37ba5 --- /dev/null +++ b/setup/src/Magento/Setup/Framework/Mail/Template/TransportBuilderMock.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Setup\Framework\Mail\Template; + +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Setup\Framework\Mail\TransportInterfaceMock; + +/** + * Mock for mail template transport builder. + */ +class TransportBuilderMock extends TransportBuilder +{ + /** + * @inheritDoc + */ + public function getTransport() + { + $this->prepareMessage(); + $this->reset(); + + return new TransportInterfaceMock($this->message); + } +} diff --git a/setup/src/Magento/Setup/Framework/Mail/TransportInterfaceMock.php b/setup/src/Magento/Setup/Framework/Mail/TransportInterfaceMock.php new file mode 100644 index 0000000000000..64abddc053504 --- /dev/null +++ b/setup/src/Magento/Setup/Framework/Mail/TransportInterfaceMock.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Setup\Framework\Mail; + +use Magento\Framework\Mail\EmailMessageInterface; +use Magento\Framework\Mail\TransportInterface; + +/** + * Mock for mail transport. + */ +class TransportInterfaceMock implements TransportInterface +{ + /** + * @var EmailMessageInterface|null + */ + private $message; + + /** + * @param EmailMessageInterface|null $message + */ + public function __construct($message = null) + { + $this->message = $message; + } + + /** + * @inheritDoc + */ + public function sendMessage() + { + } + + /** + * @inheritDoc + */ + public function getMessage() + { + return $this->message; + } +} From fe4b27b89a4e4527a34d4fceb7d690ca1b51c3e6 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Thu, 24 Dec 2020 17:04:17 +0200 Subject: [PATCH 083/112] MC-40078: Create automated test for: "Create attribute without attribute code" --- .../Catalog/Model/Product/Attribute/RepositoryTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/RepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/RepositoryTest.php index 94a5b52b03c5f..7430ebf72f8fb 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/RepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/RepositoryTest.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Api\Data\ProductAttributeInterfaceFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Setup\CategorySetup; use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; use Magento\Framework\Exception\InputException; @@ -28,7 +29,7 @@ class RepositoryTest extends TestCase /** @var ObjectManagerInterface */ private $objectManager; - /** @var Repository */ + /** @var ProductAttributeRepositoryInterface */ private $repository; /** @var ProductAttributeInterfaceFactory */ @@ -45,7 +46,7 @@ protected function setUp(): void parent::setUp(); $this->objectManager = Bootstrap::getObjectManager(); - $this->repository = $this->objectManager->get(Repository::class); + $this->repository = $this->objectManager->get(ProductAttributeRepositoryInterface::class); $this->attributeFactory = $this->objectManager->get(ProductAttributeInterfaceFactory::class); } @@ -97,7 +98,7 @@ public function testSaveWithInvalidCode(string $fieldName, string $fieldValue): /** * @return array */ - public function errorProvider():array + public function errorProvider(): array { return [ 'with_invalid_attribute_code' => [ From 2c80277103eab5b9c497d6c6ef32e6103ef70b32 Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Thu, 24 Dec 2020 18:19:13 +0200 Subject: [PATCH 084/112] MC-39711: Create automated test for: "Filter products in admin grid" --- .../AddQuantityFilterToCollectionTest.php | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityFilterToCollectionTest.php diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityFilterToCollectionTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityFilterToCollectionTest.php new file mode 100644 index 0000000000000..f0d87f06514f1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityFilterToCollectionTest.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Ui\DataProvider\Product; + +use Magento\Framework\App\RequestInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Framework\View\Element\UiComponentInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks that the product quantity filter is working correctly + * + * @magentoAppArea adminhtml + */ +class AddQuantityFilterToCollectionTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var UiComponentFactory */ + private $componentFactory; + + /** @var RequestInterface */ + private $request; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->request = $this->objectManager->get(RequestInterface::class); + $this->componentFactory = $this->objectManager->get(UiComponentFactory::class); + } + + /** + * @dataProvider quantityFilterProvider + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * @param array $filter + * @param array $expectedProducts + * @return void + */ + public function testQuantityFilter(array $filter, array $expectedProducts): void + { + $this->request->setParams([ContextInterface::FILTER_VAR => $filter]); + $dataProviderData = $this->getComponentProvidedData('product_listing'); + $actualProducts = array_column($dataProviderData['items'], 'sku'); + $this->assertEquals($expectedProducts, $actualProducts, 'Expected products do not match actual products!'); + } + + /** + * Data provider for testQuantityFilter + * + * @return array + */ + public function quantityFilterProvider(): array + { + return [ + 'from' => [ + 'filter' => [ + 'qty' => [ + 'from' => 100, + ], + ], + 'expected_products' => [ + 'simple1', + 'simple3', + ], + ], + 'to' => [ + 'filter' => [ + 'qty' => [ + 'to' => 100, + ], + ], + 'expected_products' => [ + 'simple1', + 'simple2', + ], + ], + 'both' => [ + 'filter' => [ + 'qty' => [ + 'from' => 60, + 'to' => 130, + ], + ], + 'expected_products' => [ + 'simple1', + ], + ], + ]; + } + + /** + * Call prepare method in the child components + * + * @param UiComponentInterface $component + * @return void + */ + private function prepareChildComponents(UiComponentInterface $component): void + { + foreach ($component->getChildComponents() as $child) { + $this->prepareChildComponents($child); + } + + $component->prepare(); + } + + /** + * Get component provided data + * + * @param string $namespace + * @return array + */ + private function getComponentProvidedData(string $namespace): array + { + $component = $this->componentFactory->create($namespace); + $this->prepareChildComponents($component); + + return $component->getContext()->getDataProvider()->getData(); + } +} From 57e23c6fdcc2c4232f72fe2d5361c82b47a7a7d4 Mon Sep 17 00:00:00 2001 From: Sergiy Vasiutynskyi <s.vasiutynskyi@atwix.com> Date: Fri, 25 Dec 2020 13:32:35 +0200 Subject: [PATCH 085/112] Updated value of CliIndexerReindexActionGroup action for failed test --- .../Test/ProductAvailableAfterEnablingSubCategoriesTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml index b5980cf977791..f7e35f1503a12 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml @@ -50,7 +50,7 @@ <!--Run re-index task--> <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value="cataloginventory_stock"/> + <argument name="indices" value="cataloginventory_stock catalog_product_price"/> </actionGroup> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/> From 9ca51cd1f92bc058a112f02fe85f6f4838261ba2 Mon Sep 17 00:00:00 2001 From: Sergiy Vasiutynskyi <s.vasiutynskyi@atwix.com> Date: Fri, 25 Dec 2020 17:03:44 +0200 Subject: [PATCH 086/112] Updated value of CliIndexerReindexActionGroup action for failed test --- .../Test/ProductAvailableAfterEnablingSubCategoriesTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml index f7e35f1503a12..654dc727b24ca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml @@ -50,7 +50,7 @@ <!--Run re-index task--> <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex"> - <argument name="indices" value="cataloginventory_stock catalog_product_price"/> + <argument name="indices" value="catalog_product_price"/> </actionGroup> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/> From f543cedd3c02ee717e23c22b833e2717566217a6 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 28 Dec 2020 10:55:29 +0200 Subject: [PATCH 087/112] MC-39575: Create automated test for "Apply visual swatch attribute filter on layered navigation" --- .../Configurable/Listing/ConfigurableTest.php | 115 ++++++++++++++++++ .../configurable_product_with_images.php | 41 +++++++ ...figurable_product_with_images_rollback.php | 15 +++ 3 files changed, 171 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/ConfigurableTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/ConfigurableTest.php new file mode 100644 index 0000000000000..f2d254af85c94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/ConfigurableTest.php @@ -0,0 +1,115 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Block\Product\Renderer\Configurable\Listing; + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Module\Manager; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Swatches\Block\Product\Renderer\Listing\Configurable; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for configurable products options block with swatch attribute. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class ConfigurableTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var Configurable + */ + private $block; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @inheritdoc + */ + public static function setUpBeforeClass(): void + { + $objectManager = Bootstrap::getObjectManager(); + /** @var Manager $moduleManager */ + $moduleManager = $objectManager->get(Manager::class); + if (!$moduleManager->isEnabled('Magento_Catalog')) { + self::markTestSkipped('Magento_Catalog module disabled.'); + } + } + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->serializer = $this->objectManager->get(SerializerInterface::class); + $this->productAttributeRepository = $this->objectManager->get(ProductAttributeRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Configurable::class); + $this->request = $this->objectManager->get(RequestInterface::class); + $this->request->clearParams(); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->request->clearParams(); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_with_images.php + * @return void + */ + public function testPreSelectedGalleryConfig(): void + { + $product = $this->productRepository->get('configurable'); + $this->block->setProduct($product); + $configurableAttribute = $this->productAttributeRepository->get('visual_swatch_attribute'); + $this->request->setQueryValue('visual_swatch_attribute', $configurableAttribute->getOptions()[1]->getValue()); + $jsonConfig = $this->serializer->unserialize($this->block->getJsonConfig()); + $this->assertArrayHasKey('preSelectedGallery', $jsonConfig); + $this->assertStringEndsWith('/m/a/magento_image.jpg', $jsonConfig['preSelectedGallery']['large']); + $this->assertStringEndsWith('/m/a/magento_image.jpg', $jsonConfig['preSelectedGallery']['medium']); + $this->assertStringEndsWith('/m/a/magento_image.jpg', $jsonConfig['preSelectedGallery']['small']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images.php new file mode 100644 index 0000000000000..096674c5e9620 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ProductFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture( + 'Magento/Catalog/_files/product_image.php' +); +Resolver::getInstance()->requireDataFixture( + 'Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php' +); + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$images = ['magento_image.jpg', 'magento_small_image.jpg', 'magento_thumbnail.jpg']; +foreach (range(1, 3) as $index) { + $product = $productRepository->get('simple_option_' . $index); + $product->setImage('/m/a/' . $images[$index - 1]) + ->setSmallImage('/m/a/' . $images[$index - 1]) + ->setThumbnail('/m/a/' . $images[$index - 1]) + ->setData('media_gallery', ['images' => [ + [ + 'file' => '/m/a/' . $images[$index - 1], + 'position' => 1, + 'label' => 'Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image', + ], + ]]) + ->setCanSaveCustomOptions(true) + ->save(); +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images_rollback.php new file mode 100644 index 0000000000000..c201b258c5341 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images_rollback.php @@ -0,0 +1,15 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture( + 'Magento/Swatches/_files/configurable_product_visual_swatch_attribute_rollback.php' +); +Resolver::getInstance()->requireDataFixture( + 'Magento/Catalog/_files/product_image_rollback.php' +); From a68e58e59a5bf0bd30b0e5b254a867e6d4d9f3bd Mon Sep 17 00:00:00 2001 From: mastiuhin-olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 28 Dec 2020 11:45:41 +0200 Subject: [PATCH 088/112] MC-39878: Invoice for an order that contains only one configurable product is not generated correctly --- .../Magento/Sales/Model/Order/Invoice.php | 5 ++ .../Magento/Sales/Model/Order/InvoiceTest.php | 69 +++++++++++++++++-- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Invoice.php b/app/code/Magento/Sales/Model/Order/Invoice.php index 14dd0b14ac1f3..50bbb3083a9ea 100644 --- a/app/code/Magento/Sales/Model/Order/Invoice.php +++ b/app/code/Magento/Sales/Model/Order/Invoice.php @@ -679,6 +679,11 @@ public function register() public function isLast() { foreach ($this->getAllItems() as $item) { + $orderItem = $item->getOrderItem(); + if ($orderItem->isDummy()) { + continue; + } + if (!$item->isLast()) { return false; } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/InvoiceTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/InvoiceTest.php index 8abec6ac6d734..64d5cdb037343 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/InvoiceTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/InvoiceTest.php @@ -3,20 +3,57 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Model\Order; -class InvoiceTest extends \PHPUnit\Framework\TestCase +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Sales\Model\ResourceModel\Order\Collection as OrderCollection; +use Magento\Sales\Api\InvoiceManagementInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; + +/** + * Invoice model test. + */ +class InvoiceTest extends TestCase { /** - * @var \Magento\Sales\Model\ResourceModel\Order\Collection + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var OrderCollection + */ + private $collection; + + /** + * @var InvoiceManagementInterface + */ + private $invoiceManagement; + + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var SearchCriteriaBuilder */ - private $_collection; + private $searchCriteriaBuilder; + /** + * @inheritDoc + */ protected function setUp(): void { - $this->_collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Sales\Model\ResourceModel\Order\Collection::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->collection = $this->objectManager->create(OrderCollection::class); + $this->invoiceManagement = $this->objectManager->get(InvoiceManagementInterface::class); + $this->orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); + $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); } /** @@ -27,9 +64,27 @@ public function testOrderTotalItemCount() $expectedResult = [['total_item_count' => 1]]; $actualResult = []; /** @var \Magento\Sales\Model\Order $order */ - foreach ($this->_collection->getItems() as $order) { + foreach ($this->collection->getItems() as $order) { $actualResult[] = ['total_item_count' => $order->getData('total_item_count')]; } $this->assertEquals($expectedResult, $actualResult); } + + /** + * Test order with exactly one configurable. + * + * @return void + * @magentoDataFixture Magento/Sales/_files/order_configurable_product.php + */ + public function testLastInvoiceWithConfigurable(): void + { + $searchCriteria = $this->searchCriteriaBuilder->addFilter('increment_id', '100000001') + ->create(); + $orders = $this->orderRepository->getList($searchCriteria); + $orders = $orders->getItems(); + $order = array_shift($orders); + $invoice = $this->invoiceManagement->prepareInvoice($order); + + self::assertEquals($invoice->isLast(), true); + } } From 6293b241f6469dcac784808905fae564142ce2d8 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transogtgroup.com> Date: Mon, 28 Dec 2020 11:59:10 +0200 Subject: [PATCH 089/112] MC-39864: [Magento Cloud] - Tax Miscalculation --- .../Model/Order/Creditmemo/Total/TaxTest.php | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php index 7c9d249124a9a..f6d7c6fdba60a 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php @@ -192,7 +192,7 @@ public function collectDataProvider() 'base_shipping_amount' => 30, 'tax_amount' => 0.82, 'base_tax_amount' => 0.82, - 'invoice' => new MagentoObject( + 'invoice' => $this->createInvoiceMock( [ 'tax_amount' => 24.33, 'base_tax_amount' => 24.33, @@ -283,7 +283,7 @@ public function collectDataProvider() 'base_shipping_amount' => 30, 'tax_amount' => 0.82 * $currencyRatio, 'base_tax_amount' => 0.82, - 'invoice' => new MagentoObject( + 'invoice' => $this->createInvoiceMock( [ 'tax_amount' => 24.33 * $currencyRatio, 'base_tax_amount' => 24.33, @@ -360,7 +360,7 @@ public function collectDataProvider() 'base_shipping_amount' => 30, 'tax_amount' => 1.65, 'base_tax_amount' => 1.65, - 'invoice' => new MagentoObject( + 'invoice' => $this->createInvoiceMock( [ 'tax_amount' => 11.14, 'base_tax_amount' => 11.14, @@ -438,7 +438,7 @@ public function collectDataProvider() 'base_shipping_amount' => 0, 'tax_amount' => 0.82, 'base_tax_amount' => 0.82, - 'invoice' => new MagentoObject( + 'invoice' => $this->createInvoiceMock( [ 'tax_amount' => 16.09, 'base_tax_amount' => 16.09, @@ -587,7 +587,7 @@ public function collectDataProvider() 'base_grand_total' => 60.82, 'tax_amount' => 0.82, 'base_tax_amount' => 0.82, - 'invoice' => new MagentoObject( + 'invoice' => $this->createInvoiceMock( [ 'tax_amount' => 16.09, 'base_tax_amount' => 16.09, @@ -779,4 +779,40 @@ protected function getCreditmemoItem($creditmemoItemData) $creditmemoItem->setData('qty', $creditmemoItemData['qty']); return $creditmemoItem; } + + /** + * Create invoice mock object + * + * @param array $data + * @return MockObject|Invoice + */ + private function createInvoiceMock(array $data): MockObject + { + /** @var MockObject|Invoice $invoice */ + $invoice = $this->getMockBuilder(Invoice::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->addMethods(['getBaseShippingDiscountTaxCompensationAmount']) + ->onlyMethods([ + 'getTaxAmount', + 'getBaseTaxAmount', + 'getShippingTaxAmount', + 'getBaseShippingTaxAmount', + 'getShippingDiscountTaxCompensationAmount' + ]) + ->getMock(); + + $invoice->method('getTaxAmount')->willReturn($data['tax_amount'] ?? 0); + $invoice->method('getBaseTaxAmount')->willReturn($data['base_tax_amount'] ?? 0); + $invoice->method('getShippingTaxAmount')->willReturn($data['shipping_tax_amount'] ?? 0); + $invoice->method('getBaseShippingTaxAmount')->willReturn($data['base_shipping_tax_amount'] ?? 0); + $invoice->method('getShippingDiscountTaxCompensationAmount') + ->willReturn($data['shipping_discount_tax_compensation_amount'] ?? 0); + $invoice->method('getBaseShippingDiscountTaxCompensationAmount') + ->willReturn($data['base_shipping_discount_tax_compensation_amount'] ?? 0); + + return $invoice; + } } From 7491d77c7b8de8a29c5d118796a67f3cb0dcfd8e Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 28 Dec 2020 13:48:51 +0200 Subject: [PATCH 090/112] MC-39575: Create automated test for "Apply visual swatch attribute filter on layered navigation" --- .../Configurable/Listing/ConfigurableTest.php | 11 ----------- .../_files/configurable_product_with_images.php | 14 +++++++------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/ConfigurableTest.php index f2d254af85c94..58475ea879094 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/ConfigurableTest.php @@ -83,17 +83,6 @@ protected function setUp(): void $this->productRepository->cleanCache(); $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Configurable::class); $this->request = $this->objectManager->get(RequestInterface::class); - $this->request->clearParams(); - } - - /** - * @inheritdoc - */ - protected function tearDown(): void - { - $this->request->clearParams(); - - parent::tearDown(); } /** diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images.php index 096674c5e9620..f2bcbc27dc01a 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_with_images.php @@ -7,7 +7,6 @@ use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\ProductFactory; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Workaround\Override\Fixture\Resolver; @@ -21,15 +20,16 @@ $objectManager = Bootstrap::getObjectManager(); /** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(ProductRepositoryInterface::class); +$configurableProduct = $productRepository->get('configurable'); +$children = $configurableProduct->getTypeInstance()->getUsedProducts($configurableProduct); $images = ['magento_image.jpg', 'magento_small_image.jpg', 'magento_thumbnail.jpg']; -foreach (range(1, 3) as $index) { - $product = $productRepository->get('simple_option_' . $index); - $product->setImage('/m/a/' . $images[$index - 1]) - ->setSmallImage('/m/a/' . $images[$index - 1]) - ->setThumbnail('/m/a/' . $images[$index - 1]) +foreach ($children as $index => $product) { + $product->setImage('/m/a/' . $images[$index]) + ->setSmallImage('/m/a/' . $images[$index]) + ->setThumbnail('/m/a/' . $images[$index]) ->setData('media_gallery', ['images' => [ [ - 'file' => '/m/a/' . $images[$index - 1], + 'file' => '/m/a/' . $images[$index], 'position' => 1, 'label' => 'Image Alt Text', 'disabled' => 0, From 9e4001b88bd20b2503dc6845a6eae715aa440f4e Mon Sep 17 00:00:00 2001 From: engcom-Kilo <mikola.malevanec@transoftgroup.com> Date: Mon, 28 Dec 2020 16:54:28 +0200 Subject: [PATCH 091/112] MC-39189: Exception on Admin Customers page when website is deleted. --- .../Component/Listing/Column/Confirmation.php | 34 +++-- .../Listing/Column/ConfirmationTest.php | 127 ++++++++++++++++++ 2 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Ui/Component/Listing/Column/ConfirmationTest.php diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/Confirmation.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/Confirmation.php index 26cac677ccd10..6215909a1fbee 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Column/Confirmation.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/Confirmation.php @@ -6,6 +6,7 @@ namespace Magento\Customer\Ui\Component\Listing\Column; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\Column; @@ -28,7 +29,7 @@ class Confirmation extends Column * @param ScopeConfigInterface $scopeConfig @deprecated * @param array $components * @param array $data - * @param AccountConfirmation $accountConfirmation + * @param AccountConfirmation|null $accountConfirmation * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( @@ -65,13 +66,7 @@ public function prepareDataSource(array $dataSource) */ private function getFieldLabel(array $item) { - $isConfirmationRequired = $this->accountConfirmation->isConfirmationRequired( - $item['website_id'][0] ?? null, - $item[$item['id_field_name']], - $item['email'] - ); - - if ($isConfirmationRequired) { + if ($this->getIsConfirmationRequired($item)) { if ($item[$this->getData('name')] === null) { return __('Confirmed'); } @@ -79,4 +74,27 @@ private function getFieldLabel(array $item) } return __('Confirmation Not Required'); } + + /** + * Retrieve is confirmation required flag for customer considering requested website may not exist. + * + * @param array $customer + * @return bool + */ + private function getIsConfirmationRequired(array $customer): bool + { + try { + return $this->accountConfirmation->isConfirmationRequired( + $customer['website_id'][0] ?? null, + $customer[$customer['id_field_name']], + $customer['email'] + ); + } catch (NoSuchEntityException $e) { + return $this->accountConfirmation->isConfirmationRequired( + null, + $customer[$customer['id_field_name']], + $customer['email'] + ); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Ui/Component/Listing/Column/ConfirmationTest.php b/dev/tests/integration/testsuite/Magento/Customer/Ui/Component/Listing/Column/ConfirmationTest.php new file mode 100644 index 0000000000000..24fe443c8c796 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Ui/Component/Listing/Column/ConfirmationTest.php @@ -0,0 +1,127 @@ +<?php + +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Ui\Component\Listing\Column; + +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for \Magento\Customer\Ui\Component\Listing\Column\Confirmation. + */ +class ConfirmationTest extends TestCase +{ + /** + * Test subject. + * + * @var Confirmation + */ + private $confirmation; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->confirmation = Bootstrap::getObjectManager()->create( + Confirmation::class, + [ + 'components' => [], + 'data' => ['name' => 'confirmation'], + ] + ); + } + + /** + * Verify Confirmation::prepareDataSource() won't throw exception in case requested website doesn't exist. + * + * @param array $customerDataSource + * @param array $expectedResult + * @magentoConfigFixture base_website customer/create_account/confirm 1 + * @dataProvider customersDataProvider + * + * @return void + */ + public function testPrepareDataSource(array $customerDataSource, array $expectedResult): void + { + $result = $this->confirmation->prepareDataSource($customerDataSource); + + self::assertEquals($expectedResult, $result); + } + + /** + * CustomerDataSource data provider. + * + * @return array + */ + public function customersDataProvider(): array + { + return [ + [ + 'customerDataSource' => [ + 'data' => [ + 'items' => [ + [ + 'id_field_name' => 'entity_id', + 'entity_id' => '1', + 'name' => 'John Doe', + 'email' => 'john.doe@example.com', + 'group_id' => ['1'], + 'created_at' => '2020-12-28 07:05:50', + 'website_id' => ['1'], + 'confirmation' => false, + 'created_in' => 'Default Store View', + ], + [ + 'id_field_name' => 'entity_id', + 'entity_id' => '2', + 'name' => 'Jane Doe', + 'email' => 'jane.doe@example.com', + 'group_id' => ['1'], + 'created_at' => '2020-12-28 07:06:17', + 'website_id' => ['999999999'], + 'confirmation' => null, + 'created_in' => 'CustomStoreViewWhichDoesNotExistAnymore', + ], + ], + 'totalRecords' => 2, + ], + ], + 'expectedResult' => [ + 'data' => [ + 'items' => [ + [ + 'id_field_name' => 'entity_id', + 'entity_id' => '1', + 'name' => 'John Doe', + 'email' => 'john.doe@example.com', + 'group_id' => ['1'], + 'created_at' => '2020-12-28 07:05:50', + 'website_id' => ['1'], + 'confirmation' => __('Confirmation Required'), + 'created_in' => 'Default Store View', + ], + [ + 'id_field_name' => 'entity_id', + 'entity_id' => '2', + 'name' => 'Jane Doe', + 'email' => 'jane.doe@example.com', + 'group_id' => ['1'], + 'created_at' => '2020-12-28 07:06:17', + 'website_id' => ['999999999'], + 'confirmation' => __('Confirmed'), + 'created_in' => 'CustomStoreViewWhichDoesNotExistAnymore', + ], + ], + 'totalRecords' => 2, + ], + ], + ], + ]; + } +} From 640adcdfe746f3d9acf5c6bd7afa24dd2b42a1bb Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 29 Dec 2020 11:15:27 +0200 Subject: [PATCH 092/112] MC-39598: Create automated test for: "Assign/Unassign product to websites via API" --- .../Api/ProductWebsiteLinkRepositoryTest.php | 113 ++++++++++++++++++ .../ProductWebsiteLinkRepositoryTest.php | 72 +++++++++++ 2 files changed, 185 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/ProductWebsiteLinkRepositoryTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php new file mode 100644 index 0000000000000..13218450e5054 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Api; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Webapi\Rest\Request; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Tests to check products to websites assigning. + * + * @see \Magento\Catalog\Model\ProductWebsiteLinkRepository + * + * @magentoAppIsolation enabled + */ +class ProductWebsiteLinkRepositoryTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogProductWebsiteLinkRepositoryV1'; + const SERVICE_VERSION = 'V1'; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var WebsiteRepositoryInterface */ + private $websiteRepository; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testSaveWebsiteLinkWithoutWebsiteId(): void + { + $serviceInfo = $this->fillServiceInfo('/V1/products/:sku/websites', Request::HTTP_METHOD_POST, 'Save'); + $requestData = ['productWebsiteLink' => ['sku' => 'simple2']]; + $this->expectException(\Exception::class); + $this->expectErrorMessage((string)__('There are not websites for assign to product')); + $this->_webApiCall($serviceInfo, $requestData); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testSaveWebsiteLinkWithUnexistingWebsiteId(): void + { + $unexistingWebsiteId = 8932568989; + $serviceInfo = $this->fillServiceInfo('/V1/products/:sku/websites', Request::HTTP_METHOD_POST, 'Save'); + $requestData = ['productWebsiteLink' => ['sku' => 'simple2', 'websiteId' => $unexistingWebsiteId]]; + $this->expectException(\Exception::class); + $this->expectExceptionMessageMatches('/Could not assign product \\\"%1\\\" to websites \\\"%2\\\"/'); + $this->_webApiCall($serviceInfo, $requestData); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_with_two_websites.php + * + * @return void + */ + public function testDeleteWebsiteLink(): void + { + $productSku = 'unique-simple-azaza'; + $websiteId = (int)$this->websiteRepository->get('second_website')->getId(); + $resourcePath = sprintf('/V1/products/%s/websites/%u', $productSku, $websiteId); + $serviceInfo = $this->fillServiceInfo($resourcePath, Request::HTTP_METHOD_DELETE, 'Delete'); + $this->_webApiCall($serviceInfo); + $product = $this->productRepository->get($productSku, false, null, true); + $this->assertNotContains($websiteId, $product->getWebsiteIds()); + } + + /** + * Fill service information + * + * @param string $resourcePath + * @param string $httpMethod + * @param string $operation + * @return array + */ + private function fillServiceInfo(string $resourcePath, string $httpMethod, string $operation): array + { + return [ + 'rest' => ['resourcePath' => $resourcePath, 'httpMethod' => $httpMethod], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . $operation, + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductWebsiteLinkRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductWebsiteLinkRepositoryTest.php new file mode 100644 index 0000000000000..a4441c9480b25 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductWebsiteLinkRepositoryTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model; + +use Magento\Catalog\Api\Data\ProductWebsiteLinkInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\ProductWebsiteLinkRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests to check products to websites assigning. + * + * @see \Magento\Catalog\Model\ProductWebsiteLinkRepository + * + * @magentoAppIsolation enabled + */ +class ProductWebsiteLinkRepositoryTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductWebsiteLinkRepositoryInterface */ + private $productWebsiteLinkRepository; + + /** @var ProductWebsiteLinkInterfaceFactory */ + private $productWebsiteLinkFactory; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var WebsiteRepositoryInterface */ + private $websiteRepository; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productWebsiteLinkRepository = $this->objectManager->get(ProductWebsiteLinkRepositoryInterface::class); + $this->productWebsiteLinkFactory = $this->objectManager->get(ProductWebsiteLinkInterfaceFactory::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_with_two_websites.php + * + * @return void + */ + public function testDelete(): void + { + $this->markTestSkipped('Blocked by MC-40250'); + $productWebsiteLink = $this->productWebsiteLinkFactory->create(); + $productWebsiteLink->setSku('unique-simple-azaza'); + $productWebsiteLink->setWebsiteId(1); + $this->productWebsiteLinkRepository->delete($productWebsiteLink); + $product = $this->productRepository->get('unique-simple-azaza', false, null, true); + $this->assertEquals([$this->websiteRepository->get('second_website')->getId()], $product->getWebsiteIds()); + } +} From 0073eb38787d4c52981bcb2981f335a73f6bf3b0 Mon Sep 17 00:00:00 2001 From: rostyslav-hymon <rostyslav.hymon@transoftgroup.com> Date: Tue, 29 Dec 2020 13:48:23 +0200 Subject: [PATCH 093/112] MC-39276: Can not change value for Ship Bundle Item attribute if it was moved to different group --- .../Product/Form/Modifier/BundlePanelTest.php | 166 ++++++++++++++++++ .../Product/Form/Modifier/BundlePanel.php | 17 +- 2 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePanelTest.php diff --git a/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePanelTest.php b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePanelTest.php new file mode 100644 index 0000000000000..51563d319dfc8 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePanelTest.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Test\Unit\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Bundle\Model\Product\Attribute\Source\Shipment\Type as ShipmentType; +use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel; +use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePrice; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Framework\Stdlib\ArrayManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for bundle panel + */ +class BundlePanelTest extends TestCase +{ + /** + * @var UrlInterface|MockObject + */ + private $urlBuilder; + + /** + * @var ShipmentType|MockObject + */ + private $shipmentType; + + /** + * @var LocatorInterface|MockObject + */ + private $locatorMock; + + /** + * @var ProductInterface|MockObject + */ + private $productMock; + + /** + * @var ArrayManager|MockObject + */ + private $arrayManagerMock; + + /** + * @var BundlePanel + */ + private $bundlePanelModel; + + /** + * @return void + */ + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + $this->arrayManagerMock = $this->getMockBuilder(ArrayManager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->arrayManagerMock->expects($this->any()) + ->method('get') + ->willReturn([]); + $this->urlBuilder = $this->getMockBuilder(UrlInterface::class) + ->getMockForAbstractClass(); + $this->shipmentType = $this->getMockBuilder(ShipmentType::class) + ->getMockForAbstractClass(); + $this->productMock = $this->getMockBuilder(ProductInterface::class) + ->addMethods(['getStoreId']) + ->getMockForAbstractClass(); + $this->productMock->method('getId') + ->willReturn(true); + $this->productMock->method('getStoreId') + ->willReturn(0); + $this->locatorMock = $this->getMockBuilder(LocatorInterface::class) + ->onlyMethods(['getProduct']) + ->getMockForAbstractClass(); + $this->locatorMock->method('getProduct') + ->willReturn($this->productMock); + + $this->bundlePanelModel = $this->objectManager->getObject( + BundlePanel::class, + [ + 'locator' => $this->locatorMock, + 'urlBuilder' => $this->urlBuilder, + 'shipmentType' => $this->shipmentType, + 'arrayManager' => $this->arrayManagerMock, + ] + ); + } + + /** + * Test for modify meta + * + * @param string $shipmentTypePath + * @param string $dataScope + * + * @return void + * @dataProvider getDataModifyMeta + */ + public function testModifyMeta(string $shipmentTypePath, string $dataScope): void + { + $sourceMeta = [ + 'bundle-items' => [ + 'children' => [ + BundlePrice::CODE_PRICE_TYPE => [] + ] + ] + ]; + $this->arrayManagerMock->method('findPath') + ->willReturnMap( + [ + [ + BundlePanel::CODE_SHIPMENT_TYPE, + [], + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $shipmentTypePath + ], + ] + ); + $this->arrayManagerMock->method('merge') + ->willReturn([]); + $this->arrayManagerMock->method('remove') + ->willReturn([]); + $this->arrayManagerMock->method('set') + ->willReturn([]); + $this->arrayManagerMock->expects($this->at(12)) + ->method('merge') + ->with( + $shipmentTypePath . BundlePanel::META_CONFIG_PATH, + [], + [ + 'dataScope' => $dataScope, + 'validation' => [ + 'required-entry' => false + ] + ] + ); + $this->bundlePanelModel->modifyMeta($sourceMeta); + } + + /** + * Data provider for modify meta test + * + * @return string[][] + */ + public function getDataModifyMeta(): array + { + return [ + [ + 'bundle-items/children', + 'data.product.shipment_type' + ], + [ + 'someAttrGroup/children', + 'shipment_type' + ], + ]; + } +} diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php index 5ff9e674acad9..01b113def9243 100644 --- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php +++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php @@ -252,16 +252,19 @@ public function modifyData(array $data) */ private function modifyShipmentType(array $meta) { + $actualPath = $this->arrayManager->findPath( + static::CODE_SHIPMENT_TYPE, + $meta, + null, + 'children' + ); + $meta = $this->arrayManager->merge( - $this->arrayManager->findPath( - static::CODE_SHIPMENT_TYPE, - $meta, - null, - 'children' - ) . static::META_CONFIG_PATH, + $actualPath . static::META_CONFIG_PATH, $meta, [ - 'dataScope' => 'data.product.shipment_type', + 'dataScope' => stripos($actualPath, self::CODE_BUNDLE_DATA) === 0 + ? 'data.product.shipment_type' : 'shipment_type', 'validation' => [ 'required-entry' => false ] From 04fd1acd367593c5080075d31602ca7dd68b70ca Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 29 Dec 2020 14:15:22 +0200 Subject: [PATCH 094/112] MC-39598: Create automated test for: "Assign/Unassign product to websites via API" --- .../Api/ProductWebsiteLinkRepositoryTest.php | 21 ++++--------------- .../ProductWebsiteLinkRepositoryTest.php | 17 ++++++++++++++- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php index 13218450e5054..fbca54acc9e0b 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php @@ -46,20 +46,6 @@ protected function setUp(): void $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); } - /** - * @magentoApiDataFixture Magento/Catalog/_files/second_product_simple.php - * - * @return void - */ - public function testSaveWebsiteLinkWithoutWebsiteId(): void - { - $serviceInfo = $this->fillServiceInfo('/V1/products/:sku/websites', Request::HTTP_METHOD_POST, 'Save'); - $requestData = ['productWebsiteLink' => ['sku' => 'simple2']]; - $this->expectException(\Exception::class); - $this->expectErrorMessage((string)__('There are not websites for assign to product')); - $this->_webApiCall($serviceInfo, $requestData); - } - /** * @magentoApiDataFixture Magento/Catalog/_files/second_product_simple.php * @@ -67,11 +53,12 @@ public function testSaveWebsiteLinkWithoutWebsiteId(): void */ public function testSaveWebsiteLinkWithUnexistingWebsiteId(): void { + $pattern = '/(Could\\snot\\sassign\\sproduct)+([\\s\\S]*)(to\\swebsites)+([\\s\\S]*)/'; $unexistingWebsiteId = 8932568989; $serviceInfo = $this->fillServiceInfo('/V1/products/:sku/websites', Request::HTTP_METHOD_POST, 'Save'); $requestData = ['productWebsiteLink' => ['sku' => 'simple2', 'websiteId' => $unexistingWebsiteId]]; $this->expectException(\Exception::class); - $this->expectExceptionMessageMatches('/Could not assign product \\\"%1\\\" to websites \\\"%2\\\"/'); + $this->expectExceptionMessageMatches($pattern); $this->_webApiCall($serviceInfo, $requestData); } @@ -85,8 +72,8 @@ public function testDeleteWebsiteLink(): void $productSku = 'unique-simple-azaza'; $websiteId = (int)$this->websiteRepository->get('second_website')->getId(); $resourcePath = sprintf('/V1/products/%s/websites/%u', $productSku, $websiteId); - $serviceInfo = $this->fillServiceInfo($resourcePath, Request::HTTP_METHOD_DELETE, 'Delete'); - $this->_webApiCall($serviceInfo); + $serviceInfo = $this->fillServiceInfo($resourcePath, Request::HTTP_METHOD_DELETE, 'DeleteById'); + $this->_webApiCall($serviceInfo, ['sku' => $productSku, 'websiteId' => $websiteId]); $product = $this->productRepository->get($productSku, false, null, true); $this->assertNotContains($websiteId, $product->getWebsiteIds()); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductWebsiteLinkRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductWebsiteLinkRepositoryTest.php index a4441c9480b25..9ae327036971b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductWebsiteLinkRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductWebsiteLinkRepositoryTest.php @@ -10,6 +10,7 @@ use Magento\Catalog\Api\Data\ProductWebsiteLinkInterfaceFactory; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Api\ProductWebsiteLinkRepositoryInterface; +use Magento\Framework\Exception\InputException; use Magento\Framework\ObjectManagerInterface; use Magento\Store\Api\WebsiteRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -55,7 +56,21 @@ protected function setUp(): void } /** - * @magentoApiDataFixture Magento/Catalog/_files/product_with_two_websites.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testSaveWithoutWebsiteId(): void + { + $productWebsiteLink = $this->productWebsiteLinkFactory->create(); + $productWebsiteLink->setSku('unique-simple-azaza'); + $this->expectException(InputException::class); + $this->expectErrorMessage((string)__('There are not websites for assign to product')); + $this->productWebsiteLinkRepository->save($productWebsiteLink); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_with_two_websites.php * * @return void */ From 9b0799b75169c7df4e85a647187e15512f71befc Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 29 Dec 2020 19:07:46 +0200 Subject: [PATCH 095/112] MC-39598: Create automated test for: "Assign/Unassign product to websites via API" --- .../Api/ProductWebsiteLinkRepositoryTest.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php index fbca54acc9e0b..0bbed6387ae57 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductWebsiteLinkRepositoryTest.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Api; +use Magento\Catalog\Model\ProductWebsiteLink; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Webapi\Rest\Request; use Magento\Store\Api\WebsiteRepositoryInterface; @@ -56,7 +57,12 @@ public function testSaveWebsiteLinkWithUnexistingWebsiteId(): void $pattern = '/(Could\\snot\\sassign\\sproduct)+([\\s\\S]*)(to\\swebsites)+([\\s\\S]*)/'; $unexistingWebsiteId = 8932568989; $serviceInfo = $this->fillServiceInfo('/V1/products/:sku/websites', Request::HTTP_METHOD_POST, 'Save'); - $requestData = ['productWebsiteLink' => ['sku' => 'simple2', 'websiteId' => $unexistingWebsiteId]]; + $requestData = [ + 'productWebsiteLink' => [ + ProductWebsiteLink::KEY_SKU => 'simple2', + ProductWebsiteLink::WEBSITE_ID => $unexistingWebsiteId, + ], + ]; $this->expectException(\Exception::class); $this->expectExceptionMessageMatches($pattern); $this->_webApiCall($serviceInfo, $requestData); @@ -73,7 +79,10 @@ public function testDeleteWebsiteLink(): void $websiteId = (int)$this->websiteRepository->get('second_website')->getId(); $resourcePath = sprintf('/V1/products/%s/websites/%u', $productSku, $websiteId); $serviceInfo = $this->fillServiceInfo($resourcePath, Request::HTTP_METHOD_DELETE, 'DeleteById'); - $this->_webApiCall($serviceInfo, ['sku' => $productSku, 'websiteId' => $websiteId]); + $this->_webApiCall( + $serviceInfo, + [ProductWebsiteLink::KEY_SKU => $productSku, ProductWebsiteLink::WEBSITE_ID => $websiteId] + ); $product = $this->productRepository->get($productSku, false, null, true); $this->assertNotContains($websiteId, $product->getWebsiteIds()); } From 2740e08869dfc8b0ea69bd20400c693e82c73a1b Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 30 Dec 2020 13:01:01 +0200 Subject: [PATCH 096/112] MC-40068: Create automated test for: "Try to delete root category by API call" --- .../Catalog/Api/CategoryRepositoryTest.php | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php index 5623edca62b9a..8e0aff81cc3b8 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php @@ -184,22 +184,49 @@ public function testDeleteNoSuchEntityException() /** * @dataProvider deleteSystemOrRootDataProvider + * + * @param int $categoryId + * @param string $exceptionMsg + * @return void */ - public function testDeleteSystemOrRoot() + public function testDeleteSystemOrRoot(int $categoryId, string $exceptionMsg): void { $this->expectException(\Exception::class); + $this->expectExceptionMessage($exceptionMsg); - $this->deleteCategory($this->modelId); + $this->deleteCategory($categoryId); } - public function deleteSystemOrRootDataProvider() + /** + * @return array + */ + public function deleteSystemOrRootDataProvider(): array { return [ - [Category::TREE_ROOT_ID], - [2] //Default root category + 'system_category' => [ + 'category_id' => Category::TREE_ROOT_ID, + 'exception_message' => $this->buildExceptionMessage(Category::TREE_ROOT_ID), + ], + 'root_category' => [ + 'category_id' => 2, + 'exception_message' => $this->buildExceptionMessage(2), + ], ]; } + /** + * Build response error message + * + * @param int $categoryId + * @return string + */ + private function buildExceptionMessage(int $categoryId): string + { + $translatedMsg = (string)__('Cannot delete category with id %1'); + + return sprintf('{"message":"%s","parameters":["%u"]}', $translatedMsg, $categoryId); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/category.php */ From 14f3af27d67db2f807fa413fe013b15ce48857f9 Mon Sep 17 00:00:00 2001 From: SmVladyslav <vlatame.tsg@gmail.com> Date: Wed, 30 Dec 2020 14:12:45 +0200 Subject: [PATCH 097/112] MC-39470: issue with dynamicRows component --- .../base/web/js/dynamic-rows/dynamic-rows.js | 18 +++++++--- .../base/js/dynamic-rows/dynamic-rows.test.js | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js index 0ac35df78e001..45dfaa40f87df 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js @@ -620,15 +620,12 @@ define([ * @param {Array} data */ parsePagesData: function (data) { - var pages; - this.relatedData = this.deleteProperty ? _.filter(data, function (elem) { return elem && elem[this.deleteProperty] !== this.deleteValue; }, this) : data; - pages = Math.ceil(this.relatedData.length / this.pageSize) || 1; - this.pages(pages); + this._updatePagesQuantity(); }, /** @@ -885,6 +882,18 @@ define([ this._sort(); }, + /** + * Update number of pages. + * + * @private + * @return void + */ + _updatePagesQuantity: function () { + var pages = Math.ceil(this.relatedData.length / this.pageSize) || 1; + + this.pages(pages); + }, + /** * Reduce the number of pages * @@ -960,6 +969,7 @@ define([ reload: function () { this.clear(); this.initChildren(false, true); + this._updatePagesQuantity(); /* After change page size need to check existing current page */ this._reducePages(); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js index fc60fbb0bdccc..1101770b0faa2 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js @@ -171,5 +171,38 @@ define([ }; expect(JSON.stringify(model.labels())).toEqual(JSON.stringify(result)); }); + + it('Check _updatePagesQuantity method call.', function () { + model._updatePagesQuantity = jasmine.createSpy(); + + model.reload(); + + expect(model._updatePagesQuantity).toHaveBeenCalled(); + }); + + it('Check number of pages is updated after reloading dynamic-rows.', function () { + model.pageSize = 1; + model.relatedData = [ + { + name: 'first' + }, + { + name: 'second' + }, + { + name: 'third' + } + ]; + + model.reload(); + expect(model.pages()).toEqual(3); + + model.currentPage(3); + model.pageSize = 2; + + model.reload(); + expect(model.pages()).toEqual(2); + expect(model.currentPage()).toEqual(2); + }); }); }); From 10fe506d9489296e9c4b27666d3c687c07b8ba57 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 30 Dec 2020 14:16:56 +0200 Subject: [PATCH 098/112] MC-39702: Create automated test for: "Visual swatch on the product page" --- .../Product/Renderer/ConfigurableTest.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php index c900d276c7864..111d0aa08f7f5 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php @@ -148,6 +148,32 @@ public function testGetJsonSwatchConfigUsedProductImage(): void ); } + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @return void + */ + public function testGetJsonSwatchConfigUsedWithSwatchImageType(): void + { + $this->updateAttributeUseProductImageFlag(); + $this->updateProductImage('simple_option_2', '/m/a/magento_image.jpg'); + $this->setSwatchImage('simple_option_2', '/m/a/magento_image.jpg'); + $expectedOptions = $this->getDefaultOptionsList(); + $expectedOptions['option 2']['value'] = $this->imageUrlBuilder->getUrl( + '/m/a/magento_image.jpg', + 'swatch_image_base' + ); + $expectedOptions['option 2']['thumb'] = $this->imageUrlBuilder->getUrl( + '/m/a/magento_image.jpg', + 'swatch_thumb_base' + ); + $this->assertOptionsData( + $this->serializer->unserialize($this->block->getJsonSwatchConfig()), + $expectedOptions, + ['swatch_input_type' => 'visual', 'use_product_image_for_swatch' => 1] + ); + } + /** * @magentoDataFixture Magento/Swatches/_files/configurable_product_with_visual_swatch_attribute.php * @return void @@ -249,4 +275,18 @@ private function updateProductImage(string $sku, string $imageName): void ->setCanSaveCustomOptions(true); $this->productResource->save($product); } + + /** + * Set swatch image for a Product. + * + * @param string $sku + * @param string $imageName + * @return void + */ + private function setSwatchImage(string $sku, string $imageName): void + { + $product = $this->productRepository->get($sku); + $product->setSwatchImage($imageName)->save($product); + } } + From bd329b681eeab9b667f2fcbe4a1aa67d23d4b3e3 Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Wed, 30 Dec 2020 14:46:51 +0200 Subject: [PATCH 099/112] MC-40030: Wrong currency sign in Credit Memo grid with Website scope for Price --- app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php b/app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php index 4ffb6f98447c7..cc323730f14b4 100644 --- a/app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php +++ b/app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php @@ -10,6 +10,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Listing\Columns\Column; use Magento\Framework\Pricing\PriceCurrencyInterface; @@ -77,8 +78,10 @@ public function prepareDataSource(array $dataSource) foreach ($dataSource['data']['items'] as & $item) { $currencyCode = isset($item['base_currency_code']) ? $item['base_currency_code'] : null; if (!$currencyCode) { + $storeId = isset($item['store_id']) && (int)$item['store_id'] !== 0 ? $item['store_id'] : + $this->context->getFilterParam('store_id', Store::DEFAULT_STORE_ID); $store = $this->storeManager->getStore( - $this->context->getFilterParam('store_id', \Magento\Store\Model\Store::DEFAULT_STORE_ID) + $storeId ); $currencyCode = $store->getBaseCurrency()->getCurrencyCode(); } From 16826bd19568a4df154f2fc787e2a89d99a2e46a Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Wed, 30 Dec 2020 15:01:03 +0200 Subject: [PATCH 100/112] MC-39687: Create automated test for: "Set backorders "Allow Qty Below 0" and check indexers" --- .../Model/Config/Backend/BackordersTest.php | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Config/Backend/BackordersTest.php diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Config/Backend/BackordersTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Config/Backend/BackordersTest.php new file mode 100644 index 0000000000000..279e45fc868cc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Config/Backend/BackordersTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Model\Config\Backend; + +use Magento\CatalogInventory\Model\Configuration; +use Magento\CatalogInventory\Model\Indexer\Stock\Processor; +use Magento\CatalogInventory\Model\Stock; +use Magento\Config\Model\Config\BackendFactory; +use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\Indexer\StateInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks that the backorders config backend model is working correctly + */ +class BackordersTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Backorders */ + private $backorders; + + /** @var BackendFactory */ + private $backendFactory; + + /** @var MutableScopeConfigInterface */ + private $mutableConfig; + + /** @var Processor */ + private $stockIndexerProcessor; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->backendFactory = $this->objectManager->create(BackendFactory::class); + $this->backorders = $this->backendFactory->create(Backorders::class, [ + 'data' => [ + 'path' => Configuration::XML_PATH_BACKORDERS, + ] + ]); + $this->mutableConfig = $this->objectManager->get(MutableScopeConfigInterface::class); + $this->stockIndexerProcessor = $this->objectManager->get(Processor::class); + } + + /** + * @dataProvider afterSaveDataProvider + * @param int $value + * @param int $currentValue + * @param string $expectedIndexerStatus + * @magentoDbIsolation disabled + * @return void + */ + public function testAfterSave(int $value, int $currentValue, string $expectedIndexerStatus): void + { + $this->stockIndexerProcessor->reindexAll(); + $this->mutableConfig->setValue(Configuration::XML_PATH_BACKORDERS, $currentValue); + $this->backorders->setValue((string)$value); + $this->backorders->afterSave(); + + $this->assertEquals($expectedIndexerStatus, $this->stockIndexerProcessor->getIndexer()->getStatus()); + } + + /** + * Data provider for testAfterSave + * + * @return array + */ + public function afterSaveDataProvider(): array + { + return [ + 'set_backorders' => [ + 'value' => Stock::BACKORDERS_YES_NONOTIFY, + 'current_value' => Stock::BACKORDERS_NO, + 'expected_indexer_status' => StateInterface::STATUS_INVALID, + ], + 'unset_backorders' => [ + 'value' => Stock::BACKORDERS_NO, + 'current_value' => Stock::BACKORDERS_YES_NONOTIFY, + 'expected_indexer_status' => StateInterface::STATUS_INVALID, + ], + 'same_backorders' => [ + 'value' => Stock::BACKORDERS_YES_NONOTIFY, + 'current_value' => Stock::BACKORDERS_YES_NONOTIFY, + 'expected_indexer_status' => StateInterface::STATUS_VALID, + ], + ]; + } +} From 048ada376695b0df9b5d63ab4a321a8d7302477c Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 30 Dec 2020 15:24:22 +0200 Subject: [PATCH 101/112] MC-40068: Create automated test for: "Try to delete root category by API call" --- .../testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php index 8e0aff81cc3b8..2e8eedf96b0f8 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php @@ -224,7 +224,9 @@ private function buildExceptionMessage(int $categoryId): string { $translatedMsg = (string)__('Cannot delete category with id %1'); - return sprintf('{"message":"%s","parameters":["%u"]}', $translatedMsg, $categoryId); + return TESTS_WEB_API_ADAPTER === self::ADAPTER_REST + ? sprintf('{"message":"%s","parameters":["%u"]}', $translatedMsg, $categoryId) + : $translatedMsg; } /** From c50560110e4eb940f07566ad66915c5ee58de60b Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 30 Dec 2020 15:37:34 +0200 Subject: [PATCH 102/112] MC-39702: Create automated test for: "Visual swatch on the category page" --- .../Product/Renderer/ConfigurableTest.php | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php index 111d0aa08f7f5..5c5b1399caed8 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/ConfigurableTest.php @@ -156,8 +156,7 @@ public function testGetJsonSwatchConfigUsedProductImage(): void public function testGetJsonSwatchConfigUsedWithSwatchImageType(): void { $this->updateAttributeUseProductImageFlag(); - $this->updateProductImage('simple_option_2', '/m/a/magento_image.jpg'); - $this->setSwatchImage('simple_option_2', '/m/a/magento_image.jpg'); + $this->updateProductImage('simple_option_2', '/m/a/magento_image.jpg', ['swatch_image']); $expectedOptions = $this->getDefaultOptionsList(); $expectedOptions['option 2']['value'] = $this->imageUrlBuilder->getUrl( '/m/a/magento_image.jpg', @@ -249,15 +248,16 @@ private function updateAttributeUseProductImageFlag(): void * * @param string $sku * @param string $imageName + * @param array $imageRoles * @return void */ - private function updateProductImage(string $sku, string $imageName): void - { + private function updateProductImage( + string $sku, + string $imageName, + array $imageRoles = ['image', 'small_image', 'thumbnail'] + ): void { $product = $this->productRepository->get($sku); $product->setStoreId(Store::DEFAULT_STORE_ID) - ->setImage($imageName) - ->setSmallImage($imageName) - ->setThumbnail($imageName) ->setData( 'media_gallery', [ @@ -273,20 +273,10 @@ private function updateProductImage(string $sku, string $imageName): void ] ) ->setCanSaveCustomOptions(true); - $this->productResource->save($product); - } + foreach ($imageRoles as $role) { + $product->setData($role, $imageName); + } - /** - * Set swatch image for a Product. - * - * @param string $sku - * @param string $imageName - * @return void - */ - private function setSwatchImage(string $sku, string $imageName): void - { - $product = $this->productRepository->get($sku); - $product->setSwatchImage($imageName)->save($product); + $this->productResource->save($product); } } - From 10272105edddaca5363e1e16ea033184b33dd009 Mon Sep 17 00:00:00 2001 From: Vasya Tsviklinskyi <tsviklinskyi@gmail.com> Date: Thu, 31 Dec 2020 09:47:11 +0200 Subject: [PATCH 103/112] MC-40030: Wrong currency sign in Credit Memo grid with Website scope for Price --- .../Ui/Component/Listing/Column/PriceTest.php | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Ui/Component/Listing/Column/PriceTest.php b/app/code/Magento/Sales/Test/Unit/Ui/Component/Listing/Column/PriceTest.php index 4a9061c3f3c5c..449ab230b568d 100644 --- a/app/code/Magento/Sales/Test/Unit/Ui/Component/Listing/Column/PriceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Ui/Component/Listing/Column/PriceTest.php @@ -17,6 +17,9 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +/** + * Contains tests for Price class + */ class PriceTest extends TestCase { /** @@ -34,6 +37,9 @@ class PriceTest extends TestCase */ private $storeManagerMock; + /** + * @inheritDoc + */ protected function setUp(): void { $objectManager = new ObjectManager($this); @@ -57,12 +63,20 @@ protected function setUp(): void } /** - * @param $hasCurrency - * @param $dataSource - * @param $currencyCode + * Test for prepareDataSource method + * + * @param bool $hasCurrency + * @param array $dataSource + * @param string $currencyCode + * @param int|null $expectedStoreId * @dataProvider testPrepareDataSourceDataProvider */ - public function testPrepareDataSource($hasCurrency, $dataSource, $currencyCode) + public function testPrepareDataSource( + bool $hasCurrency, + array $dataSource, + string $currencyCode, + ?int $expectedStoreId = null + ): void { $itemName = 'itemName'; $oldItemValue = 'oldItemValue'; @@ -79,6 +93,7 @@ public function testPrepareDataSource($hasCurrency, $dataSource, $currencyCode) ->willReturn($currencyCode); $this->storeManagerMock->expects($hasCurrency ? $this->never() : $this->once()) ->method('getStore') + ->with($expectedStoreId) ->willReturn($store); $store->expects($hasCurrency ? $this->never() : $this->once()) ->method('getBaseCurrency') @@ -98,7 +113,12 @@ public function testPrepareDataSource($hasCurrency, $dataSource, $currencyCode) $this->assertEquals($newItemValue, $dataSource['data']['items'][0][$itemName]); } - public function testPrepareDataSourceDataProvider() + /** + * Provider for testPrepareDataSource + * + * @return array + */ + public function testPrepareDataSourceDataProvider(): array { $dataSource1 = [ 'data' => [ @@ -119,9 +139,31 @@ public function testPrepareDataSourceDataProvider() ] ] ]; + $dataSource3 = [ + 'data' => [ + 'items' => [ + [ + 'itemName' => 'oldItemValue', + 'store_id' => '2' + ] + ] + ] + ]; + $dataSource4 = [ + 'data' => [ + 'items' => [ + [ + 'itemName' => 'oldItemValue', + 'store_id' => 'abc' + ] + ] + ] + ]; return [ [true, $dataSource1, 'US'], [false, $dataSource2, 'SAR'], + [false, $dataSource3, 'SAR', 2], + [false, $dataSource4, 'SAR'], ]; } } From 62bb82c0e93a8085f7a0d5f479ff225896c45385 Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@transoftgroup.com> Date: Thu, 31 Dec 2020 11:54:45 +0200 Subject: [PATCH 104/112] MC-30104: When using MysqlMQ messages are always set to complete even if exception occurs --- .../MessageQueue/Model/ResourceModel/Lock.php | 48 ++++---- .../TestModuleMysqlMq/Model/Processor.php | 25 +++- .../TestModuleMysqlMq/etc/communication.xml | 1 + .../Magento/TestModuleMysqlMq/etc/queue.xml | 3 + .../TestModuleMysqlMq/etc/queue_consumer.xml | 1 + .../TestModuleMysqlMq/etc/queue_publisher.xml | 3 + .../TestModuleMysqlMq/etc/queue_topology.xml | 1 + .../MessageQueue/Model/ConsumerTest.php | 109 ++++++++++++++++++ .../Model/ResourceModel/LockTest.php | 28 +++++ 9 files changed, 193 insertions(+), 26 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/MessageQueue/Model/ConsumerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/MessageQueue/Model/ResourceModel/LockTest.php diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/Lock.php b/app/code/Magento/MessageQueue/Model/ResourceModel/Lock.php index 16c02a7505664..00399e30e8b72 100644 --- a/app/code/Magento/MessageQueue/Model/ResourceModel/Lock.php +++ b/app/code/Magento/MessageQueue/Model/ResourceModel/Lock.php @@ -5,46 +5,52 @@ */ namespace Magento\MessageQueue\Model\ResourceModel; -use \Magento\Framework\MessageQueue\Lock\ReaderInterface; -use \Magento\Framework\MessageQueue\Lock\WriterInterface; +use DateInterval; +use DateTime; +use Magento\Framework\MessageQueue\Lock\ReaderInterface; +use Magento\Framework\MessageQueue\Lock\WriterInterface; +use Magento\Framework\MessageQueue\LockInterface; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\MessageQueue\Model\LockFactory; /** * Class Lock to handle database lock table db transactions. */ -class Lock extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb implements ReaderInterface, WriterInterface +class Lock extends AbstractDb implements ReaderInterface, WriterInterface { /**#@+ * Constants */ - const QUEUE_LOCK_TABLE = 'queue_lock'; + public const QUEUE_LOCK_TABLE = 'queue_lock'; /**#@-*/ /**#@-*/ private $dateTime; /** - * @var \Magento\MessageQueue\Model\LockFactory + * @var LockFactory */ private $lockFactory; /** - * @var integer + * @var int */ private $interval; /** * Initialize dependencies. * - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context + * @param Context $context * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime - * @param \Magento\MessageQueue\Model\LockFactory $lockFactory - * @param null $connectionName - * @param integer $interval + * @param LockFactory $lockFactory + * @param ?string $connectionName + * @param int $interval */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, + Context $context, \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, - \Magento\MessageQueue\Model\LockFactory $lockFactory, + LockFactory $lockFactory, $connectionName = null, $interval = 86400 ) { @@ -55,7 +61,7 @@ public function __construct( } /** - * {@inheritDoc} + * @inheritdoc */ protected function _construct() { @@ -63,9 +69,9 @@ protected function _construct() } /** - * {@inheritDoc} + * @inheritdoc */ - public function read(\Magento\Framework\MessageQueue\LockInterface $lock, $code) + public function read(LockInterface $lock, $code) { $object = $this->lockFactory->create(); $object->load($code, 'message_code'); @@ -75,23 +81,25 @@ public function read(\Magento\Framework\MessageQueue\LockInterface $lock, $code) } /** - * {@inheritDoc} + * @inheritdoc */ - public function saveLock(\Magento\Framework\MessageQueue\LockInterface $lock) + public function saveLock(LockInterface $lock) { $object = $this->lockFactory->create(); $object->setMessageCode($lock->getMessageCode()); $object->setCreatedAt($this->dateTime->gmtTimestamp()); $object->save(); + $lock->setId($object->getId()); + $lock->setCreatedAt($object->getCreatedAt()); } /** - * {@inheritDoc} + * @inheritdoc */ public function releaseOutdatedLocks() { - $date = (new \DateTime())->setTimestamp($this->dateTime->gmtTimestamp()); - $date->add(new \DateInterval('PT' . $this->interval . 'S')); + $date = (new DateTime())->setTimestamp($this->dateTime->gmtTimestamp()); + $date->add(new DateInterval('PT' . $this->interval . 'S')); $this->getConnection()->delete($this->getTable(self::QUEUE_LOCK_TABLE), ['created_at <= ?' => $date]); } } diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/Processor.php b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/Processor.php index fb6fd4c5c2802..3d2f722ccb60e 100644 --- a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/Processor.php +++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/Model/Processor.php @@ -5,13 +5,16 @@ */ namespace Magento\TestModuleMysqlMq\Model; +use LogicException; +use Magento\Framework\MessageQueue\ConnectionLostException; + /** * Test message processor is used by \Magento\MysqlMq\Model\PublisherConsumerTest */ class Processor { /** - * @param \Magento\TestModuleMysqlMq\Model\DataObject $message + * @param DataObject $message */ public function processMessage($message) { @@ -23,7 +26,7 @@ public function processMessage($message) } /** - * @param \Magento\TestModuleMysqlMq\Model\DataObject $message + * @param DataObject $message */ public function processObjectCreated($message) { @@ -35,7 +38,7 @@ public function processObjectCreated($message) } /** - * @param \Magento\TestModuleMysqlMq\Model\DataObject $message + * @param DataObject $message */ public function processCustomObjectCreated($message) { @@ -47,7 +50,7 @@ public function processCustomObjectCreated($message) } /** - * @param \Magento\TestModuleMysqlMq\Model\DataObject $message + * @param DataObject $message */ public function processObjectUpdated($message) { @@ -59,13 +62,23 @@ public function processObjectUpdated($message) } /** - * @param \Magento\TestModuleMysqlMq\Model\DataObject $message + * @param DataObject $message */ public function processMessageWithException($message) { file_put_contents($message->getOutputPath(), "Exception processing {$message->getEntityId()}"); - throw new \LogicException( + throw new LogicException( "Exception during message processing happened. Entity: {{$message->getEntityId()}}" ); } + + /** + * @throws ConnectionLostException + */ + public function processMessageWithConnectionException() + { + throw new ConnectionLostException( + "Connection exception during message processing happened." + ); + } } diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/communication.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/communication.xml index 4d6269dbb7920..1a5a5feb11324 100644 --- a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/communication.xml +++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/communication.xml @@ -7,6 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Communication/etc/communication.xsd"> <topic name="demo.exception" request="Magento\TestModuleMysqlMq\Model\DataObject"/> + <topic name="demo.connection.exception" request="Magento\TestModuleMysqlMq\Model\DataObject"/> <topic name="test.schema.defined.by.method" schema="Magento\TestModuleMysqlMq\Model\DataObjectRepository::delayedOperation" is_synchronous="false"/> <topic name="demo.object.created" request="Magento\TestModuleMysqlMq\Model\DataObject"/> <topic name="demo.object.updated" request="Magento\TestModuleMysqlMq\Model\DataObject"/> diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue.xml index 362237c0c5e62..c879b271c6651 100644 --- a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue.xml +++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue.xml @@ -9,6 +9,9 @@ <broker topic="demo.exception" type="db" exchange="magento"> <queue consumer="demoConsumerWithException" name="queue-exception" handler="Magento\TestModuleMysqlMq\Model\Processor::processMessageWithException"/> </broker> + <broker topic="demo.connection.exception" type="db" exchange="magento"> + <queue consumer="demoConsumerWithConnectionException" name="queue-connection-exception" handler="Magento\TestModuleMysqlMq\Model\Processor::processMessageWithConnectionException"/> + </broker> <broker topic="test.schema.defined.by.method" type="db" exchange="magento"> <queue consumer="delayedOperationConsumer" name="demo-queue-6" handler="Magento\TestModuleMysqlMq\Model\DataObjectRepository::delayedOperation"/> </broker> diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_consumer.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_consumer.xml index bb495a123a05d..6a3916a23b43b 100644 --- a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_consumer.xml +++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_consumer.xml @@ -10,5 +10,6 @@ <consumer name="demoConsumerQueueTwo" queue="queue-updated" connection="db" handler="Magento\TestModuleMysqlMq\Model\Processor::processObjectUpdated"/> <consumer name="demoConsumerQueueThree" queue="queue-custom-created" connection="db" handler="Magento\TestModuleMysqlMq\Model\Processor::processCustomObjectCreated"/> <consumer name="demoConsumerWithException" queue="queue-exception" connection="db" handler="Magento\TestModuleMysqlMq\Model\Processor::processMessageWithException"/> + <consumer name="demoConsumerWithConnectionException" queue="queue-connection-exception" connection="db" handler="Magento\TestModuleMysqlMq\Model\Processor::processMessageWithConnectionException"/> <consumer name="delayedOperationConsumer" queue="demo-queue-6" connection="db" handler="Magento\TestModuleMysqlMq\Model\DataObjectRepository::delayedOperation"/> </config> diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_publisher.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_publisher.xml index a665e10ef5f14..639503a936cb5 100644 --- a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_publisher.xml +++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_publisher.xml @@ -9,6 +9,9 @@ <publisher topic="demo.exception"> <connection name="db" exchange="magento"/> </publisher> + <publisher topic="demo.connection.exception"> + <connection name="db" exchange="magento"/> + </publisher> <publisher topic="test.schema.defined.by.method"> <connection name="db" exchange="magento"/> </publisher> diff --git a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_topology.xml b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_topology.xml index 2df5485ee3447..3612438c37f4a 100644 --- a/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_topology.xml +++ b/dev/tests/integration/_files/Magento/TestModuleMysqlMq/etc/queue_topology.xml @@ -9,6 +9,7 @@ <exchange name="magento" type="topic" connection="db"> <binding id="demo.exception.consumer" topic="demo.exception" destination="queue-exception" destinationType="queue"/> + <binding id="demo.connection.exception.consumer" topic="demo.connection.exception" destination="queue-connection-exception" destinationType="queue"/> <binding id="test.schema.defined.by.method" topic="test.schema.defined.by.method" destination="demo-queue-6" destinationType="queue"/> <binding id="demo.object.created" topic="demo.object.created" destination="queue-created" destinationType="queue"/> <binding id="demo.object.updated" topic="demo.object.updated" destination="queue-updated" destinationType="queue"/> diff --git a/dev/tests/integration/testsuite/Magento/MessageQueue/Model/ConsumerTest.php b/dev/tests/integration/testsuite/Magento/MessageQueue/Model/ConsumerTest.php new file mode 100644 index 0000000000000..a3515b07f1e0b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/MessageQueue/Model/ConsumerTest.php @@ -0,0 +1,109 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Model; + +use Magento\Framework\MessageQueue\Consumer; +use Magento\Framework\MessageQueue\ConsumerFactory; +use Magento\Framework\MessageQueue\EnvelopeFactory; +use Magento\Framework\MessageQueue\QueueInterface; +use Magento\MysqlMq\Model\QueueManagement; +use Magento\MysqlMq\Model\ResourceModel\Queue; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Tests the different cases of consumers running by Consumer processor + */ +class ConsumerTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Consumer + */ + private $model; + + /** + * @var Queue + */ + private $queueResource; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->objectManager = ObjectManager::getInstance(); + /** @var ConsumerFactory $factory */ + $factory = $this->objectManager->get(ConsumerFactory::class); + $this->model = $factory->get('demoConsumerWithConnectionException'); + $this->queueResource = $this->objectManager->get(Queue::class); + } + + /** + * Test if after connection exception and retry + * message doesn't have success status but still has status in progress + * + * @return void + */ + public function testRunWithException(): void + { + /** @var EnvelopeFactory $envelopFactory */ + $envelopFactory = $this->objectManager->get(EnvelopeFactory::class); + $messageBody = '{"name":"test"}'; + $topicName = 'demo.connection.exception'; + $queueName = 'queue-connection-exception'; + $envelope = $envelopFactory->create(['body' => $messageBody, 'properties' => ['topic_name' => $topicName]]); + /** @var QueueInterface $queue */ + $queue = $this->objectManager->create( + \Magento\MysqlMq\Model\Driver\Queue::class, + ['queueName' => $queueName] + ); + $queue->push($envelope); + $messages = $this->queueResource->getMessages($queueName, 1); + $envelope = $envelopFactory->create(['body' => $messageBody, 'properties' => $messages[0]]); + $this->model->process(1); + $queue->reject($envelope); + $this->model->process(1); + $message = $this->getLastMessage($queueName); + $this->assertEquals(QueueManagement::MESSAGE_STATUS_IN_PROGRESS, $message['status']); + } + + /** + * Return last message by queue name + * + * @param string $queueName + * @return array + */ + private function getLastMessage(string $queueName) + { + $connection = $this->queueResource->getConnection(); + $select = $connection->select() + ->from( + ['queue_message' => $this->queueResource->getTable('queue_message')], + [] + )->join( + ['queue_message_status' => $this->queueResource->getTable('queue_message_status')], + 'queue_message.id = queue_message_status.message_id', + [ + QueueManagement::MESSAGE_QUEUE_RELATION_ID => 'id', + QueueManagement::MESSAGE_STATUS => 'status', + ] + )->join( + ['queue' => $this->queueResource->getTable('queue')], + 'queue.id = queue_message_status.queue_id', + [QueueManagement::MESSAGE_QUEUE_NAME => 'name'] + )->where('queue.name = ?', $queueName) + ->order(['queue_message_status.id DESC']); + + return $connection->fetchRow($select); + } +} diff --git a/dev/tests/integration/testsuite/Magento/MessageQueue/Model/ResourceModel/LockTest.php b/dev/tests/integration/testsuite/Magento/MessageQueue/Model/ResourceModel/LockTest.php new file mode 100644 index 0000000000000..bede370db29c4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/MessageQueue/Model/ResourceModel/LockTest.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Model\ResourceModel; + +use Magento\Framework\MessageQueue\LockInterface; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Covers Lock resource model test cases + */ +class LockTest extends TestCase +{ + public function testSaveLock() + { + $objectManager = ObjectManager::getInstance(); + /** @var Lock $resourceModel */ + $resourceModel = $objectManager->get(Lock::class); + $lock = $objectManager->create(LockInterface::class); + $resourceModel->saveLock($lock); + self::assertNotEquals(null, $lock->getId()); + } +} From 79887430a29094e13dbdbc084a4ed8c06f19474e Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@transoftgroup.com> Date: Wed, 30 Dec 2020 13:33:06 +0200 Subject: [PATCH 105/112] MC-30127: Product Price is mismatch in invoice and invoice PDF magento for Bundle Product 2.2.1 --- .../Model/Sales/Order/Pdf/Items/Invoice.php | 152 +++++--- .../Sales/Order/Pdf/Items/InvoiceTest.php | 352 ++++++++++++++++++ 2 files changed, 460 insertions(+), 44 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTest.php diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php index 64e9f56dd65bc..26d0fd274014c 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php @@ -8,6 +8,7 @@ namespace Magento\Bundle\Model\Sales\Order\Pdf\Items; use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\DataObject; use Magento\Framework\Filesystem; use Magento\Framework\Filter\FilterManager; use Magento\Framework\Model\Context; @@ -69,34 +70,38 @@ public function __construct( } /** - * Draw item line + * Draw bundle product item line * * @return void - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function draw() { - $order = $this->getOrder(); - $item = $this->getItem(); - $pdf = $this->getPdf(); - $page = $this->getPage(); + $draw = $this->drawChildrenItems(); + $draw = $this->drawCustomOptions($draw); + $page = $this->getPdf()->drawLineBlocks($this->getPage(), $draw, ['table_header' => true]); + + $this->setPage($page); + } + + /** + * Draw bundle product children items + * + * @return array + */ + private function drawChildrenItems(): array + { $this->_setFontRegular(); - $items = $this->getChildren($item); $prevOptionId = ''; $drawItems = []; - - foreach ($items as $childItem) { - $line = []; - + $optionId = 0; + foreach ($this->getChildren($this->getItem()) as $childItem) { + $lines = []; + $index = array_key_last($lines) !== null ? array_key_last($lines) + 1 : 0; $attributes = $this->getSelectionAttributes($childItem); if (is_array($attributes)) { $optionId = $attributes['option_id']; - } else { - $optionId = 0; } if (!isset($drawItems[$optionId])) { @@ -104,15 +109,14 @@ public function draw() } if ($childItem->getOrderItem()->getParentItem() && $prevOptionId != $attributes['option_id']) { - $line[0] = [ + $lines[$index][] = [ 'font' => 'italic', 'text' => $this->string->split($attributes['option_label'], 45, true, true), 'feed' => 35, ]; - $drawItems[$optionId] = ['lines' => [$line], 'height' => 15]; - - $line = []; + $drawItems[$optionId] = ['height' => 15]; + $index++; $prevOptionId = $attributes['option_id']; } @@ -124,35 +128,97 @@ public function draw() $feed = 35; $name = $childItem->getName(); } - $line[] = ['text' => $this->string->split($name, 35, true, true), 'feed' => $feed]; + $lines[$index][] = ['text' => $this->string->split($name, 35, true, true), 'feed' => $feed]; - // draw SKUs - if (!$childItem->getOrderItem()->getParentItem()) { - $text = []; - foreach ($this->string->split($item->getSku(), 17) as $part) { - $text[] = $part; - } - $line[] = ['text' => $text, 'feed' => 255]; - } + $lines = $this->drawSkus($childItem, $lines); - // draw prices - if ($this->canShowPriceInfo($childItem)) { - $price = $order->formatPriceTxt($childItem->getPrice()); - $line[] = ['text' => $price, 'feed' => 395, 'font' => 'bold', 'align' => 'right']; - $line[] = ['text' => $childItem->getQty() * 1, 'feed' => 435, 'font' => 'bold']; + $lines = $this->drawPrices($childItem, $lines); + $drawItems[$optionId]['lines'] = $lines; + } - $tax = $order->formatPriceTxt($childItem->getTaxAmount()); - $line[] = ['text' => $tax, 'feed' => 495, 'font' => 'bold', 'align' => 'right']; + return $drawItems; + } - $row_total = $order->formatPriceTxt($childItem->getRowTotal()); - $line[] = ['text' => $row_total, 'feed' => 565, 'font' => 'bold', 'align' => 'right']; + /** + * Draw sku parts + * + * @param DataObject $childItem + * @param array $lines + * @return array + */ + private function drawSkus(DataObject $childItem, array $lines): array + { + $index = array_key_last($lines); + if (!$childItem->getOrderItem()->getParentItem()) { + $text = []; + foreach ($this->string->split($this->getItem()->getSku(), 17) as $part) { + $text[] = $part; } + $lines[$index][] = ['text' => $text, 'feed' => 255]; + } + + return $lines; + } - $drawItems[$optionId]['lines'][] = $line; + /** + * Draw prices for bundle product children items + * + * @param DataObject $childItem + * @param array $lines + * @return array + */ + private function drawPrices(DataObject $childItem, array $lines): array + { + $index = array_key_last($lines); + if ($this->canShowPriceInfo($childItem)) { + $lines[$index][] = ['text' => $childItem->getQty() * 1, 'feed' => 435, 'align' => 'right']; + + $tax = $this->getOrder()->formatPriceTxt($childItem->getTaxAmount()); + $lines[$index][] = ['text' => $tax, 'feed' => 495, 'font' => 'bold', 'align' => 'right']; + + $item = $this->getItem(); + $this->_item = $childItem; + $feedPrice = 380; + $feedSubtotal = $feedPrice + 185; + foreach ($this->getItemPricesForDisplay() as $priceData) { + if (isset($priceData['label'])) { + // draw Price label + $lines[$index][] = ['text' => $priceData['label'], 'feed' => $feedPrice, 'align' => 'right']; + // draw Subtotal label + $lines[$index][] = ['text' => $priceData['label'], 'feed' => $feedSubtotal, 'align' => 'right']; + $index++; + } + // draw Price + $lines[$index][] = [ + 'text' => $priceData['price'], + 'feed' => $feedPrice, + 'font' => 'bold', + 'align' => 'right', + ]; + // draw Subtotal + $lines[$index][] = [ + 'text' => $priceData['subtotal'], + 'feed' => $feedSubtotal, + 'font' => 'bold', + 'align' => 'right', + ]; + $index++; + } + $this->_item = $item; } - // custom options - $options = $item->getOrderItem()->getProductOptions(); + return $lines; + } + + /** + * Draw bundle product custom options + * + * @param array $draw + * @return array + */ + private function drawCustomOptions(array $draw): array + { + $options = $this->getItem()->getOrderItem()->getProductOptions(); if ($options && isset($options['options'])) { foreach ($options['options'] as $option) { $lines = []; @@ -180,12 +246,10 @@ public function draw() $lines[][] = ['text' => $text, 'feed' => 40]; } - $drawItems[] = ['lines' => $lines, 'height' => 15]; + $draw[] = ['lines' => $lines, 'height' => 15]; } } - $page = $pdf->drawLineBlocks($page, $drawItems, ['table_header' => true]); - - $this->setPage($page); + return $draw; } } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTest.php new file mode 100644 index 0000000000000..e93d231383820 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTest.php @@ -0,0 +1,352 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Test\Unit\Model\Sales\Order\Pdf\Items; + +use Magento\Bundle\Model\Sales\Order\Pdf\Items\Invoice; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\DataObject; +use Magento\Framework\Filesystem; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\StringUtils; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Pdf\Invoice as InvoicePdf; +use Magento\Tax\Helper\Data; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Zend_Pdf_Page; + +/** + * Covers bundle order item invoice print logic + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class InvoiceTest extends TestCase +{ + /** + * @var Invoice|MockObject + */ + private $model; + + /** + * @var Data|MockObject + */ + private $taxDataMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $contextMock = $this->createMock(Context::class); + $registryMock = $this->createMock(Registry::class); + $this->taxDataMock = $this->createMock(Data::class); + $directoryMock = $this->createMock(Filesystem\Directory\Read::class); + $directoryMock->expects($this->any())->method('getAbsolutePath')->willReturn(''); + $filesystemMock = $this->createMock(Filesystem::class); + $filesystemMock->expects($this->any())->method('getDirectoryRead')->willReturn($directoryMock); + $filterManagerMock = $this->createMock(FilterManager::class); + $stringUtilsMock = $this->createMock(StringUtils::class); + $stringUtilsMock->expects($this->any())->method('split')->willReturnArgument(0); + $resourceMock = $this->createMock(AbstractResource::class); + $collectionMock = $this->createMock(AbstractDb::class); + $serializerMock = $this->createMock(Json::class); + + $this->model = $this->getMockBuilder(Invoice::class) + ->setConstructorArgs( + [ + $contextMock, + $registryMock, + $this->taxDataMock, + $filesystemMock, + $filterManagerMock, + $stringUtilsMock, + $serializerMock, + $resourceMock, + $collectionMock, + [], + ] + ) + ->onlyMethods( + [ + '_setFontRegular', + 'getChildren', + 'isShipmentSeparately', + 'isChildCalculated', + 'getValueHtml', + 'getSelectionAttributes', + ] + ) + ->getMock(); + } + + /** + * @dataProvider invoiceDataProvider + * @param array $expected + * @param string $method + */ + public function testDrawPrice(array $expected, string $method): void + { + $this->taxDataMock->expects($this->any())->method($method)->willReturn(true); + $pageMock = $this->createMock(Zend_Pdf_Page::class); + $this->model->setPage($pageMock); + $pdfMock = $this->createMock(InvoicePdf::class); + $pdfMock->expects($this->any())->method('drawLineBlocks')->with( + $pageMock, + $expected, + ['table_header' => true] + )->willReturn($pageMock); + $this->model->setPdf($pdfMock); + + $this->prepareModel(); + $this->model->draw(); + } + + /** + * @return array[] + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function invoiceDataProvider(): array + { + return [ + 'display_both' => [ + 'expected' => [ + 1 => [ + 'height' => 15, + 'lines' => [ + [ + [ + 'text' => 'test option', + 'feed' => 35, + 'font' => 'italic', + + ], + ], + [ + [ + 'text' => 'Simple1', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 1.66, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => 'Excl. Tax:', + 'feed' => 380, + 'align' => 'right', + ], + [ + 'text' => 'Excl. Tax:', + 'feed' => 565, + 'align' => 'right', + ], + ], + [ + [ + 'text' => '10.00', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '20.00', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + [ + [ + 'text' => 'Incl. Tax:', + 'feed' => 380, + 'align' => 'right', + ], + [ + 'text' => 'Incl. Tax:', + 'feed' => 565, + 'align' => 'right', + ], + ], + [ + [ + 'text' => '10.83', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '21.66', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + ], + ], + ], + 'tax_mock_method' => 'displaySalesBothPrices', + ], + 'including_tax' => [ + 'expected' => [ + 1 => [ + 'height' => 15, + 'lines' => [ + [ + [ + 'text' => 'test option', + 'feed' => 35, + 'font' => 'italic', + + ], + ], + [ + [ + 'text' => 'Simple1', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 1.66, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '10.83', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '21.66', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + ], + ], + ], + 'tax_mock_method' => 'displaySalesPriceInclTax', + ], + 'excluding_tax' => [ + 'expected' => [ + 1 => [ + 'height' => 15, + 'lines' => [ + [ + [ + 'text' => 'test option', + 'feed' => 35, + 'font' => 'italic', + + ], + ], + [ + [ + 'text' => 'Simple1', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 1.66, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '10.00', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '20.00', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + ], + ], + ], + 'tax_mock_method' => 'displaySalesPriceExclTax', + ], + ]; + } + + /** + * Prepare invoice draw model for test execution + * + * @return void + */ + private function prepareModel(): void + { + $parentItem = new DataObject( + [ + 'sku' => 'bundle-simple', + 'name' => 'Bundle', + 'order_item' => new DataObject( + [ + 'product_options' => [], + ] + ), + ] + ); + $items = [ + new DataObject( + [ + 'name' => 'Simple1', + 'sku' => 'simple1', + 'price' => '10.00', + 'price_incl_tax' => '10.83', + 'row_total' => '20.00', + 'row_total_incl_tax' => '21.66', + 'qty' => '2', + 'tax_amount' => '1.66', + 'order_item' => new DataObject( + [ + 'parent_item' => $parentItem, + ] + ), + ] + ), + ]; + $orderMock = $this->createMock(Order::class); + + $this->model->expects($this->any())->method('getChildren')->willReturn($items); + $this->model->expects($this->any())->method('isShipmentSeparately')->willReturn(false); + $this->model->expects($this->any())->method('isChildCalculated')->willReturn(true); + $this->model->expects($this->any())->method('getValueHtml')->willReturn($items[0]->getName()); + $this->model->expects($this->any())->method('getSelectionAttributes')->willReturn( + ['option_id' => 1, 'option_label' => 'test option'] + ); + + $orderMock->expects($this->any())->method('formatPriceTxt')->willReturnArgument(0); + $this->model->setOrder($orderMock); + $this->model->setItem($parentItem); + } +} From 1ce3e1fe70b5c549ff519560dd7ada48f7a2034c Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@transoftgroup.com> Date: Thu, 31 Dec 2020 16:36:31 +0200 Subject: [PATCH 106/112] MC-30127: Product Price is mismatch in invoice and invoice PDF magento for Bundle Product 2.2.1 --- .../Model/Sales/Order/Pdf/Items/Invoice.php | 5 +- .../Sales/Order/Pdf/Items/InvoiceTest.php | 216 ++---------- .../Order/Pdf/Items/InvoiceTestProvider.php | 329 ++++++++++++++++++ 3 files changed, 355 insertions(+), 195 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTestProvider.php diff --git a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php index 26d0fd274014c..a7f0a70b45219 100644 --- a/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php +++ b/app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php @@ -96,8 +96,8 @@ private function drawChildrenItems(): array $prevOptionId = ''; $drawItems = []; $optionId = 0; + $lines = []; foreach ($this->getChildren($this->getItem()) as $childItem) { - $lines = []; $index = array_key_last($lines) !== null ? array_key_last($lines) + 1 : 0; $attributes = $this->getSelectionAttributes($childItem); if (is_array($attributes)) { @@ -115,7 +115,6 @@ private function drawChildrenItems(): array 'feed' => 35, ]; - $drawItems[$optionId] = ['height' => 15]; $index++; $prevOptionId = $attributes['option_id']; } @@ -133,8 +132,8 @@ private function drawChildrenItems(): array $lines = $this->drawSkus($childItem, $lines); $lines = $this->drawPrices($childItem, $lines); - $drawItems[$optionId]['lines'] = $lines; } + $drawItems[$optionId]['lines'] = $lines; return $drawItems; } diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTest.php index e93d231383820..e5bf94241dbd9 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTest.php @@ -88,7 +88,7 @@ protected function setUp(): void } /** - * @dataProvider invoiceDataProvider + * @dataProvider \Magento\Bundle\Test\Unit\Model\Sales\Order\Pdf\Items\InvoiceTestProvider::getData * @param array $expected * @param string $method */ @@ -109,195 +109,6 @@ public function testDrawPrice(array $expected, string $method): void $this->model->draw(); } - /** - * @return array[] - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function invoiceDataProvider(): array - { - return [ - 'display_both' => [ - 'expected' => [ - 1 => [ - 'height' => 15, - 'lines' => [ - [ - [ - 'text' => 'test option', - 'feed' => 35, - 'font' => 'italic', - - ], - ], - [ - [ - 'text' => 'Simple1', - 'feed' => 40, - ], - [ - 'text' => 2, - 'feed' => 435, - 'align' => 'right', - ], - [ - 'text' => 1.66, - 'feed' => 495, - 'font' => 'bold', - 'align' => 'right', - ], - [ - 'text' => 'Excl. Tax:', - 'feed' => 380, - 'align' => 'right', - ], - [ - 'text' => 'Excl. Tax:', - 'feed' => 565, - 'align' => 'right', - ], - ], - [ - [ - 'text' => '10.00', - 'feed' => 380, - 'font' => 'bold', - 'align' => 'right', - ], - [ - 'text' => '20.00', - 'feed' => 565, - 'font' => 'bold', - 'align' => 'right', - ], - ], - [ - [ - 'text' => 'Incl. Tax:', - 'feed' => 380, - 'align' => 'right', - ], - [ - 'text' => 'Incl. Tax:', - 'feed' => 565, - 'align' => 'right', - ], - ], - [ - [ - 'text' => '10.83', - 'feed' => 380, - 'font' => 'bold', - 'align' => 'right', - ], - [ - 'text' => '21.66', - 'feed' => 565, - 'font' => 'bold', - 'align' => 'right', - ], - ], - ], - ], - ], - 'tax_mock_method' => 'displaySalesBothPrices', - ], - 'including_tax' => [ - 'expected' => [ - 1 => [ - 'height' => 15, - 'lines' => [ - [ - [ - 'text' => 'test option', - 'feed' => 35, - 'font' => 'italic', - - ], - ], - [ - [ - 'text' => 'Simple1', - 'feed' => 40, - ], - [ - 'text' => 2, - 'feed' => 435, - 'align' => 'right', - ], - [ - 'text' => 1.66, - 'feed' => 495, - 'font' => 'bold', - 'align' => 'right', - ], - [ - 'text' => '10.83', - 'feed' => 380, - 'font' => 'bold', - 'align' => 'right', - ], - [ - 'text' => '21.66', - 'feed' => 565, - 'font' => 'bold', - 'align' => 'right', - ], - ], - ], - ], - ], - 'tax_mock_method' => 'displaySalesPriceInclTax', - ], - 'excluding_tax' => [ - 'expected' => [ - 1 => [ - 'height' => 15, - 'lines' => [ - [ - [ - 'text' => 'test option', - 'feed' => 35, - 'font' => 'italic', - - ], - ], - [ - [ - 'text' => 'Simple1', - 'feed' => 40, - ], - [ - 'text' => 2, - 'feed' => 435, - 'align' => 'right', - ], - [ - 'text' => 1.66, - 'feed' => 495, - 'font' => 'bold', - 'align' => 'right', - ], - [ - 'text' => '10.00', - 'feed' => 380, - 'font' => 'bold', - 'align' => 'right', - ], - [ - 'text' => '20.00', - 'feed' => 565, - 'font' => 'bold', - 'align' => 'right', - ], - ], - ], - ], - ], - 'tax_mock_method' => 'displaySalesPriceExclTax', - ], - ]; - } - /** * Prepare invoice draw model for test execution * @@ -334,16 +145,37 @@ private function prepareModel(): void ), ] ), + new DataObject( + [ + 'name' => 'Simple2', + 'sku' => 'simple2', + 'price' => '5.00', + 'price_incl_tax' => '5.41', + 'row_total' => '10.00', + 'row_total_incl_tax' => '10.83', + 'qty' => '2', + 'tax_amount' => '0.83', + 'order_item' => new DataObject( + [ + 'parent_item' => $parentItem, + ] + ), + ] + ), ]; $orderMock = $this->createMock(Order::class); $this->model->expects($this->any())->method('getChildren')->willReturn($items); $this->model->expects($this->any())->method('isShipmentSeparately')->willReturn(false); $this->model->expects($this->any())->method('isChildCalculated')->willReturn(true); - $this->model->expects($this->any())->method('getValueHtml')->willReturn($items[0]->getName()); - $this->model->expects($this->any())->method('getSelectionAttributes')->willReturn( + $this->model->expects($this->at(2))->method('getSelectionAttributes')->willReturn( ['option_id' => 1, 'option_label' => 'test option'] ); + $this->model->expects($this->at(3))->method('getValueHtml')->willReturn($items[0]->getName()); + $this->model->expects($this->at(5))->method('getSelectionAttributes')->willReturn( + ['option_id' => 1, 'option_label' => 'second option'] + ); + $this->model->expects($this->at(6))->method('getValueHtml')->willReturn($items[1]->getName()); $orderMock->expects($this->any())->method('formatPriceTxt')->willReturnArgument(0); $this->model->setOrder($orderMock); diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTestProvider.php b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTestProvider.php new file mode 100644 index 0000000000000..7de3d383d006e --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Model/Sales/Order/Pdf/Items/InvoiceTestProvider.php @@ -0,0 +1,329 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Test\Unit\Model\Sales\Order\Pdf\Items; + +/** + * Data provider class for InvoiceTest class + */ +class InvoiceTestProvider +{ + /** + * Returns invoice test variations data + * + * @return array[] + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getData(): array + { + return [ + 'display_both' => [ + 'expected' => [ + 1 => [ + 'height' => 15, + 'lines' => [ + [ + [ + 'text' => 'test option', + 'feed' => 35, + 'font' => 'italic', + + ], + ], + [ + [ + 'text' => 'Simple1', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 1.66, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => 'Excl. Tax:', + 'feed' => 380, + 'align' => 'right', + ], + [ + 'text' => 'Excl. Tax:', + 'feed' => 565, + 'align' => 'right', + ], + ], + [ + [ + 'text' => '10.00', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '20.00', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + [ + [ + 'text' => 'Incl. Tax:', + 'feed' => 380, + 'align' => 'right', + ], + [ + 'text' => 'Incl. Tax:', + 'feed' => 565, + 'align' => 'right', + ], + ], + [ + [ + 'text' => '10.83', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '21.66', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + [ + [ + 'text' => 'Simple2', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 0.83, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => 'Excl. Tax:', + 'feed' => 380, + 'align' => 'right', + ], + [ + 'text' => 'Excl. Tax:', + 'feed' => 565, + 'align' => 'right', + ], + ], + [ + [ + 'text' => '5.00', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '10.00', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + [ + [ + 'text' => 'Incl. Tax:', + 'feed' => 380, + 'align' => 'right', + ], + [ + 'text' => 'Incl. Tax:', + 'feed' => 565, + 'align' => 'right', + ], + ], + [ + [ + 'text' => '5.41', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '10.83', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + ], + ], + ], + 'tax_mock_method' => 'displaySalesBothPrices', + ], + 'including_tax' => [ + 'expected' => [ + 1 => [ + 'height' => 15, + 'lines' => [ + [ + [ + 'text' => 'test option', + 'feed' => 35, + 'font' => 'italic', + ], + ], + [ + [ + 'text' => 'Simple1', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 1.66, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '10.83', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '21.66', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + [ + [ + 'text' => 'Simple2', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 0.83, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '5.41', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '10.83', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + ], + ], + ], + 'tax_mock_method' => 'displaySalesPriceInclTax', + ], + 'excluding_tax' => [ + 'expected' => [ + 1 => [ + 'height' => 15, + 'lines' => [ + [ + [ + 'text' => 'test option', + 'feed' => 35, + 'font' => 'italic', + + ], + ], + [ + [ + 'text' => 'Simple1', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 1.66, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '10.00', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '20.00', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + [ + [ + 'text' => 'Simple2', + 'feed' => 40, + ], + [ + 'text' => 2, + 'feed' => 435, + 'align' => 'right', + ], + [ + 'text' => 0.83, + 'feed' => 495, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '5.00', + 'feed' => 380, + 'font' => 'bold', + 'align' => 'right', + ], + [ + 'text' => '10.00', + 'feed' => 565, + 'font' => 'bold', + 'align' => 'right', + ], + ], + ], + ], + ], + 'tax_mock_method' => 'displaySalesPriceExclTax', + ], + ]; + } +} From 245fc040700e534181cff1956688c77c17eb33d1 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 4 Jan 2021 17:06:37 +0200 Subject: [PATCH 107/112] MC-40061: Create automated test for: "Subscribe to stock price alert" --- .../Product/Edit/Tab/Alerts/StockTest.php | 90 +++++++++++++++++++ .../Product/Form/Modifier/AlertsTest.php | 56 ++++++++++++ .../_files/product_alert_rollback.php | 33 +++++++ .../_files/stock_alert_on_second_website.php | 66 ++++++++++++++ ...stock_alert_on_second_website_rollback.php | 52 +++++++++++ 5 files changed, 297 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AlertsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ProductAlert/_files/product_alert_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/ProductAlert/_files/stock_alert_on_second_website.php create mode 100644 dev/tests/integration/testsuite/Magento/ProductAlert/_files/stock_alert_on_second_website_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php new file mode 100644 index 0000000000000..d03bc935bcd72 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Alerts; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Check stock alert grid + * + * @see \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Alerts\Stock + * + * @magentoAppArea adminhtml + */ +class StockTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Stock */ + private $block; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Stock::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + } + + /** + * @dataProvider alertsDataProvider + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/ProductAlert/_files/product_alert.php + * @magentoDataFixture Magento/ProductAlert/_files/stock_alert_on_second_website.php + * + * @param string $sku + * @param string $expectedEmail + * @param string|null $storeCode + * @return void + */ + public function testGridCollectionWIthStoreId(string $sku, string $expectedEmail, ?string $storeCode = null): void + { + $productId = (int)$this->productRepository->get($sku)->getId(); + $storeId = $storeCode ? (int)$this->storeManager->getStore($storeCode)->getId() : null; + $this->block->getRequest()->setParams(['id' => $productId, 'store' => $storeId]); + $collection = $this->block->getPreparedCollection(); + $this->assertCount(1, $collection); + $this->assertEquals($expectedEmail, $collection->getFirstItem()->getEmail()); + } + + /** + * @return array + */ + public function alertsDataProvider(): array + { + return [ + 'without_store_id_filter' => [ + 'product_sku' => 'simple', + 'expected_customer_emails' => 'customer@example.com', + ], + 'with_store_id_filter' => [ + 'product_sku' => 'simple_on_second_website', + 'expected_customer_emails' => 'customer_second_ws_with_addr@example.com', + 'store_code' => 'fixture_third_store', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AlertsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AlertsTest.php new file mode 100644 index 0000000000000..96ddc66c875b7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AlertsTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Alerts modifier test + * + * @see \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Alerts + * + * @magentoAppArea adminhtml + */ +class AlertsTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Alerts */ + private $stockAlertsModifier; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->stockAlertsModifier = $this->objectManager->get(Alerts::class); + } + + /** + * @magentoConfigFixture current_store catalog/productalert/allow_stock 1 + * + * @return void + */ + public function testModifyMeta(): void + { + $meta = $this->stockAlertsModifier->modifyMeta([]); + $this->assertArrayHasKey('alerts', $meta); + $content = $meta['alerts']['children'][Alerts::DATA_SCOPE_STOCK]['arguments']['data']['config']['content']; + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath("//div[@data-grid-id='alertStock']", $content) + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/_files/product_alert_rollback.php b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/product_alert_rollback.php new file mode 100644 index 0000000000000..e9c4900ded341 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/product_alert_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Registry; +use Magento\ProductAlert\Model\StockFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +$objectManager = Bootstrap::getObjectManager(); +/** @var StockFactory $stockFactory */ +$stockFactory = $objectManager->get(StockFactory::class); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +$customer = $customerRepository->get('customer@example.com'); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$stockAlert = $stockFactory->create(); +$stockAlert->deleteCustomer((int)$customer->getId()); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php'); +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_rollback.php'); diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/_files/stock_alert_on_second_website.php b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/stock_alert_on_second_website.php new file mode 100644 index 0000000000000..b9d4c4b81f69b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/stock_alert_on_second_website.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\ProductAlert\Model\ResourceModel\Stock as StockResource; +use Magento\ProductAlert\Model\StockFactory; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_for_second_website_with_address.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); +$secondWebsite = $storeManager->getWebsite('test'); +/** @var ProductInterfaceFactory $productFactory */ +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +/** @var ProductRepositoryInterface $peoductRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var StockFactory $stockFactory */ +$stockFactory = $objectManager->get(StockFactory::class); +/** @var StockResource $stockResource */ +$stockResource = $objectManager->get(StockResource::class); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +$customer = $customerRepository->get('customer_second_ws_with_addr@example.com', (int)$secondWebsite->getId()); + + +$product = $productFactory->create(); +$product + ->setTypeId('simple') + ->setAttributeSetId(4) + ->setWebsiteIds([(int)$secondWebsite->getId()]) + ->setName('Simple Product2') + ->setSku('simple_on_second_website') + ->setPrice(10) + ->setMetaTitle('meta title2') + ->setMetaKeyword('meta keyword2') + ->setMetaDescription('meta description2') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK]); + +$productRepository->save($product); + +$stockAlert = $stockFactory->create(); +$stockAlert->setCustomerId( + $customer->getId() +)->setProductId( + (int)$productRepository->get($product->getSku())->getId() +)->setWebsiteId( + (int)$secondWebsite->getId() +)->setStoreId( + (int)$storeManager->getStore('fixture_third_store')->getId() +); +$stockResource->save($stockAlert); diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/_files/stock_alert_on_second_website_rollback.php b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/stock_alert_on_second_website_rollback.php new file mode 100644 index 0000000000000..0fa5f73a7927c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/stock_alert_on_second_website_rollback.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\ProductAlert\Model\ResourceModel\Stock as StockResource; +use Magento\ProductAlert\Model\StockFactory; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $peoductRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var StockFactory $stockFactory */ +$stockFactory = $objectManager->get(StockFactory::class); +/** @var StockResource $stockResource */ +$stockResource = $objectManager->get(StockResource::class); +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); +$secondWebsite = $storeManager->getWebsite('test'); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +$customer = $customerRepository->get('customer_second_ws_with_addr@example.com', (int)$secondWebsite->getId()); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $productRepository->deleteById('simple_on_second_website'); +} catch (NoSuchEntityException $e) { + //already removed +} + + +$stockAlert = $stockFactory->create(); +$stockAlert->deleteCustomer((int)$customer->getId(), (int)$secondWebsite->getId()); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +Resolver::getInstance() + ->requireDataFixture('Magento/Customer/_files/customer_for_second_website_with_address_rollback.php'); From d866a0b69b35f7207f743d48ad9bcc2c3280f85c Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 4 Jan 2021 18:17:41 +0200 Subject: [PATCH 108/112] MC-40070: Create automated test for: "Try to save product with string type qty value" --- .../Product/Attribute/Backend/StockTest.php | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/StockTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/StockTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/StockTest.php new file mode 100644 index 0000000000000..24d5b668ac09c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/StockTest.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Backend; + +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Model\Product; +use Magento\Eav\Model\Config; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test class for backend stock attribute model. + * + * @see \Magento\Catalog\Model\Product\Attribute\Backend\Stock + * + * @magentoAppArea adminhtml + */ +class StockTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductInterfaceFactory */ + private $productFactory; + + /** @var Stock */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productFactory = $this->objectManager->get(ProductInterfaceFactory::class); + $this->model = $this->objectManager->get(Stock::class); + $this->model->setAttribute( + $this->objectManager->get(Config::class)->getAttribute(Product::ENTITY, 'quantity_and_stock_status') + ); + } + + /** + * @return void + */ + public function testValidate(): void + { + $this->expectException(LocalizedException::class); + $this->expectErrorMessage((string)__('Please enter a valid number in this field.')); + $product = $this->productFactory->create(); + $product->setQuantityAndStockStatus(['qty' => 'string']); + $this->model->validate($product); + } +} From f5932e2fe2abbd8d011c53cc886d57f5d6464f20 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 5 Jan 2021 08:30:55 +0200 Subject: [PATCH 109/112] MC-40277: Integration test failed \Magento\Catalog\Block\Product\View\Options\Type\DateTest::testToHtmlWithDropDown --- .../Catalog/Block/Product/View/Options/Type/DateTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php index 91a54d8fc13fa..d21fdf190c0b8 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php @@ -20,6 +20,7 @@ /** * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DateTest extends TestCase { @@ -96,9 +97,10 @@ protected function tearDown(): void } /** - * @magentoAppArea frontend * @param array $data * @param array $expected + * @magentoAppArea frontend + * @magentoConfigFixture current_store catalog/custom_options/year_range 2020,2030 * @dataProvider toHtmlWithDropDownDataProvider */ public function testToHtmlWithDropDown(array $data, array $expected): void @@ -108,11 +110,12 @@ public function testToHtmlWithDropDown(array $data, array $expected): void } /** - * @magentoAppArea frontend - * @magentoConfigFixture current_store catalog/custom_options/use_calendar 1 * @param array $data * @param array $expected * @param string|null $locale + * @magentoAppArea frontend + * @magentoConfigFixture current_store catalog/custom_options/use_calendar 1 + * @magentoConfigFixture current_store catalog/custom_options/year_range 2020,2030 * @dataProvider toHtmlWithCalendarDataProvider */ public function testToHtmlWithCalendar(array $data, array $expected, ?string $locale = null): void From 0292cb250ec6c9a436486b7e6d1e11c4e3a95286 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 5 Jan 2021 08:54:49 +0200 Subject: [PATCH 110/112] MC-40277: Integration test failed \Magento\Catalog\Block\Product\View\Options\Type\DateTest::testToHtmlWithDropDown --- .../CreateProductAttributeEntityDateTest.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml index d1f7adb8a902c..9d805b2cf7930 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest/CreateProductAttributeEntityDateTest.xml @@ -14,8 +14,8 @@ <title value="Admin should be able to create a Date product attribute"/> <description value="Admin should be able to create a Date product attribute"/> <severity value="BLOCKER"/> - <testCaseId value="MC-10895"/> - <group value="Catalog"/> + <testCaseId value="MC-26021"/> + <group value="catalog"/> <group value="mtf_migrated"/> </annotations> @@ -34,7 +34,7 @@ <!--Generate date for use as default value, needs to be MM/d/YYYY and mm/d/yy--> <generateDate date="now" format="m/j/Y" stepKey="generateDefaultDate"/> - <generateDate date="now" format="m/j/y" stepKey="generateDateCompressedFormat"/> + <generateDate date="now" format="n/j/y" stepKey="generateDateCompressedFormat"/> <!--Navigate to Stores > Attributes > Product.--> <actionGroup ref="AdminOpenProductAttributePageActionGroup" stepKey="goToProductAttributes"/> From 7a1e8a88f00ceb4ff689e591eeaaa06e62fb336b Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 5 Jan 2021 12:35:05 +0200 Subject: [PATCH 111/112] MC-40061: Create automated test for: "Subscribe to stock price alert" --- .../Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php index d03bc935bcd72..b9ccfd6d52458 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/StockTest.php @@ -60,7 +60,7 @@ protected function setUp(): void * @param string|null $storeCode * @return void */ - public function testGridCollectionWIthStoreId(string $sku, string $expectedEmail, ?string $storeCode = null): void + public function testGridCollectionWithStoreId(string $sku, string $expectedEmail, ?string $storeCode = null): void { $productId = (int)$this->productRepository->get($sku)->getId(); $storeId = $storeCode ? (int)$this->storeManager->getStore($storeCode)->getId() : null; From 6db8da7e8d8e5d0df516b501cf2473b43fcc4c8b Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Wed, 6 Jan 2021 10:18:37 +0200 Subject: [PATCH 112/112] MC-39891: Arabic text in invoice is not displayed correctly --- app/code/Magento/Sales/Model/RtlTextHandler.php | 2 +- app/code/Magento/Sales/Test/Unit/Model/RtlTextHandlerTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/RtlTextHandler.php b/app/code/Magento/Sales/Model/RtlTextHandler.php index cfb88dc63f58b..b943320e0f897 100644 --- a/app/code/Magento/Sales/Model/RtlTextHandler.php +++ b/app/code/Magento/Sales/Model/RtlTextHandler.php @@ -48,7 +48,7 @@ public function reverseRtlText(string $string): string for ($i = 0; $i < $splitTextAmount; $i++) { if ($this->isRtlText($splitText[$i])) { - for ($j = $i + 1; $j < $splitTextAmount; $j++) { + for ($j = $i; $j < $splitTextAmount; $j++) { $tmp = $this->isRtlText($splitText[$j]) ? $this->stringUtils->strrev($splitText[$j]) : $splitText[$j]; $splitText[$j] = $this->isRtlText($splitText[$i]) diff --git a/app/code/Magento/Sales/Test/Unit/Model/RtlTextHandlerTest.php b/app/code/Magento/Sales/Test/Unit/Model/RtlTextHandlerTest.php index 2faeb17dc2395..1a8159dbf4cbb 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/RtlTextHandlerTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/RtlTextHandlerTest.php @@ -62,6 +62,7 @@ public function provideRtlTexts(): array ['Herr Prof. Dr. Gerald Schüler B.A.', false],//German ['نديم مقداد نعمان القحطاني', true],//Arabic ['شهاب الفرحان', true],//Arabic + ['مرحبا ماجنت اثنان', true],//Arabic ['צבר קרליבך', true],//Hebrew ['גורי מייזליש', true],//Hebrew ['اتابک بهشتی', true],//Persian