From cf2eb43d75d7e5004f18bbf55483f2787553171b Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Fri, 7 Jun 2019 14:25:05 +0300 Subject: [PATCH 01/55] MC-15341: Default product numbers to display results in poor display on Desktop --- app/code/Magento/Catalog/etc/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 3a842166a382..a9ed43fae1d1 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -23,7 +23,7 @@ grid-list - 9,15,30 + 12,24,36 5,10,15,20,25 9 10 From fdc9e9d9a5c92af0c50f1d4c3c4dffc6d61d7209 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Mon, 10 Jun 2019 14:13:09 +0300 Subject: [PATCH 02/55] MC-15341: Default product numbers to display results in poor display on Desktop --- app/code/Magento/Catalog/etc/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index a9ed43fae1d1..20511f4ff229 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -25,7 +25,7 @@ grid-list 12,24,36 5,10,15,20,25 - 9 + 12 10 0 position From e135272787c20f1f2a5f63c9b04a1da634ee8d2a Mon Sep 17 00:00:00 2001 From: Ani Tumanyan Date: Wed, 19 Jun 2019 10:19:23 +0400 Subject: [PATCH 03/55] MC-15341: Default product numbers to display results in poor display on Desktop - Added automated Test script --- ...heckDefaultNumberProductsToDisplayTest.xml | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml new file mode 100644 index 000000000000..6fb4f816d0b9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml @@ -0,0 +1,198 @@ + + + + + + + + <description value="Check default numbers: products to display"/> + <severity value="MAJOR"/> + <testCaseId value="MC-17386"/> + <useCaseId value="MC-15341"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Login as Admin --> + <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <!-- Set default configurations --> + <comment userInput="Set default configurations" stepKey="commentSetDefaultCategory"/> + <magentoCLI command="config:set catalog/frontend/grid_per_page_values 12,24,36" stepKey="setAllowedProductsPerPageValue"/> + <magentoCLI command="config:set catalog/frontend/grid_per_page 12" stepKey="setDefaultProductsPerPageValue"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!--Create 37 Products and Subcategory --> + <comment userInput="Create 37 Products and Subcategory" stepKey="commentCreateData"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProductOne"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwo"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThree"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductFour"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductFive"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductSix"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductSeven"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductEight"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductNine"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTen"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductEleven"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwelve"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirteen"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductFourteen"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductFifteen"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductSixteen"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductSeventeen"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductEighteen"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductNineteen"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwenty"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentyOne"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentyTwo"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentyThree"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentyFour"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentyFive"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentySix"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentySeven"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentyEight"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductTwentyNine"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirty"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirtyOne"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirtyTwo"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirtyThree"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirtyFour"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirtyFive"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirtySix"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProductThirtySeven"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProductOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="createSimpleProductTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createSimpleProductThree" stepKey="deleteProductThree"/> + <deleteData createDataKey="createSimpleProductFour" stepKey="deleteProductFour"/> + <deleteData createDataKey="createSimpleProductFive" stepKey="deleteProductFive"/> + <deleteData createDataKey="createSimpleProductSix" stepKey="deleteProductSix"/> + <deleteData createDataKey="createSimpleProductSeven" stepKey="deleteProductSeven"/> + <deleteData createDataKey="createSimpleProductEight" stepKey="deleteProductEight"/> + <deleteData createDataKey="createSimpleProductNine" stepKey="deleteProductNine"/> + <deleteData createDataKey="createSimpleProductTen" stepKey="deleteProductTen"/> + <deleteData createDataKey="createSimpleProductEleven" stepKey="deleteProductEleven"/> + <deleteData createDataKey="createSimpleProductTwelve" stepKey="deleteProductTwelve"/> + <deleteData createDataKey="createSimpleProductThirteen" stepKey="deleteProductThirteen"/> + <deleteData createDataKey="createSimpleProductFourteen" stepKey="deleteProductFourteen"/> + <deleteData createDataKey="createSimpleProductFifteen" stepKey="deleteProductFifteen"/> + <deleteData createDataKey="createSimpleProductSixteen" stepKey="deleteProductSixteen"/> + <deleteData createDataKey="createSimpleProductSeventeen" stepKey="deleteProductSeventeen"/> + <deleteData createDataKey="createSimpleProductEighteen" stepKey="deleteProductEighteen"/> + <deleteData createDataKey="createSimpleProductNineteen" stepKey="deleteProductNineteen"/> + <deleteData createDataKey="createSimpleProductTwenty" stepKey="deleteProductTwenty"/> + <deleteData createDataKey="createSimpleProductTwentyOne" stepKey="deleteProductTwentyOne"/> + <deleteData createDataKey="createSimpleProductTwentyTwo" stepKey="deleteProductTwentyTwo"/> + <deleteData createDataKey="createSimpleProductTwentyThree" stepKey="deleteProductTwentyThree"/> + <deleteData createDataKey="createSimpleProductTwentyFour" stepKey="deleteProductTwentyFour"/> + <deleteData createDataKey="createSimpleProductTwentyFive" stepKey="deleteProductTwentyFive"/> + <deleteData createDataKey="createSimpleProductTwentySix" stepKey="deleteProductTwentySix"/> + <deleteData createDataKey="createSimpleProductTwentySeven" stepKey="deleteProductTwentySeven"/> + <deleteData createDataKey="createSimpleProductTwentyEight" stepKey="deleteProductTwentyEight"/> + <deleteData createDataKey="createSimpleProductTwentyNine" stepKey="deleteProductTwentyNine"/> + <deleteData createDataKey="createSimpleProductThirty" stepKey="deleteProductThirty"/> + <deleteData createDataKey="createSimpleProductThirtyOne" stepKey="deleteProductThirtyOne"/> + <deleteData createDataKey="createSimpleProductThirtyTwo" stepKey="deleteProductThirtyTwo"/> + <deleteData createDataKey="createSimpleProductThirtyThree" stepKey="deleteProductThirtyThree"/> + <deleteData createDataKey="createSimpleProductThirtyFour" stepKey="deleteProductThirtyFour"/> + <deleteData createDataKey="createSimpleProductThirtyFive" stepKey="deleteProductThirtyFive"/> + <deleteData createDataKey="createSimpleProductThirtySix" stepKey="deleteProductThirtySix"/> + <deleteData createDataKey="createSimpleProductThirtySeven" stepKey="deleteProductThirtySeven"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Open storefront on the category page --> + <comment userInput="Open storefront on the category page" stepKey="commentOpenStorefront"/> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="goToStorefrontCreatedCategoryPage"/> + <!-- Check the drop-down at the bottom of page contains options --> + <comment userInput="Check the drop-down at the bottom of page contains options" stepKey="commentCheckOptions"/> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToBottomToolbarSection"/> + <assertElementContainsAttribute selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" expectedValue="12" stepKey="assertPerPageFirstValue" /> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="24" stepKey="selectPerPageSecondValue" /> + <assertElementContainsAttribute selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" expectedValue="24" stepKey="assertPerPageSecondValue" /> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="36" stepKey="selectPerPageThirdValue" /> + <assertElementContainsAttribute selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" attribute="value" expectedValue="36" stepKey="assertPerPageThirdValue" /> + </test> +</tests> From 3fd8742fd0d14531e1d7b7a90b8c8deb0b972f36 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Thu, 20 Jun 2019 12:41:31 +0300 Subject: [PATCH 04/55] MC-15341: Default product numbers to display results in poor display on Desktop - Fixes of failed tests --- .../AdminCheckPaginationInStorefrontTest.xml | 62 +++++++++++++------ .../StorefrontOrderPagerDisplayedTest.xml | 2 +- .../Test/StorefrontOrderPagerIsAbsentTest.xml | 2 +- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml index f40a62c164ec..384d8ddea4f1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml @@ -41,6 +41,16 @@ <createData entity="PaginationProduct" stepKey="simpleProduct18"/> <createData entity="PaginationProduct" stepKey="simpleProduct19"/> <createData entity="PaginationProduct" stepKey="simpleProduct20"/> + <createData entity="PaginationProduct" stepKey="simpleProduct21"/> + <createData entity="PaginationProduct" stepKey="simpleProduct22"/> + <createData entity="PaginationProduct" stepKey="simpleProduct23"/> + <createData entity="PaginationProduct" stepKey="simpleProduct24"/> + <createData entity="PaginationProduct" stepKey="simpleProduct25"/> + <createData entity="PaginationProduct" stepKey="simpleProduct26"/> + <createData entity="PaginationProduct" stepKey="simpleProduct27"/> + <createData entity="PaginationProduct" stepKey="simpleProduct28"/> + <createData entity="PaginationProduct" stepKey="simpleProduct29"/> + <createData entity="PaginationProduct" stepKey="simpleProduct30"/> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> </before> <after> @@ -67,6 +77,16 @@ <deleteData createDataKey="simpleProduct18" stepKey="deleteSimpleProduct18"/> <deleteData createDataKey="simpleProduct19" stepKey="deleteSimpleProduct19"/> <deleteData createDataKey="simpleProduct20" stepKey="deleteSimpleProduct20"/> + <deleteData createDataKey="simpleProduct21" stepKey="deleteSimpleProduct21"/> + <deleteData createDataKey="simpleProduct22" stepKey="deleteSimpleProduct22"/> + <deleteData createDataKey="simpleProduct23" stepKey="deleteSimpleProduct23"/> + <deleteData createDataKey="simpleProduct24" stepKey="deleteSimpleProduct24"/> + <deleteData createDataKey="simpleProduct25" stepKey="deleteSimpleProduct25"/> + <deleteData createDataKey="simpleProduct26" stepKey="deleteSimpleProduct26"/> + <deleteData createDataKey="simpleProduct27" stepKey="deleteSimpleProduct27"/> + <deleteData createDataKey="simpleProduct28" stepKey="deleteSimpleProduct28"/> + <deleteData createDataKey="simpleProduct29" stepKey="deleteSimpleProduct29"/> + <deleteData createDataKey="simpleProduct30" stepKey="deleteSimpleProduct30"/> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -86,11 +106,13 @@ <waitForElementVisible selector="{{CatalogProductsSection.resetFilter}}" time="30" stepKey="waitForResetButtonToVisible"/> <click selector="{{CatalogProductsSection.resetFilter}}" stepKey="clickOnResetFilter"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> - <selectOption selector="{{AdminProductGridFilterSection.productPerPage}}" userInput="20" stepKey="selectPagePerView"/> + + <selectOption selector="{{AdminProductGridFilterSection.productPerPage}}" userInput="30" stepKey="selectPagePerView"/> + <wait stepKey="waitFroPageToLoad1" time="30"/> <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="pagi" stepKey="selectProduct1"/> <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitFroPageToLoad1"/> - <see selector="{{AdminProductGridFilterSection.productCount}}" userInput="20" stepKey="seeNumberOfProductsFound"/> + <waitForPageLoad stepKey="waitFroPageToLoad2"/> + <see selector="{{AdminProductGridFilterSection.productCount}}" userInput="30" stepKey="seeNumberOfProductsFound"/> <click selector="{{AdminCategoryProductsGridSection.productSelectAll}}" stepKey="selectSelectAll"/> <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForCategorySaved"/> @@ -104,73 +126,73 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(_defaultCategory.name)}}" stepKey="selectCategory"/> <waitForPageLoad stepKey="waitForProductToLoad"/> - <!--Select 9 items per page and verify number of products displayed in each page --> + <!--Select 12 items per page and verify number of products displayed in each page --> <conditionalClick selector="{{StorefrontCategoryTopToolbarSection.gridMode}}" visible="true" dependentSelector="{{StorefrontCategoryTopToolbarSection.gridMode}}" stepKey="seeProductGridIsActive"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToBottomToolbarSection"/> - <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="9" stepKey="selectPerPageOption"/> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="12" stepKey="selectPerPageOption"/> <!--Verify number of products displayed in First Page --> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="9" stepKey="seeNumberOfProductsInFirstPage"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInFirstPage"/> <!--Verify number of products displayed in Second Page --> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton"/> <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage"/> <waitForPageLoad stepKey="waitForPageToLoad4"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="9" stepKey="seeNumberOfProductsInSecondPage"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInSecondPage"/> <!--Verify number of products displayed in third Page --> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton1"/> <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage1"/> <waitForPageLoad stepKey="waitForPageToLoad2"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="2" stepKey="seeNumberOfProductsInThirdPage"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInThirdPage"/> <!--Change Pages using Previous Page selector and verify number of products displayed in each page--> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="scrollToPreviousPage"/> <click selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="clickOnPreviousPage1"/> <waitForPageLoad stepKey="waitForPageToLoad5"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="9" stepKey="seeNumberOfProductsInSecondPage1"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInSecondPage1"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="scrollToPreviousPage1"/> <click selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="clickOnPreviousPage2"/> <waitForPageLoad stepKey="waitForPageToLoad6"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="9" stepKey="seeNumberOfProductsInFirstPage1"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInFirstPage1"/> <!--Select Pages by using page Number and verify number of products displayed--> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToPreviousPage2"/> <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('2')}}" stepKey="clickOnPage2"/> <waitForPageLoad stepKey="waitForPageToLoad7"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="9" stepKey="seeNumberOfProductsInSecondPage2"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInSecondPage2"/> <!--Select Third Page using page number--> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToPreviousPage3"/> <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('3')}}" stepKey="clickOnThirdPage"/> <waitForPageLoad stepKey="waitForPageToLoad8"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="2" stepKey="seeNumberOfProductsInThirdPage2"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInThirdPage2"/> <!--Select First Page using page number--> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="scrollToPreviousPage4"/> <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="clickOnFirstPage"/> <waitForPageLoad stepKey="waitForPageToLoad9"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="9" stepKey="seeNumberOfProductsFirstPage2"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsFirstPage2"/> - <!--Select 15 items per page and verify number of products displayed in each page --> + <!--Select 24 items per page and verify number of products displayed in each page --> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage"/> - <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="15" stepKey="selectPerPageOption1"/> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="24" stepKey="selectPerPageOption1"/> <waitForPageLoad stepKey="waitForPageToLoad10"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="15" stepKey="seeNumberOfProductsInFirstPage3"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="24" stepKey="seeNumberOfProductsInFirstPage3"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton2"/> <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage2"/> <waitForPageLoad stepKey="waitForPageToLoad11"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="5" stepKey="seeNumberOfProductsInSecondPage3"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInSecondPage3"/> <!--Select First Page using page number--> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="scrollToPreviousPage5"/> <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="clickOnFirstPage2"/> <waitForPageLoad stepKey="waitForPageToLoad13"/> - <!--Select 30 items per page and verify number of products displayed in each page --> + <!--Select 36 items per page and verify number of products displayed in each page --> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage4"/> - <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="30" stepKey="selectPerPageOption2"/> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="36" stepKey="selectPerPageOption2"/> <waitForPageLoad stepKey="waitForPageToLoad12"/> - <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="20" stepKey="seeNumberOfProductsInFirstPage4"/> + <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="30" stepKey="seeNumberOfProductsInFirstPage4"/> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml index b8772f24a2a4..0e58bb84988a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml @@ -129,7 +129,7 @@ <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <scrollTo selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="scrollToLimiter"/> - <selectOption userInput="30" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="selectLimitOnPage"/> + <selectOption userInput="36" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="selectLimitOnPage"/> <waitForPageLoad stepKey="waitForLoadProducts"/> <scrollToTopOfPage stepKey="scrollToTopOfPage"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml index 9909fca44fe2..3ff8a7791d88 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml @@ -125,7 +125,7 @@ <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <scrollTo selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="scrollToLimiter"/> - <selectOption userInput="30" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="selectLimitOnPage"/> + <selectOption userInput="36" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="selectLimitOnPage"/> <waitForPageLoad stepKey="waitForLoadProducts"/> <scrollToTopOfPage stepKey="scrollToTopOfPage"/> From 2d8d7047ca764dec901585376618ee658a0107ba Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 4 Jul 2019 18:00:28 +0400 Subject: [PATCH 05/55] MC-15341: Default product numbers to display results in poor display on Desktop - Updated automated test script --- .../Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml index 384d8ddea4f1..de84d00cabab 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml @@ -20,6 +20,10 @@ <before> <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 1 "/> <magentoCLI stepKey="setFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 1 "/> + <magentoCLI command="config:set catalog/frontend/grid_per_page_values 12,24,36" stepKey="setAllowedProductsPerPageValue"/> + <magentoCLI command="config:set catalog/frontend/grid_per_page 12" stepKey="setDefaultProductsPerPageValue"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> <createData entity="PaginationProduct" stepKey="simpleProduct1"/> <createData entity="PaginationProduct" stepKey="simpleProduct2"/> From cec3ca0baab08929afc3593b31f5379ce30ff052 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 12 Jul 2019 12:32:40 +0400 Subject: [PATCH 06/55] MC-15341: Default product numbers to display results in poor display on Desktop - Updated automated test script --- .../AdminCatalogStorefrontConfigSection.xml | 16 ++++++++ .../AdminCheckPaginationInStorefrontTest.xml | 41 ++++++++++--------- 2 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminCatalogStorefrontConfigSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCatalogStorefrontConfigSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCatalogStorefrontConfigSection.xml new file mode 100644 index 000000000000..d0200f1e0a5b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCatalogStorefrontConfigSection.xml @@ -0,0 +1,16 @@ +<?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="AdminCatalogStorefrontConfigSection"> + <element name="sectionHeader" type="button" selector="#catalog_frontend-head"/> + <element name="productsPerPageAllowedValues" type="input" selector="#catalog_frontend_grid_per_page_values"/> + <element name="productsPerPageDefaultValue" type="input" selector="#catalog_frontend_grid_per_page"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml index de84d00cabab..1b7245874706 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml @@ -20,10 +20,6 @@ <before> <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 1 "/> <magentoCLI stepKey="setFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 1 "/> - <magentoCLI command="config:set catalog/frontend/grid_per_page_values 12,24,36" stepKey="setAllowedProductsPerPageValue"/> - <magentoCLI command="config:set catalog/frontend/grid_per_page 12" stepKey="setDefaultProductsPerPageValue"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> <createData entity="_defaultCategory" stepKey="createDefaultCategory"/> <createData entity="PaginationProduct" stepKey="simpleProduct1"/> <createData entity="PaginationProduct" stepKey="simpleProduct2"/> @@ -93,16 +89,24 @@ <deleteData createDataKey="simpleProduct30" stepKey="deleteSimpleProduct30"/> <actionGroup ref="logout" stepKey="logout"/> </after> - + <!--Verify default number of products displayed in the grid view--> + <comment userInput="Verify default number of products displayed in the grid view" stepKey="commentVerifyDefaultValues"/> + <amOnPage url="{{CatalogConfigPage.url}}" stepKey="goToCatalogConfigPagePage"/> + <waitForPageLoad stepKey="waitForConfigPageLoad" /> + <conditionalClick selector="{{AdminCatalogStorefrontConfigSection.sectionHeader}}" dependentSelector="{{AdminCatalogStorefrontConfigSection.productsPerPageAllowedValues}}" visible="false" stepKey="openCatalogConfigStorefrontSection"/> + <waitForElementVisible selector="{{AdminCatalogStorefrontConfigSection.productsPerPageAllowedValues}}" stepKey="waitForSectionOpen"/> + <seeInField selector="{{AdminCatalogStorefrontConfigSection.productsPerPageAllowedValues}}" userInput="12,24,36" stepKey="seeDefaultValueAllowedNumberProductsPerPage"/> + <seeInField selector="{{AdminCatalogStorefrontConfigSection.productsPerPageDefaultValue}}" userInput="12" stepKey="seeDefaultValueProductPerPage"/> <!--Open Category Page and select created category--> + <comment userInput="Open Category Page and select created category" stepKey="commentOpenCategoryPage"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> <waitForPageLoad stepKey="waitForPageToLoad0"/> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(_defaultCategory.name)}}" stepKey="selectCreatedCategory"/> <waitForPageLoad stepKey="waitForPageToLoaded2"/> - <!--Select Products--> + <comment userInput="Select Products" stepKey="commentSelectProducts"/> <scrollTo selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" x="0" y="-80" stepKey="scrollToProductInCategory"/> <click selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" stepKey="clickOnProductInCategory"/> <waitForPageLoad stepKey="waitForProductsToLoad"/> @@ -110,7 +114,6 @@ <waitForElementVisible selector="{{CatalogProductsSection.resetFilter}}" time="30" stepKey="waitForResetButtonToVisible"/> <click selector="{{CatalogProductsSection.resetFilter}}" stepKey="clickOnResetFilter"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> - <selectOption selector="{{AdminProductGridFilterSection.productPerPage}}" userInput="30" stepKey="selectPagePerView"/> <wait stepKey="waitFroPageToLoad1" time="30"/> <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="pagi" stepKey="selectProduct1"/> @@ -122,35 +125,35 @@ <waitForPageLoad stepKey="waitForCategorySaved"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the category." stepKey="assertSuccessMessage"/> <waitForPageLoad stepKey="waitForPageTitleToBeSaved"/> - <!--Open Category Store Front Page--> + <comment userInput="Open Category Store Front Page" stepKey="commentOpenCategoryOnStorefront"/> <amOnPage url="{{_defaultCategory.name}}.html" stepKey="goToStorefront"/> <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(_defaultCategory.name)}}" stepKey="seeCategoryOnNavigation"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(_defaultCategory.name)}}" stepKey="selectCategory"/> <waitForPageLoad stepKey="waitForProductToLoad"/> - <!--Select 12 items per page and verify number of products displayed in each page --> + <comment userInput="Select 12 items per page and verify number of products displayed in each page" stepKey="comment12ItemsPerPage"/> <conditionalClick selector="{{StorefrontCategoryTopToolbarSection.gridMode}}" visible="true" dependentSelector="{{StorefrontCategoryTopToolbarSection.gridMode}}" stepKey="seeProductGridIsActive"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToBottomToolbarSection"/> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="12" stepKey="selectPerPageOption"/> - <!--Verify number of products displayed in First Page --> + <comment userInput="Verify number of products displayed in First Page" stepKey="commentVerifyNumberOfProducts"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInFirstPage"/> - <!--Verify number of products displayed in Second Page --> + <comment userInput="Verify number of products displayed in Second Page" stepKey="commentVerifyNumberOfProductsSecondPage"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton"/> <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage"/> <waitForPageLoad stepKey="waitForPageToLoad4"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInSecondPage"/> - <!--Verify number of products displayed in third Page --> + <comment userInput="Verify number of products displayed in third Page" stepKey="commentVerifyNumberOfProductsThirdPage"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToNextButton1"/> <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage1"/> <waitForPageLoad stepKey="waitForPageToLoad2"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInThirdPage"/> - <!--Change Pages using Previous Page selector and verify number of products displayed in each page--> + <comment userInput="Change Pages using Previous Page selector and verify number of products displayed in each page" stepKey="commentVerifyProductsOnEachPage"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="scrollToPreviousPage"/> <click selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="clickOnPreviousPage1"/> <waitForPageLoad stepKey="waitForPageToLoad5"/> @@ -159,26 +162,26 @@ <click selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="clickOnPreviousPage2"/> <waitForPageLoad stepKey="waitForPageToLoad6"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInFirstPage1"/> - <!--Select Pages by using page Number and verify number of products displayed--> + <comment userInput="Select Pages by using page Number and verify number of products displayed" stepKey="commentSelectPagesAndVerify"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToPreviousPage2"/> <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('2')}}" stepKey="clickOnPage2"/> <waitForPageLoad stepKey="waitForPageToLoad7"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsInSecondPage2"/> - <!--Select Third Page using page number--> + <comment userInput="Select Third Page using page number" stepKey="commentSelectThirdPage"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="scrollToPreviousPage3"/> <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('3')}}" stepKey="clickOnThirdPage"/> <waitForPageLoad stepKey="waitForPageToLoad8"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInThirdPage2"/> - <!--Select First Page using page number--> + <comment userInput="Select First Page using page number" stepKey="commentSelectFirstPage"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.previousPage}}" stepKey="scrollToPreviousPage4"/> <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="clickOnFirstPage"/> <waitForPageLoad stepKey="waitForPageToLoad9"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="12" stepKey="seeNumberOfProductsFirstPage2"/> - <!--Select 24 items per page and verify number of products displayed in each page --> + <comment userInput="Select 24 items per page and verify number of products displayed in each page" stepKey="commentSelect24ItemsPerPage"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage"/> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="24" stepKey="selectPerPageOption1"/> <waitForPageLoad stepKey="waitForPageToLoad10"/> @@ -187,13 +190,13 @@ <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickOnNextPage2"/> <waitForPageLoad stepKey="waitForPageToLoad11"/> <seeNumberOfElements selector="{{StorefrontCategoryMainSection.productLink}}" userInput="6" stepKey="seeNumberOfProductsInSecondPage3"/> - <!--Select First Page using page number--> + <comment userInput="Select First Page using page number" stepKey="commentSelectFirstPageSecondTime"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="scrollToPreviousPage5"/> <click selector="{{StorefrontCategoryBottomToolbarSection.pageNumber('1')}}" stepKey="clickOnFirstPage2"/> <waitForPageLoad stepKey="waitForPageToLoad13"/> - <!--Select 36 items per page and verify number of products displayed in each page --> + <comment userInput="Select 36 items per page and verify number of products displayed in each page" stepKey="commentSelect36ItemsPerPage"/> <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" stepKey="scrollToPerPage4"/> <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="36" stepKey="selectPerPageOption2"/> <waitForPageLoad stepKey="waitForPageToLoad12"/> From ed7ce471751045357ec8a0cdcf5b9858625625c9 Mon Sep 17 00:00:00 2001 From: Lilit Sargsyan <Lilit_Sargsyan@epam.com> Date: Wed, 17 Jul 2019 17:45:08 +0400 Subject: [PATCH 07/55] MC-15341: Default product numbers to display results in poor display on Desktop - Updated automated test script --- .../CheckDefaultNumberProductsToDisplayTest.xml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml index 6fb4f816d0b9..5718844fb6df 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml @@ -21,12 +21,6 @@ <!-- Login as Admin --> <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> <actionGroup ref="LoginAsAdmin" stepKey="login"/> - <!-- Set default configurations --> - <comment userInput="Set default configurations" stepKey="commentSetDefaultCategory"/> - <magentoCLI command="config:set catalog/frontend/grid_per_page_values 12,24,36" stepKey="setAllowedProductsPerPageValue"/> - <magentoCLI command="config:set catalog/frontend/grid_per_page 12" stepKey="setDefaultProductsPerPageValue"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> <!--Create 37 Products and Subcategory --> <comment userInput="Create 37 Products and Subcategory" stepKey="commentCreateData"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -183,6 +177,14 @@ <deleteData createDataKey="createSimpleProductThirtySeven" stepKey="deleteProductThirtySeven"/> <actionGroup ref="logout" stepKey="logout"/> </after> + <!--Verify configuration for default number of products displayed in the grid view--> + <comment userInput="Verify configuration for default number of products displayed in the grid view" stepKey="commentVerifyDefaultValues"/> + <amOnPage url="{{CatalogConfigPage.url}}" stepKey="goToCatalogConfigPagePage"/> + <waitForPageLoad stepKey="waitForConfigPageLoad" /> + <conditionalClick selector="{{AdminCatalogStorefrontConfigSection.sectionHeader}}" dependentSelector="{{AdminCatalogStorefrontConfigSection.productsPerPageAllowedValues}}" visible="false" stepKey="openCatalogConfigStorefrontSection"/> + <waitForElementVisible selector="{{AdminCatalogStorefrontConfigSection.productsPerPageAllowedValues}}" stepKey="waitForSectionOpen"/> + <seeInField selector="{{AdminCatalogStorefrontConfigSection.productsPerPageAllowedValues}}" userInput="12,24,36" stepKey="seeDefaultValueAllowedNumberProductsPerPage"/> + <seeInField selector="{{AdminCatalogStorefrontConfigSection.productsPerPageDefaultValue}}" userInput="12" stepKey="seeDefaultValueProductPerPage"/> <!-- Open storefront on the category page --> <comment userInput="Open storefront on the category page" stepKey="commentOpenStorefront"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="goToStorefrontCreatedCategoryPage"/> From 845eb5b80d62882a6d2c8fe85ffee6731169c31e Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Wed, 31 Jul 2019 17:22:18 +0300 Subject: [PATCH 08/55] MC-15341: Default product numbers to display results in poor display on Desktop - Updated automated Test script --- .../Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml index 5718844fb6df..e5f05f1ea00c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml @@ -10,6 +10,7 @@ <test name="CheckDefaultNumbersProductsToDisplayTest"> <annotations> <features value="Catalog"/> + <stories value="Default product numbers to display in grid"/> <title value="Check default numbers: products to display"/> <description value="Check default numbers: products to display"/> <severity value="MAJOR"/> From 39eb1e82a3cae66401f8cac96e749b9d4410b930 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Wed, 14 Aug 2019 11:09:18 +0300 Subject: [PATCH 09/55] MC-15341: Default product numbers to display results in poor display on Desktop - Updated automated Test script --- .../Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml index e5f05f1ea00c..cc8a31cc8034 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml @@ -10,7 +10,7 @@ <test name="CheckDefaultNumbersProductsToDisplayTest"> <annotations> <features value="Catalog"/> - <stories value="Default product numbers to display in grid"/> + <stories value="Product grid"/> <title value="Check default numbers: products to display"/> <description value="Check default numbers: products to display"/> <severity value="MAJOR"/> From 9c856a56a7c4599a9e377251ac30219076e7b5cf Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Mon, 19 Aug 2019 14:02:30 +0300 Subject: [PATCH 10/55] MC-15341: Default product numbers to display results in poor display on Desktop --- ...ml => StorefrontCheckDefaultNumberProductsToDisplayTest.xml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/code/Magento/Catalog/Test/Mftf/Test/{CheckDefaultNumberProductsToDisplayTest.xml => StorefrontCheckDefaultNumberProductsToDisplayTest.xml} (99%) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml similarity index 99% rename from app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml index 5718844fb6df..1ce856831cea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckDefaultNumberProductsToDisplayTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCheckDefaultNumberProductsToDisplayTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CheckDefaultNumbersProductsToDisplayTest"> + <test name="StorefrontCheckDefaultNumbersProductsToDisplayTest"> <annotations> <features value="Catalog"/> <title value="Check default numbers: products to display"/> From 88ee6cf2824b9b12b46d82cffe6d26045f4ed74f Mon Sep 17 00:00:00 2001 From: Eden <quocviet312@gmail.com> Date: Tue, 20 Aug 2019 14:53:43 +0700 Subject: [PATCH 11/55] Rss Model Refactor --- app/code/Magento/Review/Model/Rss.php | 33 ++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Review/Model/Rss.php b/app/code/Magento/Review/Model/Rss.php index df8a5dbb9684..876a3722da61 100644 --- a/app/code/Magento/Review/Model/Rss.php +++ b/app/code/Magento/Review/Model/Rss.php @@ -3,11 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Review\Model; +use Magento\Framework\App\ObjectManager; + /** - * Class Rss - * @package Magento\Catalog\Model\Rss\Product + * Model Rss + * + * Class \Magento\Catalog\Model\Rss\Product\Rss */ class Rss extends \Magento\Framework\Model\AbstractModel { @@ -24,18 +30,39 @@ class Rss extends \Magento\Framework\Model\AbstractModel protected $eventManager; /** + * Rss constructor. + * * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param ReviewFactory $reviewFactory + * @param \Magento\Framework\Model\Context|null $context + * @param \Magento\Framework\Registry|null $registry + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param array $data */ public function __construct( \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Review\Model\ReviewFactory $reviewFactory + \Magento\Review\Model\ReviewFactory $reviewFactory, + \Magento\Framework\Model\Context $context = null, + \Magento\Framework\Registry $registry = null, + \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, + \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + array $data = [] ) { $this->reviewFactory = $reviewFactory; $this->eventManager = $eventManager; + $context = $context ?? ObjectManager::getInstance()->get( + \Magento\Framework\Model\Context::class + ); + $registry = $registry ?? ObjectManager::getInstance()->get( + \Magento\Framework\Registry::class + ); + parent::__construct($context, $registry, $resource, $resourceCollection, $data); } /** + * Get Product Collection + * * @return $this|\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection */ public function getProductCollection() From 0be11cc9db67398006ffed770eab52cea25cfb2b Mon Sep 17 00:00:00 2001 From: Eden <quocviet312@gmail.com> Date: Tue, 20 Aug 2019 15:24:03 +0700 Subject: [PATCH 12/55] Fix static test rss model --- app/code/Magento/Review/Model/Rss.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Review/Model/Rss.php b/app/code/Magento/Review/Model/Rss.php index 876a3722da61..f5abdbb4d3c9 100644 --- a/app/code/Magento/Review/Model/Rss.php +++ b/app/code/Magento/Review/Model/Rss.php @@ -51,12 +51,8 @@ public function __construct( ) { $this->reviewFactory = $reviewFactory; $this->eventManager = $eventManager; - $context = $context ?? ObjectManager::getInstance()->get( - \Magento\Framework\Model\Context::class - ); - $registry = $registry ?? ObjectManager::getInstance()->get( - \Magento\Framework\Registry::class - ); + $context = $context ?? ObjectManager::getInstance()->get(\Magento\Framework\Model\Context::class); + $registry = $registry ?? ObjectManager::getInstance()->get(\Magento\Framework\Registry::class); parent::__construct($context, $registry, $resource, $resourceCollection, $data); } From 7d19fe02eaae1fdc824f26a068dc537211eed9d7 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Fri, 30 Aug 2019 15:24:05 -0500 Subject: [PATCH 13/55] MC-19713: Relative Category links created using PageBuilder have the store name as a URL parameter - Remove store query param from category and product URL on cms pages --- .../Magento/Catalog/Block/Widget/Link.php | 19 +- .../Test/Unit/Block/Widget/LinkTest.php | 221 ++++++++++++------ 2 files changed, 158 insertions(+), 82 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Widget/Link.php b/app/code/Magento/Catalog/Block/Widget/Link.php index 85e50dbd3dc2..3d8a1cdf91ca 100644 --- a/app/code/Magento/Catalog/Block/Widget/Link.php +++ b/app/code/Magento/Catalog/Block/Widget/Link.php @@ -4,17 +4,15 @@ * See COPYING.txt for license details. */ -/** - * Widget to display catalog link - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Catalog\Block\Widget; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +/** + * Render the URL of given entity + */ class Link extends \Magento\Framework\View\Element\Html\Link implements \Magento\Widget\Block\BlockInterface { /** @@ -63,10 +61,9 @@ public function __construct( /** * Prepare url using passed id path and return it - * or return false if path was not found in url rewrites. * * @throws \RuntimeException - * @return string|false + * @return string|false if path was not found in url rewrites. * @SuppressWarnings(PHPMD.NPathComplexity) */ public function getHref() @@ -92,10 +89,6 @@ public function getHref() if ($rewrite) { $href = $store->getUrl('', ['_direct' => $rewrite->getRequestPath()]); - - if (strpos($href, '___store') === false) { - $href .= (strpos($href, '?') === false ? '?' : '&') . '___store=' . $store->getCode(); - } } $this->_href = $href; } @@ -121,6 +114,7 @@ protected function parseIdPath($idPath) /** * Prepare label using passed text as parameter. + * * If anchor text was not specified get entity name from DB. * * @return string @@ -150,9 +144,8 @@ public function getLabel() /** * Render block HTML - * or return empty string if url can't be prepared * - * @return string + * @return string empty string if url can't be prepared */ protected function _toHtml() { diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Widget/LinkTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Widget/LinkTest.php index 8333ed22e1da..3ceaf7dd44f5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Widget/LinkTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Widget/LinkTest.php @@ -5,54 +5,81 @@ */ namespace Magento\Catalog\Test\Unit\Block\Widget; +use Exception; +use Magento\Catalog\Block\Widget\Link; +use Magento\Catalog\Model\ResourceModel\AbstractResource; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Url; +use Magento\Framework\Url\ModifierInterface; +use Magento\Framework\View\Element\Template\Context; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject; +use ReflectionClass; +use RuntimeException; -class LinkTest extends \PHPUnit\Framework\TestCase +/** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class LinkTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\StoreManagerInterface + * @var PHPUnit_Framework_MockObject_MockObject|StoreManagerInterface */ protected $storeManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\UrlRewrite\Model\UrlFinderInterface + * @var PHPUnit_Framework_MockObject_MockObject|UrlFinderInterface */ protected $urlFinder; /** - * @var \Magento\Catalog\Block\Widget\Link + * @var Link */ protected $block; /** - * @var \Magento\Catalog\Model\ResourceModel\AbstractResource|\PHPUnit_Framework_MockObject_MockObject + * @var AbstractResource|PHPUnit_Framework_MockObject_MockObject */ protected $entityResource; + /** + * @inheritDoc + */ protected function setUp() { - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->urlFinder = $this->createMock(\Magento\UrlRewrite\Model\UrlFinderInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->urlFinder = $this->createMock(UrlFinderInterface::class); - $context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class); + $context = $this->createMock(Context::class); $context->expects($this->any()) ->method('getStoreManager') ->will($this->returnValue($this->storeManager)); $this->entityResource = - $this->createMock(\Magento\Catalog\Model\ResourceModel\AbstractResource::class); - - $this->block = (new ObjectManager($this))->getObject(\Magento\Catalog\Block\Widget\Link::class, [ - 'context' => $context, - 'urlFinder' => $this->urlFinder, - 'entityResource' => $this->entityResource - ]); + $this->createMock(AbstractResource::class); + + $this->block = (new ObjectManager($this))->getObject( + Link::class, + [ + 'context' => $context, + 'urlFinder' => $this->urlFinder, + 'entityResource' => $this->entityResource + ] + ); } /** - * @expectedException \RuntimeException + * Tests getHref with wrong id_path + * + * @expectedException RuntimeException * @expectedExceptionMessage Parameter id_path is not set. */ public function testGetHrefWithoutSetIdPath() @@ -61,7 +88,9 @@ public function testGetHrefWithoutSetIdPath() } /** - * @expectedException \RuntimeException + * Tests getHref with wrong id_path + * + * @expectedException RuntimeException * @expectedExceptionMessage Wrong id_path structure. */ public function testGetHrefIfSetWrongIdPath() @@ -70,27 +99,30 @@ public function testGetHrefIfSetWrongIdPath() $this->block->getHref(); } + /** + * Tests getHref with wrong store ID + * + * @expectedException Exception + */ public function testGetHrefWithSetStoreId() { $this->block->setData('id_path', 'type/id'); $this->block->setData('store_id', 'store_id'); - $this->storeManager->expects($this->once()) - ->method('getStore')->with('store_id') - // interrupt test execution - ->will($this->throwException(new \Exception())); - - try { - $this->block->getHref(); - } catch (\Exception $e) { - } + ->method('getStore') + ->with('store_id') + ->will($this->throwException(new Exception())); + $this->block->getHref(); } + /** + * Tests getHref with not found URL + */ public function testGetHrefIfRewriteIsNotFound() { $this->block->setData('id_path', 'entity_type/entity_id'); - $store = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getId', '__wakeUp']); + $store = $this->createPartialMock(Store::class, ['getId', '__wakeUp']); $store->expects($this->any()) ->method('getId'); @@ -105,52 +137,94 @@ public function testGetHrefIfRewriteIsNotFound() } /** - * @param string $url - * @param string $separator + * Tests getHref whether it should include the store code or not + * * @dataProvider dataProviderForTestGetHrefWithoutUrlStoreSuffix + * @param string $path + * @param bool $includeStoreCode + * @param string $expected + * @throws \ReflectionException */ - public function testGetHrefWithoutUrlStoreSuffix($url, $separator) - { - $storeId = 15; - $storeCode = 'store-code'; - $requestPath = 'request-path'; + public function testStoreCodeShouldBeIncludedInURLOnlyIfItIsConfiguredSo( + string $path, + bool $includeStoreCode, + string $expected + ) { $this->block->setData('id_path', 'entity_type/entity_id'); - - $rewrite = $this->createMock(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class); - $rewrite->expects($this->once()) - ->method('getRequestPath') - ->will($this->returnValue($requestPath)); - - $store = $this->createPartialMock( - \Magento\Store\Model\Store::class, - ['getId', 'getUrl', 'getCode', '__wakeUp'] + $objectManager = new ObjectManager($this); + + $rewrite = $this->createPartialMock(UrlRewrite::class, ['getRequestPath']); + $url = $this->createPartialMock(Url::class, ['setScope', 'getUrl']); + $urlModifier = $this->getMockForAbstractClass(ModifierInterface::class); + $config = $this->getMockForAbstractClass(ReinitableConfigInterface::class); + $store = $objectManager->getObject( + Store::class, + [ + 'storeManager' => $this->storeManager, + 'url' => $url, + 'config' => $config + ] ); - $store->expects($this->once()) - ->method('getId') - ->will($this->returnValue($storeId)); - $store->expects($this->once()) + $property = (new ReflectionClass(get_class($store)))->getProperty('urlModifier'); + $property->setAccessible(true); + $property->setValue($store, $urlModifier); + + $urlModifier->expects($this->any()) + ->method('execute') + ->willReturnArgument(0); + $config->expects($this->any()) + ->method('getValue') + ->willReturnMap( + [ + [Store::XML_PATH_USE_REWRITES, ReinitableConfigInterface::SCOPE_TYPE_DEFAULT, null, true], + [ + Store::XML_PATH_STORE_IN_URL, + ReinitableConfigInterface::SCOPE_TYPE_DEFAULT, + null, $includeStoreCode + ] + ] + ); + + $url->expects($this->any()) + ->method('setScope') + ->willReturnSelf(); + + $url->expects($this->any()) ->method('getUrl') - ->with('', ['_direct' => $requestPath]) - ->will($this->returnValue($url)); - $store->expects($this->once()) - ->method('getCode') - ->will($this->returnValue($storeCode)); + ->willReturnCallback( + function ($route, $params) use ($store) { + return rtrim($store->getBaseUrl(), '/') .'/'. ltrim($params['_direct'], '/'); + } + ); - $this->storeManager->expects($this->once()) + $store->addData(['store_id' => 1, 'code' => 'french']); + + $this->storeManager + ->expects($this->any()) ->method('getStore') - ->will($this->returnValue($store)); + ->willReturn($store); - $this->urlFinder->expects($this->once())->method('findOneByData') - ->with([ + $this->urlFinder->expects($this->once()) + ->method('findOneByData') + ->with( + [ UrlRewrite::ENTITY_ID => 'entity_id', UrlRewrite::ENTITY_TYPE => 'entity_type', - UrlRewrite::STORE_ID => $storeId, - ]) + UrlRewrite::STORE_ID => $store->getStoreId(), + ] + ) ->will($this->returnValue($rewrite)); - $this->assertEquals($url . $separator . '___store=' . $storeCode, $this->block->getHref()); + $rewrite->expects($this->once()) + ->method('getRequestPath') + ->will($this->returnValue($path)); + + $this->assertContains($expected, $this->block->getHref()); } + /** + * Tests getLabel with custom text + */ public function testGetLabelWithCustomText() { $customText = 'Some text'; @@ -158,6 +232,9 @@ public function testGetLabelWithCustomText() $this->assertEquals($customText, $this->block->getLabel()); } + /** + * Tests getLabel without custom text + */ public function testGetLabelWithoutCustomText() { $category = 'Some text'; @@ -178,17 +255,20 @@ public function testGetLabelWithoutCustomText() public function dataProviderForTestGetHrefWithoutUrlStoreSuffix() { return [ - ['url', '?'], - ['url?some_parameter', '&'], + ['/accessories.html', true, 'french/accessories.html'], + ['/accessories.html', false, '/accessories.html'], ]; } + /** + * Tests getHref with product entity and additional category id in the id_path + */ public function testGetHrefWithForProductWithCategoryIdParameter() { $storeId = 15; $this->block->setData('id_path', ProductUrlRewriteGenerator::ENTITY_TYPE . '/entity_id/category_id'); - $store = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getId', '__wakeUp']); + $store = $this->createPartialMock(Store::class, ['getId', '__wakeUp']); $store->expects($this->any()) ->method('getId') ->will($this->returnValue($storeId)); @@ -197,13 +277,16 @@ public function testGetHrefWithForProductWithCategoryIdParameter() ->method('getStore') ->will($this->returnValue($store)); - $this->urlFinder->expects($this->once())->method('findOneByData') - ->with([ - UrlRewrite::ENTITY_ID => 'entity_id', - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::STORE_ID => $storeId, - UrlRewrite::METADATA => ['category_id' => 'category_id'], - ]) + $this->urlFinder->expects($this->once()) + ->method('findOneByData') + ->with( + [ + UrlRewrite::ENTITY_ID => 'entity_id', + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::STORE_ID => $storeId, + UrlRewrite::METADATA => ['category_id' => 'category_id'], + ] + ) ->will($this->returnValue(false)); $this->block->getHref(); From 7211d50998f786dce0628346439649ac8d196c46 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Thu, 5 Sep 2019 14:40:08 -0500 Subject: [PATCH 14/55] MC-19440: Bug generating a country list in Admin when Country is restricted - fixed --- .../view/adminhtml/ui_component/customer_address_form.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index 692cb2ecb964..3af0172b3fca 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -191,13 +191,6 @@ </validation> <dataType>text</dataType> </settings> - <formElements> - <select> - <settings> - <options class="Magento\Directory\Model\ResourceModel\Country\Collection"/> - </settings> - </select> - </formElements> </field> <field name="region_id" component="Magento_Customer/js/form/element/region" formElement="select"> <settings> From 433b517b5caad4b74a56b232801d9a5048b89351 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Thu, 5 Sep 2019 15:04:44 -0500 Subject: [PATCH 15/55] MC-19713: Relative Category links created using PageBuilder have the store name as a URL parameter - Fix category/product link to different store should have a store code in the URL --- .../Magento/Catalog/Block/Widget/Link.php | 20 ++++++++++++ .../Test/Unit/Block/Widget/LinkTest.php | 32 +++++++++++++++---- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Widget/Link.php b/app/code/Magento/Catalog/Block/Widget/Link.php index 3d8a1cdf91ca..a25af297111d 100644 --- a/app/code/Magento/Catalog/Block/Widget/Link.php +++ b/app/code/Magento/Catalog/Block/Widget/Link.php @@ -89,12 +89,32 @@ public function getHref() if ($rewrite) { $href = $store->getUrl('', ['_direct' => $rewrite->getRequestPath()]); + + if ($this->addStoreCodeParam($store, $href)) { + $href .= (strpos($href, '?') === false ? '?' : '&') . '___store=' . $store->getCode(); + } } $this->_href = $href; } return $this->_href; } + /** + * Checks whether store code query param should be appended to the URL + * + * @param \Magento\Store\Model\Store $store + * @param string $url + * @return bool + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function addStoreCodeParam(\Magento\Store\Model\Store $store, string $url): bool + { + return $this->getStoreId() + && !$store->isUseStoreInUrl() + && $store->getId() !== $this->_storeManager->getStore()->getId() + && strpos($url, '___store') === false; + } + /** * Parse id_path * diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Widget/LinkTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Widget/LinkTest.php index 3ceaf7dd44f5..dcbd3161733a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Widget/LinkTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Widget/LinkTest.php @@ -141,16 +141,19 @@ public function testGetHrefIfRewriteIsNotFound() * * @dataProvider dataProviderForTestGetHrefWithoutUrlStoreSuffix * @param string $path + * @param int|null $storeId * @param bool $includeStoreCode * @param string $expected * @throws \ReflectionException */ public function testStoreCodeShouldBeIncludedInURLOnlyIfItIsConfiguredSo( string $path, + ?int $storeId, bool $includeStoreCode, string $expected ) { $this->block->setData('id_path', 'entity_type/entity_id'); + $this->block->setData('store_id', $storeId); $objectManager = new ObjectManager($this); $rewrite = $this->createPartialMock(UrlRewrite::class, ['getRequestPath']); @@ -192,17 +195,27 @@ public function testStoreCodeShouldBeIncludedInURLOnlyIfItIsConfiguredSo( $url->expects($this->any()) ->method('getUrl') ->willReturnCallback( - function ($route, $params) use ($store) { - return rtrim($store->getBaseUrl(), '/') .'/'. ltrim($params['_direct'], '/'); + function ($route, $params) use ($storeId) { + $baseUrl = rtrim($this->storeManager->getStore($storeId)->getBaseUrl(), '/'); + return $baseUrl .'/' . ltrim($params['_direct'], '/'); } ); $store->addData(['store_id' => 1, 'code' => 'french']); + $store2 = clone $store; + $store2->addData(['store_id' => 2, 'code' => 'german']); + $this->storeManager ->expects($this->any()) ->method('getStore') - ->willReturn($store); + ->willReturnMap( + [ + [null, $store], + [1, $store], + [2, $store2], + ] + ); $this->urlFinder->expects($this->once()) ->method('findOneByData') @@ -210,7 +223,7 @@ function ($route, $params) use ($store) { [ UrlRewrite::ENTITY_ID => 'entity_id', UrlRewrite::ENTITY_TYPE => 'entity_type', - UrlRewrite::STORE_ID => $store->getStoreId(), + UrlRewrite::STORE_ID => $this->storeManager->getStore($storeId)->getStoreId(), ] ) ->will($this->returnValue($rewrite)); @@ -219,7 +232,7 @@ function ($route, $params) use ($store) { ->method('getRequestPath') ->will($this->returnValue($path)); - $this->assertContains($expected, $this->block->getHref()); + $this->assertEquals($expected, $this->block->getHref()); } /** @@ -255,8 +268,13 @@ public function testGetLabelWithoutCustomText() public function dataProviderForTestGetHrefWithoutUrlStoreSuffix() { return [ - ['/accessories.html', true, 'french/accessories.html'], - ['/accessories.html', false, '/accessories.html'], + ['/accessories.html', null, true, 'french/accessories.html'], + ['/accessories.html', null, false, '/accessories.html'], + ['/accessories.html', 1, true, 'french/accessories.html'], + ['/accessories.html', 1, false, '/accessories.html'], + ['/accessories.html', 2, true, 'german/accessories.html'], + ['/accessories.html', 2, false, '/accessories.html?___store=german'], + ['/accessories.html?___store=german', 2, false, '/accessories.html?___store=german'], ]; } From 17f291b465b5481ffa0ca221593114db01ff8ae2 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Thu, 5 Sep 2019 14:21:27 -0500 Subject: [PATCH 16/55] MC-19749: Store creation ('app:config:import' with pre-defined stores) fails during upgrade - Fix object manager provider reset causes multiple connection to database --- .../Setup/Model/ObjectManagerProviderTest.php | 31 +++++++++++++--- .../Magento/Framework/Console/Cli.php | 1 + .../Setup/Model/ObjectManagerProvider.php | 5 ++- .../Unit/Model/ObjectManagerProviderTest.php | 35 ++++++++++--------- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php index c4fc0fa7c585..a80da16be67e 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/ObjectManagerProviderTest.php @@ -6,9 +6,17 @@ namespace Magento\Setup\Model; +use Magento\Framework\ObjectManagerInterface; use Magento\Setup\Mvc\Bootstrap\InitParamListener; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject; +use Symfony\Component\Console\Application; +use Zend\ServiceManager\ServiceLocatorInterface; -class ObjectManagerProviderTest extends \PHPUnit\Framework\TestCase +/** + * Tests ObjectManagerProvider + */ +class ObjectManagerProviderTest extends TestCase { /** * @var ObjectManagerProvider @@ -16,21 +24,34 @@ class ObjectManagerProviderTest extends \PHPUnit\Framework\TestCase private $object; /** - * @var \Zend\ServiceManager\ServiceLocatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ServiceLocatorInterface|PHPUnit_Framework_MockObject_MockObject */ private $locator; + /** + * @inheritDoc + */ protected function setUp() { - $this->locator = $this->getMockForAbstractClass(\Zend\ServiceManager\ServiceLocatorInterface::class); + $this->locator = $this->getMockForAbstractClass(ServiceLocatorInterface::class); $this->object = new ObjectManagerProvider($this->locator, new Bootstrap()); + $this->locator->expects($this->any()) + ->method('get') + ->willReturnMap( + [ + [InitParamListener::BOOTSTRAP_PARAM, []], + [Application::class, $this->getMockForAbstractClass(Application::class)], + ] + ); } + /** + * Tests the same instance of ObjectManagerInterface should be provided by the ObjectManagerProvider + */ public function testGet() { - $this->locator->expects($this->once())->method('get')->with(InitParamListener::BOOTSTRAP_PARAM)->willReturn([]); $objectManager = $this->object->get(); - $this->assertInstanceOf(\Magento\Framework\ObjectManagerInterface::class, $objectManager); + $this->assertInstanceOf(ObjectManagerInterface::class, $objectManager); $this->assertSame($objectManager, $this->object->get()); } } diff --git a/lib/internal/Magento/Framework/Console/Cli.php b/lib/internal/Magento/Framework/Console/Cli.php index 2ef41f361027..34fd6316ce45 100644 --- a/lib/internal/Magento/Framework/Console/Cli.php +++ b/lib/internal/Magento/Framework/Console/Cli.php @@ -93,6 +93,7 @@ public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') } parent::__construct($name, $version); + $this->serviceManager->setService(\Symfony\Component\Console\Application::class, $this); } /** diff --git a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php index e25b976e9207..79216c8ec89b 100644 --- a/setup/src/Magento/Setup/Model/ObjectManagerProvider.php +++ b/setup/src/Magento/Setup/Model/ObjectManagerProvider.php @@ -76,10 +76,9 @@ private function createCliCommands() { /** @var CommandListInterface $commandList */ $commandList = $this->objectManager->create(CommandListInterface::class); + $application = $this->serviceLocator->get(Application::class); foreach ($commandList->getCommands() as $command) { - $command->setApplication( - $this->serviceLocator->get(Application::class) - ); + $application->add($command); } } diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php index 9d40b053e394..552453c4a185 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ObjectManagerProviderTest.php @@ -47,6 +47,14 @@ public function setUp() public function testGet() { $initParams = ['param' => 'value']; + $commands = [ + new Command('setup:install'), + new Command('setup:upgrade'), + ]; + + $application = $this->getMockBuilder(Application::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); $this->serviceLocatorMock ->expects($this->atLeastOnce()) @@ -56,16 +64,21 @@ public function testGet() [InitParamListener::BOOTSTRAP_PARAM, $initParams], [ Application::class, - $this->getMockBuilder(Application::class)->disableOriginalConstructor()->getMock(), + $application, ], ] ); + $commandListMock = $this->createMock(CommandListInterface::class); + $commandListMock->expects($this->once()) + ->method('getCommands') + ->willReturn($commands); + $objectManagerMock = $this->createMock(ObjectManagerInterface::class); $objectManagerMock->expects($this->once()) ->method('create') ->with(CommandListInterface::class) - ->willReturn($this->getCommandListMock()); + ->willReturn($commandListMock); $objectManagerFactoryMock = $this->getMockBuilder(ObjectManagerFactory::class) ->disableOriginalConstructor() @@ -81,21 +94,9 @@ public function testGet() ->willReturn($objectManagerFactoryMock); $this->assertInstanceOf(ObjectManagerInterface::class, $this->model->get()); - } - - /** - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getCommandListMock() - { - $commandMock = $this->getMockBuilder(Command::class)->disableOriginalConstructor()->getMock(); - $commandMock->expects($this->once())->method('setApplication'); - - $commandListMock = $this->createMock(CommandListInterface::class); - $commandListMock->expects($this->once()) - ->method('getCommands') - ->willReturn([$commandMock]); - return $commandListMock; + foreach ($commands as $command) { + $this->assertSame($application, $command->getApplication()); + } } } From 2f04e666aceba9bcbd863b78613714922a1f0f35 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Fri, 6 Sep 2019 12:42:04 -0500 Subject: [PATCH 17/55] MC-19798: 'Quote Lifetime (days)' setting does not work - fixed --- app/code/Magento/Sales/Cron/CleanExpiredQuotes.php | 1 - .../Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php b/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php index 021e7b66cd13..a5c7f71df66c 100644 --- a/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php +++ b/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php @@ -57,7 +57,6 @@ public function execute() $quotes->addFieldToFilter('store_id', $storeId); $quotes->addFieldToFilter('updated_at', ['to' => date("Y-m-d", time() - $lifetime)]); - $quotes->addFieldToFilter('is_active', 0); foreach ($this->getExpireQuotesAdditionalFilterFields() as $field => $condition) { $quotes->addFieldToFilter($field, $condition); diff --git a/app/code/Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php b/app/code/Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php index e424cae85f22..ad6a3e03ba67 100644 --- a/app/code/Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php +++ b/app/code/Magento/Sales/Test/Unit/Cron/CleanExpiredQuotesTest.php @@ -59,7 +59,7 @@ public function testExecute($lifetimes, $additionalFilterFields) $this->quoteFactoryMock->expects($this->exactly(count($lifetimes))) ->method('create') ->will($this->returnValue($quotesMock)); - $quotesMock->expects($this->exactly((3 + count($additionalFilterFields)) * count($lifetimes))) + $quotesMock->expects($this->exactly((2 + count($additionalFilterFields)) * count($lifetimes))) ->method('addFieldToFilter'); if (!empty($lifetimes)) { $quotesMock->expects($this->exactly(count($lifetimes))) From 41ef34feca73eea48f3dd1744bce529d36909c91 Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Sat, 7 Sep 2019 14:34:35 +0200 Subject: [PATCH 18/55] Fixes excluding JS files from bundles when minifying is enabled. --- app/code/Magento/Deploy/Service/Bundle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Deploy/Service/Bundle.php b/app/code/Magento/Deploy/Service/Bundle.php index f16b93a18559..26e61624c219 100644 --- a/app/code/Magento/Deploy/Service/Bundle.php +++ b/app/code/Magento/Deploy/Service/Bundle.php @@ -216,7 +216,7 @@ private function isExcluded($filePath, $area, $theme) $excludedFiles = $this->bundleConfig->getExcludedFiles($area, $theme); foreach ($excludedFiles as $excludedFileId) { $excludedFilePath = $this->prepareExcludePath($excludedFileId); - if ($excludedFilePath === $filePath) { + if ($excludedFilePath === $filePath || $excludedFilePath === str_replace('.min.js', '.js', $filePath)) { return true; } } From a7bec1ae86cbc65f7ecef0133272054ed742265a Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Sun, 8 Sep 2019 19:14:44 -0500 Subject: [PATCH 19/55] MC-19904: Start/End Date time is changed after event saving in event edit page --- .../Framework/Stdlib/DateTime/Timezone.php | 26 +++--- .../Test/Unit/DateTime/TimezoneTest.php | 87 +++++++++++++++---- 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 014854ddd584..118a3e053bd7 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -195,16 +195,16 @@ public function date($date = null, $locale = null, $useTimezone = true, $include */ public function scopeDate($scope = null, $date = null, $includeTime = false) { - $timezone = $this->_scopeConfig->getValue($this->getDefaultTimezonePath(), $this->_scopeType, $scope); + $timezone = new \DateTimeZone( + $this->_scopeConfig->getValue($this->getDefaultTimezonePath(), $this->_scopeType, $scope) + ); switch (true) { case (empty($date)): - $date = new \DateTime('now', new \DateTimeZone($timezone)); + $date = new \DateTime('now', $timezone); break; case ($date instanceof \DateTime): - $date = $date->setTimezone(new \DateTimeZone($timezone)); - break; case ($date instanceof \DateTimeImmutable): - $date = new \DateTime($date->format('Y-m-d H:i:s'), $date->getTimezone()); + $date = $date->setTimezone($timezone); break; case (!is_numeric($date)): $timeType = $includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE; @@ -212,14 +212,20 @@ public function scopeDate($scope = null, $date = null, $includeTime = false) $this->_localeResolver->getLocale(), \IntlDateFormatter::SHORT, $timeType, - new \DateTimeZone($timezone) + $timezone ); - $date = $formatter->parse($date) ?: (new \DateTime($date))->getTimestamp(); - $date = (new \DateTime(null, new \DateTimeZone($timezone)))->setTimestamp($date); + $timestamp = $formatter->parse($date); + $date = $timestamp + ? (new \DateTime('@' . $timestamp))->setTimezone($timezone) + : new \DateTime($date, $timezone); + break; + case (is_numeric($date)): + $date = new \DateTime('@' . $date); + $date = $date->setTimezone($timezone); break; default: - $date = new \DateTime(is_numeric($date) ? '@' . $date : $date); - $date->setTimezone(new \DateTimeZone($timezone)); + $date = new \DateTime($date, $timezone); + break; } if (!$includeTime) { diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php index 3d7d14a39462..53980e574c26 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php @@ -22,6 +22,16 @@ class TimezoneTest extends \PHPUnit\Framework\TestCase */ private $defaultTimeZone; + /** + * @var string + */ + private $scopeType; + + /** + * @var string + */ + private $defaultTimezonePath; + /** * @var ObjectManager */ @@ -49,6 +59,8 @@ protected function setUp() { $this->defaultTimeZone = date_default_timezone_get(); date_default_timezone_set('UTC'); + $this->scopeType = 'store'; + $this->defaultTimezonePath = 'default/timezone/path'; $this->objectManager = new ObjectManager($this); $this->scopeResolver = $this->getMockBuilder(ScopeResolverInterface::class)->getMock(); @@ -86,9 +98,10 @@ public function testDateIncludeTime($date, $locale, $includeTime, $expectedTimes /** * DataProvider for testDateIncludeTime + * * @return array */ - public function dateIncludeTimeDataProvider() + public function dateIncludeTimeDataProvider(): array { return [ 'Parse d/m/y date without time' => [ @@ -133,9 +146,10 @@ public function testConvertConfigTimeToUtc($date, $configuredTimezone, $expected /** * Data provider for testConvertConfigTimeToUtc + * * @return array */ - public function getConvertConfigTimeToUtcFixtures() + public function getConvertConfigTimeToUtcFixtures(): array { return [ 'string' => [ @@ -181,9 +195,10 @@ public function testDate() /** * DataProvider for testDate + * * @return array */ - private function getDateFixtures() + private function getDateFixtures(): array { return [ 'now_datetime_utc' => [ @@ -239,29 +254,71 @@ private function getTimezone() return new Timezone( $this->scopeResolver, $this->localeResolver, - $this->getMockBuilder(DateTime::class)->getMock(), + $this->createMock(DateTime::class), $this->scopeConfig, - '', - '' + $this->scopeType, + $this->defaultTimezonePath ); } /** * @param string $configuredTimezone + * @param string|null $scope */ - private function scopeConfigWillReturnConfiguredTimezone($configuredTimezone) + private function scopeConfigWillReturnConfiguredTimezone(string $configuredTimezone, string $scope = null) { - $this->scopeConfig->method('getValue')->with('', '', null)->willReturn($configuredTimezone); + $this->scopeConfig->expects($this->atLeastOnce()) + ->method('getValue') + ->with($this->defaultTimezonePath, $this->scopeType, $scope) + ->willReturn($configuredTimezone); } - public function testCheckIfScopeDateSetsTimeZone() + /** + * @dataProvider scopeDateDataProvider + * @param \DateTimeInterface|string|int $date + * @param string $timezone + * @param string $locale + * @param string $expectedDate + */ + public function testScopeDate($date, string $timezone, string $locale, string $expectedDate) { - $scopeDate = new \DateTime('now', new \DateTimeZone('America/Vancouver')); - $this->scopeConfig->method('getValue')->willReturn('America/Vancouver'); + $scopeCode = 'test'; - $this->assertEquals( - $scopeDate->getTimezone(), - $this->getTimezone()->scopeDate(0, $scopeDate->getTimestamp())->getTimezone() - ); + $this->scopeConfigWillReturnConfiguredTimezone($timezone, $scopeCode); + $this->localeResolver->method('getLocale') + ->willReturn($locale); + + $scopeDate = $this->getTimezone()->scopeDate($scopeCode, $date, true); + $this->assertEquals($expectedDate, $scopeDate->format('Y-m-d H:i:s')); + $this->assertEquals($timezone, $scopeDate->getTimezone()->getName()); + } + + /** + * @return array + */ + public function scopeDateDataProvider(): array + { + $utcTz = new \DateTimeZone('UTC'); + + return [ + ['2018-10-20 00:00:00', 'UTC', 'en_US', '2018-10-20 00:00:00'], + ['2018-10-20 00:00:00', 'America/Los_Angeles', 'en_US', '2018-10-20 00:00:00'], + ['2018-10-20 00:00:00', 'Asia/Qatar', 'en_US', '2018-10-20 00:00:00'], + ['10/20/18 00:00', 'UTC', 'en_US', '2018-10-20 00:00:00'], + ['10/20/18 00:00', 'America/Los_Angeles', 'en_US', '2018-10-20 00:00:00'], + ['10/20/18 00:00', 'Asia/Qatar', 'en_US', '2018-10-20 00:00:00'], + ['20/10/18 00:00', 'UTC', 'fr_FR', '2018-10-20 00:00:00'], + ['20/10/18 00:00', 'America/Los_Angeles', 'fr_FR', '2018-10-20 00:00:00'], + ['20/10/18 00:00', 'Asia/Qatar', 'fr_FR', '2018-10-20 00:00:00'], + [1539993600, 'UTC', 'en_US', '2018-10-20 00:00:00'], + [1539993600, 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'], + [1539993600, 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'], + [new \DateTime('2018-10-20', $utcTz), 'UTC', 'en_US', '2018-10-20 00:00:00'], + [new \DateTime('2018-10-20', $utcTz), 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'], + [new \DateTime('2018-10-20', $utcTz), 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'], + [new \DateTimeImmutable('2018-10-20', $utcTz), 'UTC', 'en_US', '2018-10-20 00:00:00'], + [new \DateTimeImmutable('2018-10-20', $utcTz), 'America/Los_Angeles', 'en_US', '2018-10-19 17:00:00'], + [new \DateTimeImmutable('2018-10-20', $utcTz), 'Asia/Qatar', 'en_US', '2018-10-20 03:00:00'], + ]; } } From cbe25f4960b59feed344eebee25e3dbe2f3efaaa Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Sat, 7 Sep 2019 14:51:53 +0200 Subject: [PATCH 20/55] Cleaned up non-existing files from being excluded from bundling. --- app/design/adminhtml/Magento/backend/etc/view.xml | 9 --------- app/design/frontend/Magento/blank/etc/view.xml | 2 -- app/design/frontend/Magento/luma/etc/view.xml | 2 -- 3 files changed, 13 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/etc/view.xml b/app/design/adminhtml/Magento/backend/etc/view.xml index 18c2d8f1b172..6539e3330e98 100644 --- a/app/design/adminhtml/Magento/backend/etc/view.xml +++ b/app/design/adminhtml/Magento/backend/etc/view.xml @@ -24,7 +24,6 @@ </media> <exclude> <item type="file">Lib::mage/captcha.js</item> - <item type="file">Lib::mage/captcha.min.js</item> <item type="file">Lib::mage/common.js</item> <item type="file">Lib::mage/cookies.js</item> <item type="file">Lib::mage/dataPost.js</item> @@ -46,7 +45,6 @@ <item type="file">Lib::mage/translate-inline-vde.js</item> <item type="file">Lib::mage/webapi.js</item> <item type="file">Lib::mage/zoom.js</item> - <item type="file">Lib::mage/validation/dob-rule.js</item> <item type="file">Lib::mage/validation/validation.js</item> <item type="file">Lib::mage/adminhtml/varienLoader.js</item> <item type="file">Lib::mage/adminhtml/tools.js</item> @@ -57,11 +55,9 @@ <item type="file">Lib::jquery/jquery.parsequery.js</item> <item type="file">Lib::jquery/jquery.mobile.custom.js</item> <item type="file">Lib::jquery/jquery-ui.js</item> - <item type="file">Lib::jquery/jquery-ui.min.js</item> <item type="file">Lib::matchMedia.js</item> <item type="file">Lib::requirejs/require.js</item> <item type="file">Lib::requirejs/text.js</item> - <item type="file">Lib::date-format-normalizer.js</item> <item type="file">Lib::varien/js.js</item> <item type="directory">Lib::css</item> <item type="directory">Lib::lib</item> @@ -72,10 +68,5 @@ <item type="directory">Lib::fotorama</item> <item type="directory">Lib::magnifier</item> <item type="directory">Lib::tiny_mce</item> - <item type="directory">Lib::tiny_mce/classes</item> - <item type="directory">Lib::tiny_mce/langs</item> - <item type="directory">Lib::tiny_mce/plugins</item> - <item type="directory">Lib::tiny_mce/themes</item> - <item type="directory">Lib::tiny_mce/utils</item> </exclude> </view> diff --git a/app/design/frontend/Magento/blank/etc/view.xml b/app/design/frontend/Magento/blank/etc/view.xml index e742ce0a21cd..dfc5ad39af55 100644 --- a/app/design/frontend/Magento/blank/etc/view.xml +++ b/app/design/frontend/Magento/blank/etc/view.xml @@ -262,12 +262,10 @@ <item type="file">Lib::jquery/jquery.min.js</item> <item type="file">Lib::jquery/jquery-ui-1.9.2.js</item> <item type="file">Lib::jquery/jquery.details.js</item> - <item type="file">Lib::jquery/jquery.details.min.js</item> <item type="file">Lib::jquery/jquery.hoverIntent.js</item> <item type="file">Lib::jquery/colorpicker/js/colorpicker.js</item> <item type="file">Lib::requirejs/require.js</item> <item type="file">Lib::requirejs/text.js</item> - <item type="file">Lib::date-format-normalizer.js</item> <item type="file">Lib::legacy-build.min.js</item> <item type="file">Lib::mage/captcha.js</item> <item type="file">Lib::mage/dropdown_old.js</item> diff --git a/app/design/frontend/Magento/luma/etc/view.xml b/app/design/frontend/Magento/luma/etc/view.xml index 7aa2e51481bd..c04c0d73cb3c 100644 --- a/app/design/frontend/Magento/luma/etc/view.xml +++ b/app/design/frontend/Magento/luma/etc/view.xml @@ -273,12 +273,10 @@ <item type="file">Lib::jquery/jquery.min.js</item> <item type="file">Lib::jquery/jquery-ui-1.9.2.js</item> <item type="file">Lib::jquery/jquery.details.js</item> - <item type="file">Lib::jquery/jquery.details.min.js</item> <item type="file">Lib::jquery/jquery.hoverIntent.js</item> <item type="file">Lib::jquery/colorpicker/js/colorpicker.js</item> <item type="file">Lib::requirejs/require.js</item> <item type="file">Lib::requirejs/text.js</item> - <item type="file">Lib::date-format-normalizer.js</item> <item type="file">Lib::legacy-build.min.js</item> <item type="file">Lib::mage/captcha.js</item> <item type="file">Lib::mage/dropdown_old.js</item> From a2f41c7455588d631f20e1cb7ffec879312abfd1 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Mon, 9 Sep 2019 14:44:12 -0500 Subject: [PATCH 21/55] MC-17948: Newsletter template preview show small section with scroll when image added --- .../adminhtml/Magento/backend/web/css/styles-old.less | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index 53af8933343f..44fca79c31be 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -4060,6 +4060,16 @@ } } +.newsletter-template-preview { + height: 100%; + .cms-revision-preview { + height: 100%; + .preview_iframe { + height: calc(~'100% - 50px'); + } + } +} + .admin__scope-old { .buttons-set { margin: 0 0 15px; From e3bedcf838dac3a965b11c75d5f6e1688e1426be Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Mon, 9 Sep 2019 16:12:23 -0500 Subject: [PATCH 22/55] MC-17948: Newsletter template preview show small section with scroll when image added --- .../view/adminhtml/templates/preview/iframeswitcher.phtml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Newsletter/view/adminhtml/templates/preview/iframeswitcher.phtml b/app/code/Magento/Newsletter/view/adminhtml/templates/preview/iframeswitcher.phtml index 5175080add91..20ff63a60a26 100644 --- a/app/code/Magento/Newsletter/view/adminhtml/templates/preview/iframeswitcher.phtml +++ b/app/code/Magento/Newsletter/view/adminhtml/templates/preview/iframeswitcher.phtml @@ -17,6 +17,7 @@ <iframe name="preview_iframe" id="preview_iframe" + class="preview_iframe" frameborder="0" title="<?= $block->escapeHtmlAttr(__('Preview')) ?>" width="100%" From 3acfc94d0d6d0dfa544c2317d26daab5978e9193 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 28 Aug 2019 12:52:03 +0300 Subject: [PATCH 23/55] magento/magento2#23966: WYSIWYG image upload dialog hangs up. --- .../Test/AdminAddImageToWYSIWYGProductTest.xml | 5 ++--- .../Test/AdminAddImageToWYSIWYGBlockTest.xml | 3 --- .../Test/AdminAddImageToWYSIWYGCMSTest.xml | 3 --- .../AdminAddImageToWYSIWYGNewsletterTest.xml | 3 --- lib/web/mage/adminhtml/browser.js | 8 ++++++-- .../wysiwyg/tiny_mce/tinymce4Adapter.js | 18 +++++++++++------- 6 files changed, 19 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml index f3d3e653b260..ee105320c5f2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml @@ -16,9 +16,6 @@ <description value="Admin should be able to add image to WYSIWYG Editor on Product Page"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84375"/> - <skip> - <issueId value="MC-17232"/> - </skip> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> @@ -78,6 +75,8 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoading13"/> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn2" /> <waitForLoadingMaskToDisappear stepKey="waitForLoading14"/> + <click userInput="Storage Root" stepKey="clickOnRootFolder" /> + <waitForLoadingMaskToDisappear stepKey="waitForLoading15"/> <dontSeeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn3" /> <attachFile selector="{{ProductShortDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload3.value}}" stepKey="uploadImage3"/> <waitForLoadingMaskToDisappear stepKey="waitForFileUpload3"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index 5baf75d43c53..03edc69e6d62 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -16,9 +16,6 @@ <description value="Admin should be able to add image to WYSIWYG content of Block"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84376"/> - <skip> - <issueId value="MC-17232"/> - </skip> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml index e63a6be51bcc..205850f88879 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml @@ -16,9 +16,6 @@ <description value="Admin should be able to add image to WYSIWYG content of CMS Page"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-85825"/> - <skip> - <issueId value="MC-17232"/> - </skip> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml index 510f3e16e8d8..0371c0265d14 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml @@ -16,9 +16,6 @@ <description value="Admin should be able to add image to WYSIWYG content Newsletter"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84377"/> - <skip> - <issueId value="MC-17233"/> - </skip> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 9019e941bc58..20bcdd36baca 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -52,8 +52,11 @@ define([ content = '<div class="popup-window" id="' + windowId + '"></div>', self = this; - if (this.modalLoaded === true) { - if (options && typeof options.closed !== 'undefined') { + if (this.modalLoaded === true + && options + && self.targetElementId + && self.targetElementId === options.targetElementId) { + if (typeof options.closed !== 'undefined') { this.modal.modal('option', 'closed', options.closed); } this.modal.modal('openModal'); @@ -85,6 +88,7 @@ define([ }).done(function (data) { self.modal.html(data).trigger('contentUpdated'); self.modalLoaded = true; + self.targetElementId = options.targetElementId; }); }, diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 227ee10a73e6..4dafc845309c 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -377,6 +377,7 @@ define([ var typeTitle = this.translate('Select Images'), storeId = this.config['store_id'] !== null ? this.config['store_id'] : 0, frameDialog = jQuery('div.mce-container[role="dialog"]'), + self = this, wUrl = this.config['files_browser_window_url'] + 'target_element_id/' + this.getId() + '/' + 'store/' + storeId + '/'; @@ -393,14 +394,17 @@ define([ require(['mage/adminhtml/browser'], function () { MediabrowserUtility.openDialog(wUrl, false, false, typeTitle, { - /** - * Closed. - */ - closed: function () { - frameDialog.show(); - jQuery('#mce-modal-block').show(); + /** + * Closed. + */ + closed: function () { + frameDialog.show(); + jQuery('#mce-modal-block').show(); + }, + + targetElementId: self.activeEditor() ? self.activeEditor().id : null } - }); + ); }); }, From 091eb7b900fd8292dec57ab312d7e79c3ba524c6 Mon Sep 17 00:00:00 2001 From: Yevhen Miroshnychenko <ymiroshnychenko@magento.com> Date: Tue, 10 Sep 2019 14:21:07 -0500 Subject: [PATCH 24/55] MC-19421: Reduce q-ty of Reports Created in /app/*/var/report --- app/etc/di.xml | 1 + .../Magento/Framework/Error/ProcessorTest.php | 122 ++++++-- .../Rule/Design/AllPurposeAction.php | 2 +- .../Framework/App/ExceptionHandler.php | 284 ++++++++++++++++++ .../App/ExceptionHandlerInterface.php | 31 ++ lib/internal/Magento/Framework/App/Http.php | 243 ++------------- .../App/Test/Unit/ExceptionHandlerTest.php | 277 +++++++++++++++++ .../Framework/App/Test/Unit/HttpTest.php | 115 +------ .../App/Test/Unit/_files/pub/errors/404.php | 6 + .../Test/Unit/_files/pub/errors/report.php | 6 + pub/errors/local.xml.sample | 17 ++ pub/errors/processor.php | 186 ++++++++++-- 12 files changed, 910 insertions(+), 380 deletions(-) create mode 100644 lib/internal/Magento/Framework/App/ExceptionHandler.php create mode 100644 lib/internal/Magento/Framework/App/ExceptionHandlerInterface.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/ExceptionHandlerTest.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/_files/pub/errors/404.php create mode 100644 lib/internal/Magento/Framework/App/Test/Unit/_files/pub/errors/report.php diff --git a/app/etc/di.xml b/app/etc/di.xml index 50088d41f1b4..882d1be62398 100644 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -47,6 +47,7 @@ <preference for="Magento\Framework\App\RequestSafetyInterface" type="Magento\Framework\App\Request\Http" /> <preference for="\Magento\Framework\Setup\SchemaSetupInterface" type="\Magento\Setup\Module\Setup" /> <preference for="\Magento\Framework\Setup\ModuleDataSetupInterface" type="\Magento\Setup\Module\DataSetup" /> + <preference for="Magento\Framework\App\ExceptionHandlerInterface" type="Magento\Framework\App\ExceptionHandler" /> <type name="Magento\Store\Model\Store"> <arguments> <argument name="currencyInstalled" xsi:type="string">system/currency/installed</argument> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Error/ProcessorTest.php b/dev/tests/integration/testsuite/Magento/Framework/Error/ProcessorTest.php index 515e1a898dfa..51441e487953 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Error/ProcessorTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Error/ProcessorTest.php @@ -5,52 +5,136 @@ */ namespace Magento\Framework\Error; +use Magento\TestFramework\Helper\Bootstrap; + require_once __DIR__ . '/../../../../../../../pub/errors/processor.php'; class ProcessorTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Framework\Error\Processor */ + /** + * @var Processor + */ private $processor; + /** + * @inheritdoc + */ public function setUp() { $this->processor = $this->createProcessor(); } + /** + * {@inheritdoc} + * @throws \Exception + */ public function tearDown() { - if ($this->processor->reportId) { - unlink($this->processor->_reportDir . '/' . $this->processor->reportId); - } + $reportDir = $this->processor->_reportDir; + $this->removeDirRecursively($reportDir); } - public function testSaveAndLoadReport() - { + /** + * @param int $logReportDirNestingLevel + * @param int $logReportDirNestingLevelChanged + * @param string $exceptionMessage + * @dataProvider dataProviderSaveAndLoadReport + */ + public function testSaveAndLoadReport( + int $logReportDirNestingLevel, + int $logReportDirNestingLevelChanged, + string $exceptionMessage + ) { + $_ENV['MAGE_LOG_REPORT_DIR_NESTING_LEVEL'] = $logReportDirNestingLevel; $reportData = [ - 0 => 'exceptionMessage', + 0 => $exceptionMessage, 1 => 'exceptionTrace', 'script_name' => 'processor.php' ]; + $reportData['report_id'] = hash('sha256', implode('', $reportData)); $expectedReportData = array_merge($reportData, ['url' => '']); - $this->processor = $this->createProcessor(); - $this->processor->saveReport($reportData); - if (!$this->processor->reportId) { + $processor = $this->createProcessor(); + $processor->saveReport($reportData); + $reportId = $processor->reportId; + if (!$reportId) { $this->fail("Failed to generate report id"); } - $this->assertFileExists($this->processor->_reportDir . '/' . $this->processor->reportId); - $this->assertEquals($expectedReportData, $this->processor->reportData); + $this->assertEquals($expectedReportData, $processor->reportData); + $_ENV['MAGE_LOG_REPORT_DIR_NESTING_LEVEL'] = $logReportDirNestingLevelChanged; + $processor = $this->createProcessor(); + $processor->loadReport($reportId); + $this->assertEquals($expectedReportData, $processor->reportData, "File contents of report don't match"); + } + + /** + * Data Provider for testSaveAndLoadReport + * + * @return array + */ + public function dataProviderSaveAndLoadReport(): array + { + return [ + [ + 'logReportDirNestingLevel' => 0, + 'logReportDirNestingLevelChanged' => 0, + 'exceptionMessage' => '$exceptionMessage 0', + ], + [ + 'logReportDirNestingLevel' => 1, + 'logReportDirNestingLevelChanged' => 1, + 'exceptionMessage' => '$exceptionMessage 1', + ], + [ + 'logReportDirNestingLevel' => 2, + 'logReportDirNestingLevelChanged' => 2, + 'exceptionMessage' => '$exceptionMessage 2', + ], + [ + 'logReportDirNestingLevel' => 3, + 'logReportDirNestingLevelChanged' => 23, + 'exceptionMessage' => '$exceptionMessage 2', + ], + [ + 'logReportDirNestingLevel' => 32, + 'logReportDirNestingLevelChanged' => 32, + 'exceptionMessage' => '$exceptionMessage 3', + ], + [ + 'logReportDirNestingLevel' => 100, + 'logReportDirNestingLevelChanged' => 100, + 'exceptionMessage' => '$exceptionMessage 100', + ], + ]; + } - $loadProcessor = $this->createProcessor(); - $loadProcessor->loadReport($this->processor->reportId); - $this->assertEquals($expectedReportData, $loadProcessor->reportData, "File contents of report don't match"); + /** + * @return Processor + */ + private function createProcessor(): Processor + { + return Bootstrap::getObjectManager()->create(Processor::class); } /** - * @return \Magento\Framework\Error\Processor + * Remove dir recursively + * + * @param string $dir + * @param int $i + * @return bool + * @throws \Exception */ - private function createProcessor() + private function removeDirRecursively(string $dir, int $i = 0): bool { - return \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Framework\Error\Processor::class); + if ($i >= 100) { + throw new \Exception('Emergency exit from recursion'); + } + $files = array_diff(scandir($dir), ['.', '..']); + foreach ($files as $file) { + $i++; + (is_dir("$dir/$file")) + ? $this->removeDirRecursively("$dir/$file", $i) + : unlink("$dir/$file"); + } + return rmdir($dir); } } diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/AllPurposeAction.php b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/AllPurposeAction.php index ca257c1f6eb3..00a6e49d4e31 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/AllPurposeAction.php +++ b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/AllPurposeAction.php @@ -38,7 +38,7 @@ public function apply(AbstractNode $node) return; } - if (in_array(ActionInterface::class, $impl, true)) { + if (is_array($impl) && in_array(ActionInterface::class, $impl, true)) { $methodsDefined = false; foreach ($impl as $i) { if (preg_match('/\\\Http[a-z]+ActionInterface$/i', $i)) { diff --git a/lib/internal/Magento/Framework/App/ExceptionHandler.php b/lib/internal/Magento/Framework/App/ExceptionHandler.php new file mode 100644 index 000000000000..38e85326ef5e --- /dev/null +++ b/lib/internal/Magento/Framework/App/ExceptionHandler.php @@ -0,0 +1,284 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\App; + +use Magento\Framework\App\Response\Http as ResponseHttp; +use Magento\Framework\App\Request\Http as RequestHttp; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Debug; +use Magento\Framework\Filesystem; +use Psr\Log\LoggerInterface; +use Magento\Framework\Exception\SessionException; +use Magento\Framework\Exception\State\InitException; + +/** + * Handler of HTTP web application exception + */ +class ExceptionHandler implements ExceptionHandlerInterface +{ + /** + * @var Filesystem + */ + protected $_filesystem; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var EncryptorInterface + */ + private $encryptor; + + /** + * @param EncryptorInterface $encryptor + * @param Filesystem $filesystem + * @param LoggerInterface $logger + */ + public function __construct( + EncryptorInterface $encryptor, + Filesystem $filesystem, + LoggerInterface $logger + ) { + $this->encryptor = $encryptor; + $this->_filesystem = $filesystem; + $this->logger = $logger; + } + + /** + * Handles exception of HTTP web application + * + * @param Bootstrap $bootstrap + * @param \Exception $exception + * @param ResponseHttp $response + * @param RequestHttp $request + * @return bool + */ + public function handle( + Bootstrap $bootstrap, + \Exception $exception, + ResponseHttp $response, + RequestHttp $request + ): bool { + $result = $this->handleDeveloperMode($bootstrap, $exception, $response) + || $this->handleBootstrapErrors($bootstrap, $exception, $response) + || $this->handleSessionException($exception, $response, $request) + || $this->handleInitException($exception) + || $this->handleGenericReport($bootstrap, $exception); + return $result; + } + + /** + * Error handler for developer mode + * + * @param Bootstrap $bootstrap + * @param \Exception $exception + * @param ResponseHttp $response + * @return bool + */ + private function handleDeveloperMode( + Bootstrap $bootstrap, + \Exception $exception, + ResponseHttp $response + ): bool { + if ($bootstrap->isDeveloperMode()) { + if (Bootstrap::ERR_IS_INSTALLED == $bootstrap->getErrorCode()) { + try { + $this->redirectToSetup($bootstrap, $exception, $response); + return true; + } catch (\Exception $e) { + $exception = $e; + } + } + $response->setHttpResponseCode(500); + $response->setHeader('Content-Type', 'text/plain'); + $response->setBody($this->buildContentFromException($exception)); + $response->sendResponse(); + return true; + } + return false; + } + + /** + * Build content based on an exception + * + * @param \Exception $exception + * @return string + */ + private function buildContentFromException(\Exception $exception): string + { + /** @var \Exception[] $exceptions */ + $exceptions = []; + + do { + $exceptions[] = $exception; + } while ($exception = $exception->getPrevious()); + + $buffer = sprintf("%d exception(s):\n", count($exceptions)); + + foreach ($exceptions as $index => $exception) { + $buffer .= sprintf( + "Exception #%d (%s): %s\n", + $index, + get_class($exception), + $exception->getMessage() + ); + } + + foreach ($exceptions as $index => $exception) { + $buffer .= sprintf( + "\nException #%d (%s): %s\n%s\n", + $index, + get_class($exception), + $exception->getMessage(), + Debug::trace( + $exception->getTrace(), + true, + true, + (bool)getenv('MAGE_DEBUG_SHOW_ARGS') + ) + ); + } + + return $buffer; + } + + /** + * Handler for bootstrap errors + * + * @param Bootstrap $bootstrap + * @param \Exception $exception + * @param ResponseHttp $response + * @return bool + */ + private function handleBootstrapErrors( + Bootstrap $bootstrap, + \Exception &$exception, + ResponseHttp $response + ): bool { + $bootstrapCode = $bootstrap->getErrorCode(); + if (Bootstrap::ERR_MAINTENANCE == $bootstrapCode) { + // phpcs:ignore Magento2.Security.IncludeFile + require $this->_filesystem + ->getDirectoryRead(DirectoryList::PUB) + ->getAbsolutePath('errors/503.php'); + return true; + } + if (Bootstrap::ERR_IS_INSTALLED == $bootstrapCode) { + try { + $this->redirectToSetup($bootstrap, $exception, $response); + return true; + } catch (\Exception $e) { + $exception = $e; + } + } + return false; + } + + /** + * Handler for session errors + * + * @param \Exception $exception + * @param ResponseHttp $response + * @param RequestHttp $request + * @return bool + */ + private function handleSessionException( + \Exception $exception, + ResponseHttp $response, + RequestHttp $request + ): bool { + if ($exception instanceof SessionException) { + $response->setRedirect($request->getDistroBaseUrl()); + $response->sendHeaders(); + return true; + } + return false; + } + + /** + * Handler for application initialization errors + * + * @param \Exception $exception + * @return bool + */ + private function handleInitException(\Exception $exception): bool + { + if ($exception instanceof InitException) { + $this->logger->critical($exception); + // phpcs:ignore Magento2.Security.IncludeFile + require $this->_filesystem + ->getDirectoryRead(DirectoryList::PUB) + ->getAbsolutePath('errors/404.php'); + return true; + } + return false; + } + + /** + * Handle for any other errors + * + * @param Bootstrap $bootstrap + * @param \Exception $exception + * @return bool + */ + private function handleGenericReport(Bootstrap $bootstrap, \Exception $exception): bool + { + $reportData = [ + $exception->getMessage(), + Debug::trace( + $exception->getTrace(), + true, + false, + (bool)getenv('MAGE_DEBUG_SHOW_ARGS') + ) + ]; + $params = $bootstrap->getParams(); + if (isset($params['REQUEST_URI'])) { + $reportData['url'] = $params['REQUEST_URI']; + } + if (isset($params['SCRIPT_NAME'])) { + $reportData['script_name'] = $params['SCRIPT_NAME']; + } + $reportData['report_id'] = $this->encryptor->getHash(implode('', $reportData)); + $this->logger->critical($exception, ['report_id' => $reportData['report_id']]); + // phpcs:ignore Magento2.Security.IncludeFile + require $this->_filesystem + ->getDirectoryRead(DirectoryList::PUB) + ->getAbsolutePath('errors/report.php'); + return true; + } + + /** + * If not installed, try to redirect to installation wizard + * + * @param Bootstrap $bootstrap + * @param \Exception $exception + * @param ResponseHttp $response + * @return void + * @throws \Exception + */ + private function redirectToSetup(Bootstrap $bootstrap, \Exception $exception, ResponseHttp $response) + { + $setupInfo = new SetupInfo($bootstrap->getParams()); + $projectRoot = $this->_filesystem->getDirectoryRead(DirectoryList::ROOT)->getAbsolutePath(); + if ($setupInfo->isAvailable()) { + $response->setRedirect($setupInfo->getUrl()); + $response->sendHeaders(); + } else { + $newMessage = $exception->getMessage() . "\nNOTE: You cannot install Magento using the Setup Wizard " + . "because the Magento setup directory cannot be accessed. \n" + . 'You can install Magento using either the command line or you must restore access ' + . 'to the following directory: ' . $setupInfo->getDir($projectRoot) . "\n"; + // phpcs:ignore Magento2.Exceptions.DirectThrow + throw new \Exception($newMessage, 0, $exception); + } + } +} diff --git a/lib/internal/Magento/Framework/App/ExceptionHandlerInterface.php b/lib/internal/Magento/Framework/App/ExceptionHandlerInterface.php new file mode 100644 index 000000000000..b4bb5d555017 --- /dev/null +++ b/lib/internal/Magento/Framework/App/ExceptionHandlerInterface.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\App; + +use Magento\Framework\App\Response\Http as ResponseHttp; +use Magento\Framework\App\Request\Http as RequestHttp; + +/** + * Interface ExceptionHandler + */ +interface ExceptionHandlerInterface +{ + /** + * Handles exception of HTTP web application + * + * @param Bootstrap $bootstrap + * @param \Exception $exception + * @param ResponseHttp $response + * @param RequestHttp $request + * @return bool + */ + public function handle( + Bootstrap $bootstrap, + \Exception $exception, + ResponseHttp $response, + RequestHttp $request + ): bool; +} diff --git a/lib/internal/Magento/Framework/App/Http.php b/lib/internal/Magento/Framework/App/Http.php index ca3976da1df5..e69a858ee5a5 100644 --- a/lib/internal/Magento/Framework/App/Http.php +++ b/lib/internal/Magento/Framework/App/Http.php @@ -3,18 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Framework\App; -use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\Request\Http as RequestHttp; use Magento\Framework\App\Response\Http as ResponseHttp; use Magento\Framework\App\Response\HttpInterface; use Magento\Framework\Controller\ResultInterface; -use Magento\Framework\Debug; use Magento\Framework\Event; -use Magento\Framework\Filesystem; use Magento\Framework\ObjectManager\ConfigLoaderInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Event\Manager; +use Magento\Framework\Registry; /** * HTTP web application. Called from webroot index.php to serve web requests. @@ -24,12 +24,12 @@ class Http implements \Magento\Framework\AppInterface { /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ protected $_objectManager; /** - * @var \Magento\Framework\Event\Manager + * @var Manager */ protected $_eventManager; @@ -53,47 +53,42 @@ class Http implements \Magento\Framework\AppInterface */ protected $_state; - /** - * @var Filesystem - */ - protected $_filesystem; - /** * @var ResponseHttp */ protected $_response; /** - * @var \Magento\Framework\Registry + * @var Registry */ protected $registry; /** - * @var \Psr\Log\LoggerInterface + * @var ExceptionHandlerInterface */ - private $logger; + private $exceptionHandler; /** - * @param \Magento\Framework\ObjectManagerInterface $objectManager + * @param ObjectManagerInterface $objectManager * @param Event\Manager $eventManager * @param AreaList $areaList * @param RequestHttp $request * @param ResponseHttp $response * @param ConfigLoaderInterface $configLoader * @param State $state - * @param Filesystem $filesystem - * @param \Magento\Framework\Registry $registry + * @param Registry $registry + * @param ExceptionHandlerInterface $exceptionHandler */ public function __construct( - \Magento\Framework\ObjectManagerInterface $objectManager, + ObjectManagerInterface $objectManager, Event\Manager $eventManager, AreaList $areaList, RequestHttp $request, ResponseHttp $response, ConfigLoaderInterface $configLoader, State $state, - Filesystem $filesystem, - \Magento\Framework\Registry $registry + Registry $registry, + ExceptionHandlerInterface $exceptionHandler = null ) { $this->_objectManager = $objectManager; $this->_eventManager = $eventManager; @@ -102,30 +97,15 @@ public function __construct( $this->_response = $response; $this->_configLoader = $configLoader; $this->_state = $state; - $this->_filesystem = $filesystem; $this->registry = $registry; - } - - /** - * Add new dependency - * - * @return \Psr\Log\LoggerInterface - * - * @deprecated 100.1.0 - */ - private function getLogger() - { - if (!$this->logger instanceof \Psr\Log\LoggerInterface) { - $this->logger = \Magento\Framework\App\ObjectManager::getInstance()->get(\Psr\Log\LoggerInterface::class); - } - return $this->logger; + $this->exceptionHandler = $exceptionHandler ?: $this->_objectManager->get(ExceptionHandlerInterface::class); } /** * Run application * - * @throws \InvalidArgumentException * @return ResponseInterface + * @throws LocalizedException|\InvalidArgumentException */ public function launch() { @@ -172,193 +152,8 @@ private function handleHeadRequest() /** * @inheritdoc */ - public function catchException(Bootstrap $bootstrap, \Exception $exception) - { - $result = $this->handleDeveloperMode($bootstrap, $exception) - || $this->handleBootstrapErrors($bootstrap, $exception) - || $this->handleSessionException($exception) - || $this->handleInitException($exception) - || $this->handleGenericReport($bootstrap, $exception); - return $result; - } - - /** - * Error handler for developer mode - * - * @param Bootstrap $bootstrap - * @param \Exception $exception - * @return bool - */ - private function handleDeveloperMode(Bootstrap $bootstrap, \Exception $exception) + public function catchException(Bootstrap $bootstrap, \Exception $exception): bool { - if ($bootstrap->isDeveloperMode()) { - if (Bootstrap::ERR_IS_INSTALLED == $bootstrap->getErrorCode()) { - try { - $this->redirectToSetup($bootstrap, $exception); - return true; - } catch (\Exception $e) { - $exception = $e; - } - } - $this->_response->setHttpResponseCode(500); - $this->_response->setHeader('Content-Type', 'text/plain'); - $this->_response->setBody($this->buildContentFromException($exception)); - $this->_response->sendResponse(); - return true; - } - return false; - } - - /** - * Build content based on an exception - * - * @param \Exception $exception - * @return string - */ - private function buildContentFromException(\Exception $exception) - { - /** @var \Exception[] $exceptions */ - $exceptions = []; - - do { - $exceptions[] = $exception; - } while ($exception = $exception->getPrevious()); - - $buffer = sprintf("%d exception(s):\n", count($exceptions)); - - foreach ($exceptions as $index => $exception) { - $buffer .= sprintf("Exception #%d (%s): %s\n", $index, get_class($exception), $exception->getMessage()); - } - - foreach ($exceptions as $index => $exception) { - $buffer .= sprintf( - "\nException #%d (%s): %s\n%s\n", - $index, - get_class($exception), - $exception->getMessage(), - Debug::trace( - $exception->getTrace(), - true, - true, - (bool)getenv('MAGE_DEBUG_SHOW_ARGS') - ) - ); - } - - return $buffer; - } - - /** - * If not installed, try to redirect to installation wizard - * - * @param Bootstrap $bootstrap - * @param \Exception $exception - * @return void - * @throws \Exception - */ - private function redirectToSetup(Bootstrap $bootstrap, \Exception $exception) - { - $setupInfo = new SetupInfo($bootstrap->getParams()); - $projectRoot = $this->_filesystem->getDirectoryRead(DirectoryList::ROOT)->getAbsolutePath(); - if ($setupInfo->isAvailable()) { - $this->_response->setRedirect($setupInfo->getUrl()); - $this->_response->sendHeaders(); - } else { - $newMessage = $exception->getMessage() . "\nNOTE: You cannot install Magento using the Setup Wizard " - . "because the Magento setup directory cannot be accessed. \n" - . 'You can install Magento using either the command line or you must restore access ' - . 'to the following directory: ' . $setupInfo->getDir($projectRoot) . "\n"; - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \Exception($newMessage, 0, $exception); - } - } - - /** - * Handler for bootstrap errors - * - * @param Bootstrap $bootstrap - * @param \Exception $exception - * @return bool - */ - private function handleBootstrapErrors(Bootstrap $bootstrap, \Exception &$exception) - { - $bootstrapCode = $bootstrap->getErrorCode(); - if (Bootstrap::ERR_MAINTENANCE == $bootstrapCode) { - // phpcs:ignore Magento2.Security.IncludeFile - require $this->_filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/503.php'); - return true; - } - if (Bootstrap::ERR_IS_INSTALLED == $bootstrapCode) { - try { - $this->redirectToSetup($bootstrap, $exception); - return true; - } catch (\Exception $e) { - $exception = $e; - } - } - return false; - } - - /** - * Handler for session errors - * - * @param \Exception $exception - * @return bool - */ - private function handleSessionException(\Exception $exception) - { - if ($exception instanceof \Magento\Framework\Exception\SessionException) { - $this->_response->setRedirect($this->_request->getDistroBaseUrl()); - $this->_response->sendHeaders(); - return true; - } - return false; - } - - /** - * Handler for application initialization errors - * - * @param \Exception $exception - * @return bool - */ - private function handleInitException(\Exception $exception) - { - if ($exception instanceof \Magento\Framework\Exception\State\InitException) { - $this->getLogger()->critical($exception); - // phpcs:ignore Magento2.Security.IncludeFile - require $this->_filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/404.php'); - return true; - } - return false; - } - - /** - * Handle for any other errors - * - * @param Bootstrap $bootstrap - * @param \Exception $exception - * @return bool - */ - private function handleGenericReport(Bootstrap $bootstrap, \Exception $exception) - { - $reportData = [ - $exception->getMessage(), - Debug::trace( - $exception->getTrace(), - true, - true, - (bool)getenv('MAGE_DEBUG_SHOW_ARGS') - ) - ]; - $params = $bootstrap->getParams(); - if (isset($params['REQUEST_URI'])) { - $reportData['url'] = $params['REQUEST_URI']; - } - if (isset($params['SCRIPT_NAME'])) { - $reportData['script_name'] = $params['SCRIPT_NAME']; - } - // phpcs:ignore Magento2.Security.IncludeFile - require $this->_filesystem->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/report.php'); - return true; + return $this->exceptionHandler->handle($bootstrap, $exception, $this->_response, $this->_request); } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ExceptionHandlerTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ExceptionHandlerTest.php new file mode 100644 index 000000000000..7ade77ee40df --- /dev/null +++ b/lib/internal/Magento/Framework/App/Test/Unit/ExceptionHandlerTest.php @@ -0,0 +1,277 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\App\Test\Unit; + +use Magento\Framework\App\ExceptionHandler; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\SetupInfo; +use Magento\Framework\App\Bootstrap; +use Magento\Framework\Debug; +use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Framework\Filesystem; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\App\Response\Http as ResponseHttp; +use Magento\Framework\App\Request\Http as RequestHttp; +use Psr\Log\LoggerInterface; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Exception\SessionException; +use Magento\Framework\Phrase; +use PHPUnit\Framework\Constraint\StringStartsWith; +use Magento\Framework\Exception\State\InitException; + +/** + * Test for \Magento\Framework\App\ExceptionHandler class + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ExceptionHandlerTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ExceptionHandler + */ + private $exceptionHandler; + + /** + * @var EncryptorInterface|MockObject + */ + private $encryptorInterfaceMock; + + /** + * @var FileSystem|MockObject + */ + private $filesystemMock; + + /** + * @var LoggerInterface|MockObject + */ + protected $loggerMock; + + /** + * @var ResponseHttp|MockObject + */ + protected $responseMock; + + /** + * @var RequestHttp|MockObject + */ + protected $requestMock; + + protected function setUp() + { + $this->encryptorInterfaceMock = $this->createMock(EncryptorInterface::class); + $this->filesystemMock = $this->createMock(Filesystem::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->responseMock = $this->createMock(ResponseHttp::class); + $this->requestMock = $this->getMockBuilder(RequestHttp::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->exceptionHandler = new ExceptionHandler( + $this->encryptorInterfaceMock, + $this->filesystemMock, + $this->loggerMock + ); + } + + public function testHandleDeveloperModeNotInstalled() + { + $dir = $this->getMockForAbstractClass(ReadInterface::class); + $dir->expects($this->once()) + ->method('getAbsolutePath') + ->willReturn(__DIR__); + $this->filesystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->with(DirectoryList::ROOT) + ->willReturn($dir); + $this->responseMock->expects($this->once()) + ->method('setRedirect') + ->with('/_files/'); + $this->responseMock->expects($this->once()) + ->method('sendHeaders'); + $bootstrap = $this->getBootstrapNotInstalled(); + $bootstrap->expects($this->once()) + ->method('getParams') + ->willReturn( + [ + 'SCRIPT_NAME' => '/index.php', + 'DOCUMENT_ROOT' => __DIR__, + 'SCRIPT_FILENAME' => __DIR__ . '/index.php', + SetupInfo::PARAM_NOT_INSTALLED_URL_PATH => '_files', + ] + ); + $this->assertTrue( + $this->exceptionHandler->handle( + $bootstrap, + new \Exception('Test Message'), + $this->responseMock, + $this->requestMock + ) + ); + } + + public function testHandleDeveloperMode() + { + $this->filesystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->will($this->throwException(new \Exception('strange error'))); + $this->responseMock->expects($this->once()) + ->method('setHttpResponseCode') + ->with(500); + $this->responseMock->expects($this->once()) + ->method('setHeader') + ->with('Content-Type', 'text/plain'); + $constraint = new StringStartsWith('1 exception(s):'); + $this->responseMock->expects($this->once()) + ->method('setBody') + ->with($constraint); + $this->responseMock->expects($this->once()) + ->method('sendResponse'); + $bootstrap = $this->getBootstrapNotInstalled(); + $bootstrap->expects($this->once()) + ->method('getParams') + ->willReturn( + ['DOCUMENT_ROOT' => 'something', 'SCRIPT_FILENAME' => 'something/else'] + ); + $this->assertTrue( + $this->exceptionHandler->handle( + $bootstrap, + new \Exception('Test'), + $this->responseMock, + $this->requestMock + ) + ); + } + + public function testCatchExceptionSessionException() + { + $this->responseMock->expects($this->once()) + ->method('setRedirect'); + $this->responseMock->expects($this->once()) + ->method('sendHeaders'); + /** @var Bootstrap|MockObject $bootstrap */ + $bootstrap = $this->createMock(Bootstrap::class); + $bootstrap->expects($this->once()) + ->method('isDeveloperMode') + ->willReturn(false); + $this->assertTrue( + $this->exceptionHandler->handle( + $bootstrap, + new SessionException(new Phrase('Test')), + $this->responseMock, + $this->requestMock + ) + ); + } + + public function testHandleInitException() + { + $bootstrap = $this->getBootstrapInstalled(); + $exception = new InitException(new Phrase('Test')); + $dir = $this->getMockForAbstractClass(ReadInterface::class); + $dir->expects($this->once()) + ->method('getAbsolutePath') + ->with('errors/404.php') + ->willReturn(__DIR__ . '/_files/pub/errors/404.php'); + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with($exception); + $this->filesystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->with(DirectoryList::PUB) + ->willReturn($dir); + + $this->assertTrue( + $this->exceptionHandler->handle( + $bootstrap, + $exception, + $this->responseMock, + $this->requestMock + ) + ); + } + + public function testHandleGenericReport() + { + $bootstrap = $this->getBootstrapInstalled(); + $exception = new \Exception('Test'); + $dir = $this->getMockForAbstractClass(ReadInterface::class); + $dir->expects($this->once()) + ->method('getAbsolutePath') + ->with('errors/report.php') + ->willReturn(__DIR__ . '/_files/pub/errors/report.php'); + $bootstrap->expects($this->once()) + ->method('getParams') + ->willReturn(['REQUEST_URI' => 'some-request-uri', 'SCRIPT_NAME' => 'some-script-name']); + $reportData = [ + $exception->getMessage(), + Debug::trace( + $exception->getTrace(), + true, + false, + (bool)getenv('MAGE_DEBUG_SHOW_ARGS') + ), + 'url' => 'some-request-uri', + 'script_name' => 'some-script-name' + ]; + $this->encryptorInterfaceMock->expects($this->once()) + ->method('getHash') + ->with(implode('', $reportData)) + ->willReturn('some-sha256-hash'); + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with($exception, ['report_id' => 'some-sha256-hash']); + $this->filesystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->with(DirectoryList::PUB) + ->willReturn($dir); + + $this->assertTrue( + $this->exceptionHandler->handle( + $bootstrap, + $exception, + $this->responseMock, + $this->requestMock + ) + ); + } + + /** + * Prepares a mock of bootstrap in "not installed" state + * + * @return Bootstrap|MockObject + */ + private function getBootstrapNotInstalled(): Bootstrap + { + $bootstrap = $this->createMock(Bootstrap::class); + $bootstrap->expects($this->once()) + ->method('isDeveloperMode') + ->willReturn(true); + $bootstrap->expects($this->once()) + ->method('getErrorCode') + ->willReturn(Bootstrap::ERR_IS_INSTALLED); + return $bootstrap; + } + + /** + * Prepares a mock of bootstrap in "installed" state + * + * @return Bootstrap|MockObject + */ + private function getBootstrapInstalled(): Bootstrap + { + /** @var Bootstrap|MockObject $bootstrap */ + $bootstrap = $this->createMock(Bootstrap::class); + $bootstrap->expects($this->once()) + ->method('isDeveloperMode') + ->willReturn(false); + $bootstrap->expects($this->once()) + ->method('getErrorCode') + ->willReturn(0); + return $bootstrap; + } +} diff --git a/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php index dbb315e88a52..6b5f11b21b9b 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php @@ -3,13 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Framework\App\Test\Unit; -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\App\SetupInfo; -use Magento\Framework\App\Bootstrap; - /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -63,7 +58,7 @@ class HttpTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $filesystemMock; + protected $exceptionHandlerMock; protected function setUp() { @@ -85,13 +80,15 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) - ->setConstructorArgs([ - 'cookieReader' => $cookieReaderMock, - 'converter' => $converterMock, - 'routeConfig' => $routeConfigMock, - 'pathInfoProcessor' => $pathInfoProcessorMock, - 'objectManager' => $objectManagerMock - ]) + ->setConstructorArgs( + [ + 'cookieReader' => $cookieReaderMock, + 'converter' => $converterMock, + 'routeConfig' => $routeConfigMock, + 'pathInfoProcessor' => $pathInfoProcessorMock, + 'objectManager' => $objectManagerMock + ] + ) ->setMethods(['getFrontName', 'isHead']) ->getMock(); $this->areaListMock = $this->getMockBuilder(\Magento\Framework\App\AreaList::class) @@ -112,7 +109,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['dispatch']) ->getMock(); - $this->filesystemMock = $this->createMock(\Magento\Framework\Filesystem::class); + $this->exceptionHandlerMock = $this->createMock(\Magento\Framework\App\ExceptionHandlerInterface::class); $this->http = $this->objectManager->getObject( \Magento\Framework\App\Http::class, @@ -123,7 +120,7 @@ protected function setUp() 'request' => $this->requestMock, 'response' => $this->responseMock, 'configLoader' => $this->configLoaderMock, - 'filesystem' => $this->filesystemMock, + 'exceptionHandler' => $this->exceptionHandlerMock, ] ); } @@ -247,92 +244,4 @@ public function dataProviderForTestLaunchHeadRequest(): array ] ]; } - - public function testHandleDeveloperModeNotInstalled() - { - $dir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\ReadInterface::class); - $dir->expects($this->once()) - ->method('getAbsolutePath') - ->willReturn(__DIR__); - $this->filesystemMock->expects($this->once()) - ->method('getDirectoryRead') - ->with(DirectoryList::ROOT) - ->willReturn($dir); - $this->responseMock->expects($this->once()) - ->method('setRedirect') - ->with('/_files/'); - $this->responseMock->expects($this->once()) - ->method('sendHeaders'); - $bootstrap = $this->getBootstrapNotInstalled(); - $bootstrap->expects($this->once()) - ->method('getParams') - ->willReturn( - [ - 'SCRIPT_NAME' => '/index.php', - 'DOCUMENT_ROOT' => __DIR__, - 'SCRIPT_FILENAME' => __DIR__ . '/index.php', - SetupInfo::PARAM_NOT_INSTALLED_URL_PATH => '_files', - ] - ); - $this->assertTrue($this->http->catchException($bootstrap, new \Exception('Test Message'))); - } - - public function testHandleDeveloperMode() - { - $this->filesystemMock->expects($this->once()) - ->method('getDirectoryRead') - ->will($this->throwException(new \Exception('strange error'))); - $this->responseMock->expects($this->once()) - ->method('setHttpResponseCode') - ->with(500); - $this->responseMock->expects($this->once()) - ->method('setHeader') - ->with('Content-Type', 'text/plain'); - $constraint = new \PHPUnit\Framework\Constraint\StringStartsWith('1 exception(s):'); - $this->responseMock->expects($this->once()) - ->method('setBody') - ->with($constraint); - $this->responseMock->expects($this->once()) - ->method('sendResponse'); - $bootstrap = $this->getBootstrapNotInstalled(); - $bootstrap->expects($this->once()) - ->method('getParams') - ->willReturn( - ['DOCUMENT_ROOT' => 'something', 'SCRIPT_FILENAME' => 'something/else'] - ); - $this->assertTrue($this->http->catchException($bootstrap, new \Exception('Test'))); - } - - public function testCatchExceptionSessionException() - { - $this->responseMock->expects($this->once()) - ->method('setRedirect'); - $this->responseMock->expects($this->once()) - ->method('sendHeaders'); - $bootstrap = $this->createMock(\Magento\Framework\App\Bootstrap::class); - $bootstrap->expects($this->once()) - ->method('isDeveloperMode') - ->willReturn(false); - $this->assertTrue($this->http->catchException( - $bootstrap, - new \Magento\Framework\Exception\SessionException(new \Magento\Framework\Phrase('Test')) - )); - } - - /** - * Prepares a mock of bootstrap in "not installed" state - * - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getBootstrapNotInstalled() - { - $bootstrap = $this->createMock(\Magento\Framework\App\Bootstrap::class); - $bootstrap->expects($this->once()) - ->method('isDeveloperMode') - ->willReturn(true); - $bootstrap->expects($this->once()) - ->method('getErrorCode') - ->willReturn(Bootstrap::ERR_IS_INSTALLED); - return $bootstrap; - } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/errors/404.php b/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/errors/404.php new file mode 100644 index 000000000000..37121092d0ba --- /dev/null +++ b/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/errors/404.php @@ -0,0 +1,6 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); diff --git a/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/errors/report.php b/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/errors/report.php new file mode 100644 index 000000000000..37121092d0ba --- /dev/null +++ b/lib/internal/Magento/Framework/App/Test/Unit/_files/pub/errors/report.php @@ -0,0 +1,6 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); diff --git a/pub/errors/local.xml.sample b/pub/errors/local.xml.sample index b89dbb3fee8f..bdd980d4f8a2 100644 --- a/pub/errors/local.xml.sample +++ b/pub/errors/local.xml.sample @@ -27,5 +27,22 @@ value "delete" is for cleaning --> <trash>leave</trash> + <!-- + The number of subdirectories that will be created to save the report. + Valid Values: Integers from 0 to 32 + + Example: + If we have the report name as hash sha256('') = 44ffb1087a44e61b018b3cdee72284d017f22e52755c24e5c85cbac1647ae7a7 + + dir_nesting_level=0 -> <magento_root>/var/report/44ffb1087a44e61b018b3cdee72284d017f22e52755c24e5c85cbac1647ae7a7 + dir_nesting_level=1 -> <magento_root>/var/report/44/44ffb1087a44e61b018b3cdee72284d017f22e52755c24e5c85cbac1647ae7a7 + dir_nesting_level=2 -> <magento_root>/var/report/44/ff/44ffb1087a44e61b018b3cdee72284d017f22e52755c24e5c85cbac1647ae7a7 + ... + dir_nesting_level=32 -> <magento_root>/var/report/44/ff/b1/08/7a/44/e6/1b/01/8b/3c/de/e7/22/84/d0/17/f2/2e/52/75/5c/24/e5/c8/5c/ba/c1/64/7a/e7/a7/44ffb1087a44e61b018b3cdee72284d017f22e52755c24e5c85cbac1647ae7a7 + + If you use an environment variable MAGE_LOG_REPORT_DIR_NESTING_LEVEL, this property will be ignored. + Environment variable has a higher priority. + --> + <dir_nesting_level>0</dir_nesting_level> </report> </config> diff --git a/pub/errors/processor.php b/pub/errors/processor.php index ab21f791bc02..63d054319fb0 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -8,11 +8,15 @@ namespace Magento\Framework\Error; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Escaper; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Response\Http; /** * Error processor * * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * phpcs:ignoreFile */ class Processor @@ -21,6 +25,7 @@ class Processor const MAGE_ERRORS_DESIGN_XML = 'design.xml'; const DEFAULT_SKIN = 'default'; const ERROR_DIR = 'pub/errors'; + const NUMBER_SYMBOLS_IN_SUBDIR_NAME = 2; /** * Page title @@ -67,7 +72,7 @@ class Processor /** * Report ID * - * @var int + * @var string */ public $reportId; @@ -128,7 +133,7 @@ class Processor /** * Http response * - * @var \Magento\Framework\App\Response\Http + * @var Http */ protected $_response; @@ -140,15 +145,25 @@ class Processor private $serializer; /** - * @param \Magento\Framework\App\Response\Http $response + * @var Escaper + */ + private $escaper; + + /** + * @param Http $response * @param Json $serializer + * @param Escaper $escaper */ - public function __construct(\Magento\Framework\App\Response\Http $response, Json $serializer = null) - { + public function __construct( + Http $response, + Json $serializer = null, + Escaper $escaper = null + ) { $this->_response = $response; $this->_errorDir = __DIR__ . '/'; $this->_reportDir = dirname(dirname($this->_errorDir)) . '/var/report/'; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Json::class); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class); if (!empty($_SERVER['SCRIPT_NAME'])) { if (in_array(basename($_SERVER['SCRIPT_NAME'], '.php'), ['404', '503', 'report'])) { @@ -158,11 +173,6 @@ public function __construct(\Magento\Framework\App\Response\Http $response, Json } } - $reportId = (isset($_GET['id'])) ? (int)$_GET['id'] : null; - if ($reportId) { - $this->loadReport($reportId); - } - $this->_indexDir = $this->_getIndexDir(); $this->_root = is_dir($this->_indexDir . 'app'); @@ -170,6 +180,9 @@ public function __construct(\Magento\Framework\App\Response\Http $response, Json if (isset($_GET['skin'])) { $this->_setSkin($_GET['skin']); } + if (isset($_GET['id'])) { + $this->loadReport($_GET['id']); + } } /** @@ -371,6 +384,9 @@ protected function _prepareConfig() if ((string)$local->report->trash) { $config->trash = $local->report->trash; } + if ($local->report->dir_nesting_level) { + $config->dir_nesting_level = (int)$local->report->dir_nesting_level; + } if ((string)$local->skin) { $this->_setSkin((string)$local->skin, $config); } @@ -467,7 +483,7 @@ protected function _setReportData($reportData) $this->reportData['url'] = $this->getHostUrl() . $reportData['url']; } - if ($this->reportData['script_name']) { + if (isset($this->reportData['script_name'])) { $this->_scriptName = $this->reportData['script_name']; } } @@ -478,17 +494,18 @@ protected function _setReportData($reportData) * @param array $reportData * @return string */ - public function saveReport($reportData) + public function saveReport(array $reportData): string { - $this->reportData = $reportData; - $this->reportId = abs((int)(microtime(true) * random_int(100, 1000))); - $this->_reportFile = $this->_reportDir . '/' . $this->reportId; - $this->_setReportData($reportData); - - if (!file_exists($this->_reportDir)) { - @mkdir($this->_reportDir, 0777, true); + $this->reportId = $reportData['report_id']; + $this->_reportFile = $this->getReportPath( + $this->getReportDirNestingLevel($this->reportId), + $this->reportId + ); + $reportDirName = dirname($this->_reportFile); + if (!file_exists($reportDirName)) { + @mkdir($reportDirName, 0777, true); } - + $this->_setReportData($reportData); @file_put_contents($this->_reportFile, $this->serializer->serialize($reportData)); if (isset($reportData['skin']) && self::DEFAULT_SKIN != $reportData['skin']) { @@ -502,19 +519,117 @@ public function saveReport($reportData) /** * Get report * - * @param int $reportId + * @param string $reportId * @return void */ public function loadReport($reportId) { - $this->reportId = $reportId; - $this->_reportFile = $this->_reportDir . '/' . $reportId; + try { + if (!$this->isReportIdValid($reportId)) { + throw new \RuntimeException("Report Id is invalid"); + } + $reportFile = $this->findReportFile($reportId); + if (!is_readable($reportFile)) { + throw new \RuntimeException("Report file cannot be read"); + } + $this->reportId = $reportId; + $this->_reportFile = $reportFile; + $this->_setReportData($this->serializer->unserialize(file_get_contents($this->_reportFile))); + } catch (\RuntimeException $e) { + $this->redirectToBaseUrl(); + } + } - if (!file_exists($this->_reportFile) || !is_readable($this->_reportFile)) { - header("Location: " . $this->getBaseUrl()); - die(); + /** + * Searches for the report file and returns the path to it + * + * @param string $reportId + * @return string + * @throws \RuntimeException + */ + private function findReportFile(string $reportId): string + { + $reportFile = $this->getReportPath( + $this->getReportDirNestingLevel($reportId), + $reportId + ); + if (file_exists($reportFile)) { + return $reportFile; + } + $maxReportDirNestingLevel = $this->getMaxReportDirNestingLevel($reportId); + for ($i = 0; $i <= $maxReportDirNestingLevel; $i++) { + $reportFile = $this->getReportPath($i, $reportId); + if (file_exists($reportFile)) { + return $reportFile; + } } - $this->_setReportData($this->serializer->unserialize(file_get_contents($this->_reportFile))); + throw new \RuntimeException("Report file not found"); + } + + /** + * Redirect to a base url + * @return void + */ + private function redirectToBaseUrl() + { + header("Location: " . $this->getBaseUrl()); + die(); + } + + /** + * Checks report id + * + * @param string $reportId + * @return bool + */ + private function isReportIdValid(string $reportId): bool + { + return (bool)preg_match('/[a-fA-F0-9]{64}/', $reportId); + } + + /** + * Get path to reports + * + * @param integer $reportDirNestingLevel + * @param string $reportId + * @return string + */ + private function getReportPath(int $reportDirNestingLevel, string $reportId): string + { + $reportDirPath = $this->_reportDir; + for ($i = 0, $j = 0; $j < $reportDirNestingLevel; $i += 2, $j++) { + $reportDirPath .= $reportId[$i] . $reportId[$i + 1] . '/'; + } + return $reportDirPath . $reportId; + } + + /** + * Returns nesting Level for the report files + * + * @var $reportId + * @return int + */ + private function getReportDirNestingLevel(string $reportId): int + { + $envName = 'MAGE_LOG_REPORT_DIR_NESTING_LEVEL'; + $value = $_ENV[$envName] ?? getenv($envName); + if(false === $value && property_exists($this->_config, 'dir_nesting_level')) { + $value = $this->_config->dir_nesting_level; + } + $value = (int)$value; + $maxValue= $this->getMaxReportDirNestingLevel($reportId); + return 0 < $value && $maxValue >= $value ? $value : 0; + } + + /** + * Returns maximum nesting level directories of report files + * + * @param string $reportId + * @return integer + */ + private function getMaxReportDirNestingLevel(string $reportId): int + { + return (integer)floor(mb_strlen($reportId) / self::NUMBER_SYMBOLS_IN_SUBDIR_NAME); } /** @@ -528,11 +643,16 @@ public function sendReport() { $this->pageTitle = 'Error Submission Form'; - $this->postData['firstName'] = (isset($_POST['firstname'])) ? trim(htmlspecialchars($_POST['firstname'])) : ''; - $this->postData['lastName'] = (isset($_POST['lastname'])) ? trim(htmlspecialchars($_POST['lastname'])) : ''; - $this->postData['email'] = (isset($_POST['email'])) ? trim(htmlspecialchars($_POST['email'])) : ''; - $this->postData['telephone'] = (isset($_POST['telephone'])) ? trim(htmlspecialchars($_POST['telephone'])) : ''; - $this->postData['comment'] = (isset($_POST['comment'])) ? trim(htmlspecialchars($_POST['comment'])) : ''; + $this->postData['firstName'] = (isset($_POST['firstname'])) + ? trim($this->escaper->escapeHtml($_POST['firstname'])) : ''; + $this->postData['lastName'] = (isset($_POST['lastname'])) + ? trim($this->escaper->escapeHtml($_POST['lastname'])) : ''; + $this->postData['email'] = (isset($_POST['email'])) + ? trim($this->escaper->escapeHtml($_POST['email'])) : ''; + $this->postData['telephone'] = (isset($_POST['telephone'])) + ? trim($this->escaper->escapeHtml($_POST['telephone'])) : ''; + $this->postData['comment'] = (isset($_POST['comment'])) + ? trim($this->escaper->escapeHtml($_POST['comment'])) : ''; if (isset($_POST['submit'])) { if ($this->_validate()) { From 8911e4290d8087bb11741790810bcddef333c4ff Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Tue, 10 Sep 2019 12:00:26 -0500 Subject: [PATCH 25/55] MC-19866: Redundant attributes inside CMS widget body - Fix wrong parameters in widget declaration --- lib/web/mage/adminhtml/wysiwyg/widget.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/widget.js b/lib/web/mage/adminhtml/wysiwyg/widget.js index 5c1a77b6382a..f39fc2203410 100644 --- a/lib/web/mage/adminhtml/wysiwyg/widget.js +++ b/lib/web/mage/adminhtml/wysiwyg/widget.js @@ -223,7 +223,9 @@ define([ * @param {*} containerId */ enableOptionsContainer: function (containerId) { - $$('#' + containerId + ' .widget-option').each(function (e) { + var container = $(containerId); + + container.select('.widget-option').each(function (e) { e.removeClassName('skip-submit'); if (e.hasClassName('obligatory')) { @@ -231,18 +233,19 @@ define([ e.addClassName('required-entry'); } }); - $(containerId).removeClassName('no-display'); + container.removeClassName('no-display'); }, /** * @param {*} containerId */ disableOptionsContainer: function (containerId) { + var container = $(containerId); - if ($(containerId).hasClassName('no-display')) { + if (container.hasClassName('no-display')) { return; } - $$('#' + containerId + ' .widget-option').each(function (e) { + container.select('.widget-option').each(function (e) { // Avoid submitting fields of unactive container if (!e.hasClassName('skip-submit')) { e.addClassName('skip-submit'); @@ -253,7 +256,7 @@ define([ e.addClassName('obligatory'); } }); - $(containerId).addClassName('no-display'); + container.addClassName('no-display'); }, /** @@ -439,7 +442,7 @@ define([ i = 0; Form.getElements($(this.formEl)).each(function (e) { - if (!e.hasClassName('skip-submit')) { + if (jQuery(e).closest('.skip-submit, .no-display').length === 0) { formElements[i] = e; i++; } From dc2c7c07b80be22d116284b5f44f8a0475240a9d Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 11 Sep 2019 11:32:08 +0300 Subject: [PATCH 26/55] MC-6443: Product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key --- .../ActionGroup/AdminCategoryActionGroup.xml | 18 +++ .../Section/AdminCategoryContentSection.xml | 1 + ...nAssignedToCategoryWithoutCustomURLKey.xml | 111 ++++++++++++++++++ .../AdminDeleteStoreViewActionGroup.xml | 1 + 4 files changed, 131 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 12bf5179a07d..f2005219ffbc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -396,4 +396,22 @@ <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> </actionGroup> + + <actionGroup name="AdminCategoryAssignProduct"> + <annotations> + <description>Assign products to category - using "Products in Category" tab.</description> + </annotations> + <arguments> + <argument name="productSku" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" dependentSelector="{{CatalogProductsSection.resetFilter}}" visible="false" stepKey="clickOnProductInCategory"/> + <waitForPageLoad stepKey="waitForProductsInCategoryOpened"/> + <click selector="{{CatalogProductsSection.resetFilter}}" stepKey="clickOnResetFilter"/> + <waitForPageLoad stepKey="waitForProductsToLoad"/> + <fillField selector="{{AdminCategoryContentSection.productTableColumnSku}}" userInput="{{productSku}}" stepKey="selectProduct"/> + <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectProductFromTableRow"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml index e3d224904671..1cb095974d0f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryContentSection.xml @@ -24,5 +24,6 @@ <element name="productTableColumnName" type="input" selector="#catalog_category_products_filter_name"/> <element name="productTableRow" type="button" selector="#catalog_category_products_table tbody tr"/> <element name="productSearch" type="button" selector="//button[@data-action='grid-filter-apply']" timeout="30"/> + <element name="productTableColumnSku" type="input" selector="#catalog_category_products_filter_sku"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml new file mode 100644 index 000000000000..d7a01ac0c861 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml @@ -0,0 +1,111 @@ +<?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="AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey"> + <annotations> + <stories value="Product"/> + <title value="Product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key"/> + <description value="The test verifies that product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key."/> + <severity value="MAJOR"/> + <testCaseId value="MC-6443"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <!-- Create Simple Products --> + <createData entity="SimpleProduct2" stepKey="createSimpleProductFirst"/> + <createData entity="SimpleProduct2" stepKey="createSimpleProductSecond"/> + + <!--Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> + <argument name="customStore" value="storeViewData"/> + </actionGroup> + + <!--Run full reindex and clear caches --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProductFirst" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createSimpleProductSecond" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> + <argument name="customStore" value="storeViewData"/> + </actionGroup> + + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Open product --> + <amOnPage url="{{AdminProductEditPage.url($createSimpleProductSecond.id$)}}" stepKey="openProductSecondEditPage"/> + <!-- switch store view --> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchToStoreView"> + <argument name="storeView" value="storeViewData.name"/> + </actionGroup> + + <!-- set url key --> + <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openSeoSection"/> + <uncheckOption selector="{{AdminProductSEOSection.useDefaultUrl}}" stepKey="uncheckUseDefaultUrlKey"/> + <fillField userInput="U2" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <actionGroup ref="goToAdminCategoryPageById" stepKey="openCategory"> + <argument name="id" value="$createCategory.id$"/> + </actionGroup> + + <actionGroup ref="AdminCategoryAssignProduct" stepKey="assignSimpleProductFirst"> + <argument name="productSku" value="$createSimpleProductFirst.sku$"/> + </actionGroup> + <actionGroup ref="AdminCategoryAssignProduct" stepKey="assignSimpleProductSecond"> + <argument name="productSku" value="$createSimpleProductSecond.sku$"/> + </actionGroup> + + <actionGroup ref="saveCategoryForm" stepKey="saveCategory"/> + + <executeJS function="var s = '$createCategory.name$'; var res=s.toLowerCase(); return res;" stepKey="categoryNameLower" /> + <executeJS function="var s = '$createSimpleProductFirst.name$'; var res=s.toLowerCase(); return res;" stepKey="simpleProductFirstNameLower" /> + <executeJS function="var s = '$createSimpleProductSecond.name$';var res=s.toLowerCase(); return res;" stepKey="simpleProductSecondNameLower" /> + + <!-- Make assertions on frontend --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="onCategoryPage"/> + <seeInCurrentUrl url="{$categoryNameLower}.html" stepKey="checkCategryUrlKey"/> + + <!-- Open first product --> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductFirst.name$)}}" stepKey="openFirstProduct"/> + <seeInCurrentUrl url="{$simpleProductFirstNameLower}.html" stepKey="checkFirstSimpleProductUrlKey"/> + + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryView"/> + <!-- Open second product --> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductSecond.name$)}}" stepKey="openSecondProduct"/> + <seeInCurrentUrl url="{$simpleProductSecondNameLower}.html" stepKey="checkSecondSimpleProductUrlKey"/> + + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeView" value="storeViewData"/> + </actionGroup> + + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="openCategoryPage"/> + <seeInCurrentUrl url="{$categoryNameLower}.html" stepKey="seeCategoryUrlKey"/> + + <!-- Open product first --> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProductFirst.name$$)}}" stepKey="openFirstSimpleProduct"/> + <seeInCurrentUrl url="{$simpleProductFirstNameLower}.html" stepKey="assertFirstSimpleProductUrlKey"/> + + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="openCategoryView"/> + <!-- Open product2 --> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductSecond.name$)}}" stepKey="openSecondSimpleProduct"/> + <seeInCurrentUrl url="u2.html" stepKey="assertSecondSimpleProductUrlKey"/> + </test> +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml index 94856bb083da..63dc4b0ded4f 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml @@ -18,6 +18,7 @@ <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="navigateToStoresIndex"/> <waitForPageLoad stepKey="waitStoreIndexPageLoad"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> <fillField selector="{{AdminStoresGridSection.storeFilterTextField}}" userInput="{{customStore.name}}" stepKey="fillStoreViewFilterField"/> <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearch"/> <click selector="{{AdminStoresGridSection.storeNameInFirstRow}}" stepKey="clickStoreViewInGrid"/> From eafb431b230166b3b7091ba10c47061c34ee8ca7 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 11 Sep 2019 13:37:03 +0300 Subject: [PATCH 27/55] MC-12353: Frontend area session must not affect admin area --- ...dAreaSessionMustNotAffectAdminAreaTest.xml | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml new file mode 100644 index 000000000000..375211e5f2f5 --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/AdminFrontendAreaSessionMustNotAffectAdminAreaTest.xml @@ -0,0 +1,89 @@ +<?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="AdminFrontendAreaSessionMustNotAffectAdminAreaTest"> + <annotations> + <stories value="Backend"/> + <features value="Session cookies"/> + <title value="Frontend area session must not affect admin area"/> + <description value="Frontend area session must not affect admin area"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-12353"/> + <group value="backend"/> + <group value="pagecache"/> + <group value="cookie"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="_defaultCategory" stepKey="createCategoryA"/> + <createData entity="SubCategoryWithParent" stepKey="createCategoryB"> + <requiredEntity createDataKey="createCategoryA"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createCategoryC"> + <requiredEntity createDataKey="createCategoryB"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct1"> + <requiredEntity createDataKey="createCategoryC"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct2"> + <requiredEntity createDataKey="createCategoryC"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct3"> + <requiredEntity createDataKey="createCategoryA"/> + </createData> + + <magentoCLI command="cache:clean" arguments="full_page" stepKey="clearCache"/> + <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <resetCookie userInput="PHPSESSID" stepKey="resetSessionCookie"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="createProduct3" stepKey="deleteProduct3"/> + + <deleteData createDataKey="createCategoryC" stepKey="deleteCategoryC"/> + <deleteData createDataKey="createCategoryB" stepKey="deleteCategoryB"/> + <deleteData createDataKey="createCategoryA" stepKey="deleteCategoryA"/> + + <actionGroup ref="logout" stepKey="logoutAdmin"/> + </after> + + <!-- 1. Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- 2. Navigate Go to "Catalog"->"Products" --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="onCatalogProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <!-- 3. Open separate tab with Storefront --> + <openNewTab stepKey="openNewTab"/> + + <!-- 4. Navigate to Men -> "Tops" -> "Jackets" --> + <amOnPage + url="{{StorefrontCategoryPage.url($$createCategoryA.custom_attributes[url_key]$$/$$createCategoryB.custom_attributes[url_key]$$/$$createCategoryC.custom_attributes[url_key]$$)}}" + stepKey="openCategoryPage"/> + <waitForPageLoad time="60" stepKey="waitForCategoryPage"/> + + <!-- 5. Open admin tab with page with products. Reload this page twice. --> + <switchToPreviousTab stepKey="switchToPreviousTab"/> + <reloadPage stepKey="reloadAdminCatalogPageFirst"/> + <waitForPageLoad stepKey="waitForReloadFirst"/> + <reloadPage stepKey="reloadAdminCatalogPageSecond"/> + <waitForPageLoad stepKey="waitForReloadSecond"/> + + <seeInTitle userInput="Products / Inventory / Catalog / Magento Admin" stepKey="seeAdminProductsPageTitle"/> + <see userInput="Products" selector="{{AdminHeaderSection.pageTitle}}" stepKey="seeAdminProductsPageHeader"/> + + <switchToNextTab stepKey="switchToFrontendTab"/> + <closeTab stepKey="closeFrontendTab"/> + </test> +</tests> From 0d43912118850097deb864eb0786ffda2d1556fc Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 11 Sep 2019 14:07:22 -0500 Subject: [PATCH 28/55] MC-20108: dd/mm mm/yy Sales order grid Date Filters not working in en_GB locale --- .../Magento/Ui/Component/Form/Element/DataType/Date.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php index 470767af6d31..31d2fe786cfd 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php @@ -111,14 +111,7 @@ public function getComponentName() public function convertDate($date, $hour = 0, $minute = 0, $second = 0, $setUtcTimeZone = true) { try { - $dateObj = $this->localeDate->date( - new \DateTime( - $date, - new \DateTimeZone($this->localeDate->getConfigTimezone()) - ), - $this->getLocale(), - true - ); + $dateObj = $this->localeDate->date($date, $this->getLocale(), true); $dateObj->setTime($hour, $minute, $second); //convert store date to default date in UTC timezone without DST if ($setUtcTimeZone) { From ddeb980c2c9aec565ad1b06db482a6e8b518b2eb Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Wed, 11 Sep 2019 13:43:03 -0500 Subject: [PATCH 29/55] MC-19906: ElasticSearch doesn't match results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix the matching query text is used for search instead of the original one. Because of inaccurate string comparison of utf8_general_ci, the search_query result text may be different from the original text (e.g organos, Organos, Órganos) --- .../Magento/Search/Model/QueryFactory.php | 16 ++-- .../Test/Unit/Model/QueryFactoryTest.php | 81 ++++++++++++------- 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Search/Model/QueryFactory.php b/app/code/Magento/Search/Model/QueryFactory.php index 212245174240..4186b5c3055f 100644 --- a/app/code/Magento/Search/Model/QueryFactory.php +++ b/app/code/Magento/Search/Model/QueryFactory.php @@ -5,13 +5,15 @@ */ namespace Magento\Search\Model; -use Magento\Search\Helper\Data; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Helper\Context; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Stdlib\StringUtils as StdlibString; +use Magento\Search\Helper\Data; /** + * Search Query Factory + * * @api * @since 100.0.2 */ @@ -72,7 +74,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function get() { @@ -82,9 +84,7 @@ public function get() $rawQueryText = $this->getRawQueryText(); $preparedQueryText = $this->getPreparedQueryText($rawQueryText, $maxQueryLength); $query = $this->create()->loadByQueryText($preparedQueryText); - if (!$query->getId()) { - $query->setQueryText($preparedQueryText); - } + $query->setQueryText($preparedQueryText); $query->setIsQueryTextExceeded($this->isQueryTooLong($rawQueryText, $maxQueryLength)); $query->setIsQueryTextShort($this->isQueryTooShort($rawQueryText, $minQueryLength)); $this->query = $query; @@ -117,6 +117,8 @@ private function getRawQueryText() } /** + * Prepare query text + * * @param string $queryText * @param int|string $maxQueryLength * @return string @@ -130,6 +132,8 @@ private function getPreparedQueryText($queryText, $maxQueryLength) } /** + * Check if the provided text exceeds the provided length + * * @param string $queryText * @param int|string $maxQueryLength * @return bool @@ -140,6 +144,8 @@ private function isQueryTooLong($queryText, $maxQueryLength) } /** + * Check if the provided text is shorter than the provided length + * * @param string $queryText * @param int|string $minQueryLength * @return bool diff --git a/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php b/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php index 3df457b0d449..f66c1c7dd9e3 100644 --- a/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php +++ b/app/code/Magento/Search/Test/Unit/Model/QueryFactoryTest.php @@ -5,14 +5,14 @@ */ namespace Magento\Search\Test\Unit\Model; -use Magento\Search\Helper\Data; use Magento\Framework\App\Helper\Context; use Magento\Framework\App\RequestInterface; use Magento\Framework\ObjectManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Search\Model\QueryFactory; use Magento\Framework\Stdlib\StringUtils; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Search\Helper\Data; use Magento\Search\Model\Query; +use Magento\Search\Model\QueryFactory; /** * Class QueryFactoryTest tests Magento\Search\Model\QueryFactory @@ -67,7 +67,7 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->query = $this->getMockBuilder(Query::class) - ->setMethods(['setIsQueryTextExceeded', 'setIsQueryTextShort', 'loadByQueryText', 'getId', 'setQueryText']) + ->setMethods(['setIsQueryTextExceeded', 'setIsQueryTextShort', 'loadByQueryText', 'getId']) ->disableOriginalConstructor() ->getMock(); @@ -124,7 +124,6 @@ public function testGetNewQuery() $isQueryTextExceeded = false; $isQueryTextShort = false; - $this->mockSetQueryTextNeverExecute($cleanedRawText); $this->mockString($cleanedRawText); $this->mockQueryLengths($maxQueryLength, $minQueryLength); $this->mockGetRawQueryText($rawQueryText); @@ -135,6 +134,7 @@ public function testGetNewQuery() $result = $this->model->get(); $this->assertSame($this->query, $result); + $this->assertSearchQuery($cleanedRawText); } /** @@ -150,7 +150,6 @@ public function testGetQueryTwice() $isQueryTextExceeded = false; $isQueryTextShort = false; - $this->mockSetQueryTextNeverExecute($cleanedRawText); $this->mockString($cleanedRawText); $this->mockQueryLengths($maxQueryLength, $minQueryLength); $this->mockGetRawQueryText($rawQueryText); @@ -163,6 +162,7 @@ public function testGetQueryTwice() $result = $this->model->get(); $this->assertSame($this->query, $result, 'After second execution queries are not same'); + $this->assertSearchQuery($cleanedRawText); } /** @@ -184,7 +184,6 @@ public function testGetTooLongQuery() ->withConsecutive([$cleanedRawText, 0, $maxQueryLength]) ->willReturn($subRawText); - $this->mockSetQueryTextNeverExecute($cleanedRawText); $this->mockString($cleanedRawText); $this->mockQueryLengths($maxQueryLength, $minQueryLength); $this->mockGetRawQueryText($rawQueryText); @@ -194,6 +193,7 @@ public function testGetTooLongQuery() $result = $this->model->get(); $this->assertSame($this->query, $result); + $this->assertSearchQuery($subRawText); } /** @@ -209,7 +209,6 @@ public function testGetTooShortQuery() $isQueryTextExceeded = false; $isQueryTextShort = true; - $this->mockSetQueryTextNeverExecute($cleanedRawText); $this->mockString($cleanedRawText); $this->mockQueryLengths($maxQueryLength, $minQueryLength); $this->mockGetRawQueryText($rawQueryText); @@ -219,6 +218,7 @@ public function testGetTooShortQuery() $result = $this->model->get(); $this->assertSame($this->query, $result); + $this->assertSearchQuery($cleanedRawText); } /** @@ -234,7 +234,6 @@ public function testGetQueryWithoutId() $isQueryTextExceeded = false; $isQueryTextShort = false; - $this->mockSetQueryTextOnceExecute($cleanedRawText); $this->mockString($cleanedRawText); $this->mockQueryLengths($maxQueryLength, $minQueryLength); $this->mockGetRawQueryText($rawQueryText); @@ -244,6 +243,35 @@ public function testGetQueryWithoutId() $result = $this->model->get(); $this->assertSame($this->query, $result); + $this->assertSearchQuery($cleanedRawText); + } + + /** + * Test for inaccurate match of search query in query_text table + * + * Because of inaccurate string comparison of utf8_general_ci, + * the search_query result text may be different from the original text (e.g organos, Organos, Órganos) + */ + public function testInaccurateQueryTextMatch() + { + $queryId = 1; + $maxQueryLength = 100; + $minQueryLength = 3; + $rawQueryText = 'Órganos'; + $cleanedRawText = 'Órganos'; + $isQueryTextExceeded = false; + $isQueryTextShort = false; + + $this->mockString($cleanedRawText); + $this->mockQueryLengths($maxQueryLength, $minQueryLength); + $this->mockGetRawQueryText($rawQueryText); + $this->mockSimpleQuery($cleanedRawText, $queryId, $isQueryTextExceeded, $isQueryTextShort, 'Organos'); + + $this->mockCreateQuery(); + + $result = $this->model->get(); + $this->assertSame($this->query, $result); + $this->assertSearchQuery($cleanedRawText); } /** @@ -305,15 +333,25 @@ private function mockCreateQuery() * @param int $queryId * @param bool $isQueryTextExceeded * @param bool $isQueryTextShort + * @param string $matchedQueryText * @return void */ - private function mockSimpleQuery($cleanedRawText, $queryId, $isQueryTextExceeded, $isQueryTextShort) - { + private function mockSimpleQuery( + string $cleanedRawText, + ?int $queryId, + bool $isQueryTextExceeded, + bool $isQueryTextShort, + string $matchedQueryText = null + ) { + if (null === $matchedQueryText) { + $matchedQueryText = $cleanedRawText; + } $this->query->expects($this->once()) ->method('loadByQueryText') ->withConsecutive([$cleanedRawText]) ->willReturnSelf(); - $this->query->expects($this->once()) + $this->query->setData(['query_text' => $matchedQueryText]); + $this->query->expects($this->any()) ->method('getId') ->willReturn($queryId); $this->query->expects($this->once()) @@ -328,23 +366,8 @@ private function mockSimpleQuery($cleanedRawText, $queryId, $isQueryTextExceeded * @param string $cleanedRawText * @return void */ - private function mockSetQueryTextNeverExecute($cleanedRawText) + private function assertSearchQuery($cleanedRawText) { - $this->query->expects($this->never()) - ->method('setQueryText') - ->withConsecutive([$cleanedRawText]) - ->willReturnSelf(); - } - - /** - * @param string $cleanedRawText - * @return void - */ - private function mockSetQueryTextOnceExecute($cleanedRawText) - { - $this->query->expects($this->once()) - ->method('setQueryText') - ->withConsecutive([$cleanedRawText]) - ->willReturnSelf(); + $this->assertEquals($cleanedRawText, $this->query->getQueryText()); } } From db8b074a4a8499915969640cdd0e3cbdc6ad426b Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 12 Sep 2019 09:51:20 +0300 Subject: [PATCH 30/55] MC-6443: Product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key --- .../Mftf/ActionGroup/AdminCategoryActionGroup.xml | 8 ++++---- ...edWhenAssignedToCategoryWithoutCustomURLKey.xml | 14 ++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index f2005219ffbc..96575ee475a2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -399,19 +399,19 @@ <actionGroup name="AdminCategoryAssignProduct"> <annotations> - <description>Assign products to category - using "Products in Category" tab.</description> + <description>Requires navigation to category creation/edit page. Assign products to category - using "Products in Category" tab.</description> </annotations> <arguments> <argument name="productSku" type="string"/> </arguments> <conditionalClick selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" dependentSelector="{{CatalogProductsSection.resetFilter}}" visible="false" stepKey="clickOnProductInCategory"/> - <waitForPageLoad stepKey="waitForProductsInCategoryOpened"/> + <waitForPageLoad time="30" stepKey="waitForProductsInCategoryOpened"/> <click selector="{{CatalogProductsSection.resetFilter}}" stepKey="clickOnResetFilter"/> - <waitForPageLoad stepKey="waitForProductsToLoad"/> + <waitForPageLoad time="30" stepKey="waitForProductsToLoad"/> <fillField selector="{{AdminCategoryContentSection.productTableColumnSku}}" userInput="{{productSku}}" stepKey="selectProduct"/> <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForSearch"/> + <waitForPageLoad time="30" stepKey="waitForSearch"/> <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectProductFromTableRow"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml index d7a01ac0c861..bae81513de63 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml @@ -11,10 +11,12 @@ <test name="AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey"> <annotations> <stories value="Product"/> + <features value="Catalog"/> <title value="Product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key"/> <description value="The test verifies that product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key."/> <severity value="MAJOR"/> <testCaseId value="MC-6443"/> + <useCaseId value="MAGETWO-90331"/> <group value="catalog"/> </annotations> <before> @@ -74,9 +76,9 @@ <actionGroup ref="saveCategoryForm" stepKey="saveCategory"/> - <executeJS function="var s = '$createCategory.name$'; var res=s.toLowerCase(); return res;" stepKey="categoryNameLower" /> - <executeJS function="var s = '$createSimpleProductFirst.name$'; var res=s.toLowerCase(); return res;" stepKey="simpleProductFirstNameLower" /> - <executeJS function="var s = '$createSimpleProductSecond.name$';var res=s.toLowerCase(); return res;" stepKey="simpleProductSecondNameLower" /> + <executeJS function="return '$createCategory.name$'.toLowerCase();" stepKey="categoryNameLower" /> + <executeJS function="return '$createSimpleProductFirst.name$'.toLowerCase();" stepKey="simpleProductFirstNameLower" /> + <executeJS function="return '$createSimpleProductSecond.name$'.toLowerCase();" stepKey="simpleProductSecondNameLower" /> <!-- Make assertions on frontend --> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> @@ -85,11 +87,13 @@ <!-- Open first product --> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductFirst.name$)}}" stepKey="openFirstProduct"/> + <waitForPageLoad time="30" stepKey="waitForFirstProduct"/> <seeInCurrentUrl url="{$simpleProductFirstNameLower}.html" stepKey="checkFirstSimpleProductUrlKey"/> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryView"/> + <amOnPage url="{{StorefrontCategoryPage.url($createCategory.custom_attributes[url_key]$)}}" stepKey="onCategoryView"/> <!-- Open second product --> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductSecond.name$)}}" stepKey="openSecondProduct"/> + <waitForPageLoad time="30" stepKey="waitForSecondProduct"/> <seeInCurrentUrl url="{$simpleProductSecondNameLower}.html" stepKey="checkSecondSimpleProductUrlKey"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> @@ -101,11 +105,13 @@ <!-- Open product first --> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProductFirst.name$$)}}" stepKey="openFirstSimpleProduct"/> + <waitForPageLoad time="30" stepKey="waitForFirstSimpleProduct"/> <seeInCurrentUrl url="{$simpleProductFirstNameLower}.html" stepKey="assertFirstSimpleProductUrlKey"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($createCategory.name$)}}" stepKey="openCategoryView"/> <!-- Open product2 --> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($createSimpleProductSecond.name$)}}" stepKey="openSecondSimpleProduct"/> + <waitForPageLoad time="30" stepKey="waitForSecondSimpleProduct"/> <seeInCurrentUrl url="u2.html" stepKey="assertSecondSimpleProductUrlKey"/> </test> </tests> From 6630bad98faff1777c9608a697cdaf016db316ad Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 12 Sep 2019 12:07:49 +0300 Subject: [PATCH 31/55] MC-3377: Admin should be able to associate grouped product to websites --- ...nAssociateGroupedProductToWebsitesTest.xml | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml new file mode 100644 index 000000000000..382766625247 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml @@ -0,0 +1,116 @@ +<?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="AdminAssociateGroupedProductToWebsitesTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Create/Edit grouped product in Admin"/> + <title value="Admin should be able to associate grouped product to websites"/> + <description value="Admin should be able to associate grouped product to websites"/> + <testCaseId value="MC-3377"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="groupedProduct"/> + </annotations> + + <before> + <!-- Set Store Code To Urls --> + <magentoCLI command="config:set {{StorefrontEnableAddStoreCodeToUrls.path}} {{StorefrontEnableAddStoreCodeToUrls.value}}" stepKey="setAddStoreCodeToUrlsToYes"/> + + <!-- Create grouped product --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + <createData entity="ApiGroupedProduct" stepKey="createGroupedProduct"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Create website--> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> + <argument name="newWebsiteName" value="{{secondCustomWebsite.name}}"/> + <argument name="websiteCode" value="{{secondCustomWebsite.code}}"/> + </actionGroup> + <!-- Create second store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createSecondStoreGroup"> + <argument name="website" value="{{secondCustomWebsite.name}}"/> + <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> + <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> + </actionGroup> + <!-- Create second store view --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView"> + <argument name="StoreGroup" value="SecondStoreGroupUnique"/> + <argument name="customStore" value="SecondStoreUnique"/> + </actionGroup> + + <!-- Reindex --> + <magentoCLI command="indexer:reindex" stepKey="reindexAllIndexes"/> + </before> + + <after> + <!-- Disable Store Code To Urls --> + <magentoCLI command="config:set {{StorefrontDisableAddStoreCodeToUrls.path}} {{StorefrontDisableAddStoreCodeToUrls.value}}" stepKey="setAddStoreCodeToUrlsToNo"/> + + <!-- Delete product data --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> + + <!-- Delete second website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{secondCustomWebsite.name}}"/> + </actionGroup> + + <actionGroup ref="NavigateToAndResetProductGridToDefaultView" stepKey="resetProductGridFilter"/> + + <!-- Admin logout --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Open product page and assign grouped project to second website --> + <actionGroup ref="filterAndSelectProduct" stepKey="openAdminProductPage"> + <argument name="productSku" value="$$createGroupedProduct.sku$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="assignProductToSecondWebsite"> + <argument name="website" value="{{secondCustomWebsite.name}}"/> + </actionGroup> + <actionGroup ref="AdminUnassignProductInWebsiteActionGroup" stepKey="unassignProductFromDefaultWebsite"> + <argument name="website" value="{{_defaultWebsite.name}}"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveGroupedProduct"/> + + <!-- Assert product is assigned to Second website --> + <actionGroup ref="AssertProductIsAssignedToWebsite" stepKey="seeCustomWebsiteIsChecked"> + <argument name="website" value="{{secondCustomWebsite.name}}"/> + </actionGroup> + + <!-- Assert product is not assigned to Main website --> + <actionGroup ref="AssertProductIsNotAssignedToWebsite" stepKey="seeMainWebsiteIsNotChecked"> + <argument name="website" value="{{_defaultWebsite.name}}"/> + </actionGroup> + + <!-- Go to frontend and open product on Main website --> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> + <argument name="productUrl" value="$$createGroupedProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <!-- Assert 404 page --> + <actionGroup ref="StorefrontAssertPageNotFoundErrorOnProductDetailPageActionGroup" stepKey="assertPageNotFoundErrorOnProductDetailPage"> + <argument name="product" value="$$createGroupedProduct$$"/> + </actionGroup> + + <!-- Assert grouped product on Second website --> + <actionGroup ref="StorefrontOpenProductPageUsingStoreCodeInUrlActionGroup" stepKey="openProductPageUsingStoreCodeInUrl"> + <argument name="product" value="$$createGroupedProduct$$"/> + <argument name="storeView" value="SecondStoreUnique"/> + </actionGroup> + </test> +</tests> From d2c03a6041ddc6e409ccaee3d6dadc97b8868ada Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 12 Sep 2019 11:45:40 +0300 Subject: [PATCH 32/55] MC-3344: Admin should be able to associate bundle product to websites --- ...inAssociateBundleProductToWebsitesTest.xml | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml new file mode 100644 index 000000000000..505a319c5c44 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml @@ -0,0 +1,126 @@ +<?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="AdminAssociateBundleProductToWebsitesTest"> + <annotations> + <features value="Bundle"/> + <stories value="Create/Edit bundle product in Admin"/> + <title value="Admin should be able to associate bundle product to websites"/> + <description value="Admin should be able to associate bundle product to websites"/> + <testCaseId value="MC-3344"/> + <severity value="CRITICAL"/> + <group value="bundle"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Configure Store URLs --> + <magentoCLI command="config:set {{StorefrontEnableAddStoreCodeToUrls.path}} {{StorefrontEnableAddStoreCodeToUrls.value}}" stepKey="setAddStoreCodeToUrlsToYes"/> + + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <!-- Create Simple product --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + + <!-- Create Bundle product --> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createNewBundleLink"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + + <!-- Reindex --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Create website--> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> + <argument name="newWebsiteName" value="{{secondCustomWebsite.name}}"/> + <argument name="websiteCode" value="{{secondCustomWebsite.code}}"/> + </actionGroup> + <!-- Create second store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createSecondStoreGroup"> + <argument name="website" value="{{secondCustomWebsite.name}}"/> + <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> + <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> + </actionGroup> + <!-- Create second store view --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView"> + <argument name="StoreGroup" value="SecondStoreGroupUnique"/> + <argument name="customStore" value="SecondStoreUnique"/> + </actionGroup> + </before> + <after> + <!-- Disabled Store URLs --> + <magentoCLI command="config:set {{StorefrontDisableAddStoreCodeToUrls.path}} {{StorefrontDisableAddStoreCodeToUrls.value}}" stepKey="setAddStoreCodeToUrlsToNo"/> + + <!-- Delete simple product --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <!-- Delete bundle product --> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + + <!-- Delete second website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{secondCustomWebsite.name}}"/> + </actionGroup> + + <actionGroup ref="NavigateToAndResetProductGridToDefaultView" stepKey="resetProductGridFilter"/> + + <!-- Admin logout --> + <actionGroup ref="logout" stepKey="adminLogout"/> + </after> + + <!-- Open product page and assign grouped project to second website --> + <actionGroup ref="filterAndSelectProduct" stepKey="openAdminProductPage"> + <argument name="productSku" value="$$createBundleProduct.sku$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="assignProductToSecondWebsite"> + <argument name="website" value="{{secondCustomWebsite.name}}"/> + </actionGroup> + <actionGroup ref="AdminUnassignProductInWebsiteActionGroup" stepKey="unassignProductFromDefaultWebsite"> + <argument name="website" value="{{_defaultWebsite.name}}"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveGroupedProduct"/> + + <!-- Assert product is assigned to Second website --> + <actionGroup ref="AssertProductIsAssignedToWebsite" stepKey="seeCustomWebsiteIsChecked"> + <argument name="website" value="{{secondCustomWebsite.name}}"/> + </actionGroup> + + <!-- Assert product is not assigned to Main website --> + <actionGroup ref="AssertProductIsNotAssignedToWebsite" stepKey="seeMainWebsiteIsNotChecked"> + <argument name="website" value="{{_defaultWebsite.name}}"/> + </actionGroup> + + <!-- Go to frontend and open product on Main website --> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> + <argument name="productUrl" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <!-- Assert 404 page --> + <actionGroup ref="StorefrontAssertPageNotFoundErrorOnProductDetailPageActionGroup" stepKey="assertPageNotFoundError"> + <argument name="product" value="$$createBundleProduct$$"/> + </actionGroup> + + <!-- Assert product is present at Second website --> + <actionGroup ref="StorefrontOpenProductPageUsingStoreCodeInUrlActionGroup" stepKey="openProductPageUsingStoreCodeInUrl"> + <argument name="product" value="$$createBundleProduct$$"/> + <argument name="storeView" value="SecondStoreUnique"/> + </actionGroup> + </test> +</tests> From 41a35bd439a3ddf68d016d5d699fc32c4d19976d Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 28 Aug 2019 16:21:54 +0300 Subject: [PATCH 33/55] magento/magento2#: Captcha. Improvement. Replace deprecated addError with addErrorMessage. magento/magento2#: Replace deprecated Magento\Customer\Model\Customer::isConfirmationRequired method magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento/Customer/Controller/Account/Confirmation.php --- .../Observer/CheckContactUsFormObserver.php | 5 ++- .../Observer/CheckForgotpasswordObserver.php | 5 ++- .../Observer/CheckUserCreateObserver.php | 7 +++- .../Observer/CheckUserEditObserver.php | 11 ++++-- ...CheckUserForgotPasswordBackendObserver.php | 7 +++- .../Observer/CheckUserLoginObserver.php | 10 +++-- .../CheckContactUsFormObserverTest.php | 9 +++-- .../CheckForgotpasswordObserverTest.php | 2 +- .../Observer/CheckUserCreateObserverTest.php | 2 +- .../Observer/CheckUserEditObserverTest.php | 2 +- .../Observer/CheckUserLoginObserverTest.php | 2 +- .../Controller/Account/Confirmation.php | 21 +++++++---- app/code/Magento/Customer/Model/Customer.php | 29 ++++++++++----- .../Customer/Model/ResourceModel/Customer.php | 25 +++++++++++-- app/code/Magento/Customer/Model/Session.php | 37 ++++++++++++++++--- .../Customer/Test/Unit/Model/SessionTest.php | 9 ++--- 16 files changed, 132 insertions(+), 51 deletions(-) diff --git a/app/code/Magento/Captcha/Observer/CheckContactUsFormObserver.php b/app/code/Magento/Captcha/Observer/CheckContactUsFormObserver.php index 91737c1a3d77..8c1da0e1ef10 100644 --- a/app/code/Magento/Captcha/Observer/CheckContactUsFormObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckContactUsFormObserver.php @@ -9,6 +9,9 @@ use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\App\ObjectManager; +/** + * Class CheckContactUsFormObserver + */ class CheckContactUsFormObserver implements ObserverInterface { /** @@ -76,7 +79,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) /** @var \Magento\Framework\App\Action\Action $controller */ $controller = $observer->getControllerAction(); if (!$captcha->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { - $this->messageManager->addError(__('Incorrect CAPTCHA.')); + $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA.')); $this->getDataPersistor()->set($formId, $controller->getRequest()->getPostValue()); $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); $this->redirect->redirect($controller->getResponse(), 'contact/index/index'); diff --git a/app/code/Magento/Captcha/Observer/CheckForgotpasswordObserver.php b/app/code/Magento/Captcha/Observer/CheckForgotpasswordObserver.php index 0736c7514a56..623d11903926 100644 --- a/app/code/Magento/Captcha/Observer/CheckForgotpasswordObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckForgotpasswordObserver.php @@ -7,6 +7,9 @@ use Magento\Framework\Event\ObserverInterface; +/** + * Class CheckForgotpasswordObserver + */ class CheckForgotpasswordObserver implements ObserverInterface { /** @@ -69,7 +72,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) /** @var \Magento\Framework\App\Action\Action $controller */ $controller = $observer->getControllerAction(); if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { - $this->messageManager->addError(__('Incorrect CAPTCHA')); + $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA')); $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); $this->redirect->redirect($controller->getResponse(), '*/*/forgotpassword'); } diff --git a/app/code/Magento/Captcha/Observer/CheckUserCreateObserver.php b/app/code/Magento/Captcha/Observer/CheckUserCreateObserver.php index 6d2ed4d1050c..ef66116432f5 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserCreateObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserCreateObserver.php @@ -7,6 +7,11 @@ use Magento\Framework\Event\ObserverInterface; +/** + * Class CheckUserCreateObserver + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class CheckUserCreateObserver implements ObserverInterface { /** @@ -86,7 +91,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) /** @var \Magento\Framework\App\Action\Action $controller */ $controller = $observer->getControllerAction(); if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { - $this->messageManager->addError(__('Incorrect CAPTCHA')); + $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA')); $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); $this->_session->setCustomerFormData($controller->getRequest()->getPostValue()); $url = $this->_urlManager->getUrl('*/*/create', ['_nosecret' => true]); diff --git a/app/code/Magento/Captcha/Observer/CheckUserEditObserver.php b/app/code/Magento/Captcha/Observer/CheckUserEditObserver.php index 9d3cd8d36709..70ccfeae6b7e 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserEditObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserEditObserver.php @@ -11,7 +11,9 @@ use Magento\Framework\App\Config\ScopeConfigInterface; /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * Class CheckUserEditObserver + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class CheckUserEditObserver implements ObserverInterface { @@ -96,7 +98,8 @@ public function __construct( * Check Captcha On Forgot Password Page * * @param \Magento\Framework\Event\Observer $observer - * @return $this + * @return $this|void + * @throws \Magento\Framework\Exception\SessionException */ public function execute(\Magento\Framework\Event\Observer $observer) { @@ -119,9 +122,9 @@ public function execute(\Magento\Framework\Event\Observer $observer) 'The account is locked. Please wait and try again or contact %1.', $this->scopeConfig->getValue('contact/email/recipient_email') ); - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } - $this->messageManager->addError(__('Incorrect CAPTCHA')); + $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA')); $this->actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); $this->redirect->redirect($controller->getResponse(), '*/*/edit'); } diff --git a/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php b/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php index 2de93dcf6b59..e11e48a52716 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php @@ -7,6 +7,11 @@ use Magento\Framework\Event\ObserverInterface; +/** + * Class CheckUserForgotPasswordBackendObserver + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class CheckUserForgotPasswordBackendObserver implements ObserverInterface { /** @@ -76,7 +81,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) ) { $this->_session->setEmail((string)$controller->getRequest()->getPost('email')); $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $this->messageManager->addError(__('Incorrect CAPTCHA')); + $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA')); $controller->getResponse()->setRedirect( $controller->getUrl('*/*/forgotpassword', ['_nosecret' => true]) ); diff --git a/app/code/Magento/Captcha/Observer/CheckUserLoginObserver.php b/app/code/Magento/Captcha/Observer/CheckUserLoginObserver.php index dd4974c5d842..27507423e77e 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserLoginObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserLoginObserver.php @@ -6,10 +6,10 @@ namespace Magento\Captcha\Observer; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Model\AuthenticationInterface; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Customer\Api\CustomerRepositoryInterface; /** * Check captcha on user login page observer. @@ -64,6 +64,8 @@ class CheckUserLoginObserver implements ObserverInterface protected $authentication; /** + * CheckUserLoginObserver constructor. + * * @param \Magento\Captcha\Helper\Data $helper * @param \Magento\Framework\App\ActionFlag $actionFlag * @param \Magento\Framework\Message\ManagerInterface $messageManager @@ -125,8 +127,7 @@ private function getAuthentication() * Check captcha on user login page * * @param \Magento\Framework\Event\Observer $observer - * @throws NoSuchEntityException - * @return $this + * @return $this|void */ public function execute(\Magento\Framework\Event\Observer $observer) { @@ -143,10 +144,11 @@ public function execute(\Magento\Framework\Event\Observer $observer) try { $customer = $this->getCustomerRepository()->get($login); $this->getAuthentication()->processAuthenticationFailure($customer->getId()); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (NoSuchEntityException $e) { //do nothing as customer existence is validated later in authenticate method } - $this->messageManager->addError(__('Incorrect CAPTCHA')); + $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA')); $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); $this->_session->setUsername($login); $beforeUrl = $this->_session->getBeforeAuthUrl(); diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckContactUsFormObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckContactUsFormObserverTest.php index 08f76aa74ac6..83bfb2910f9f 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckContactUsFormObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckContactUsFormObserverTest.php @@ -69,7 +69,10 @@ protected function setUp() $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); $this->redirectMock = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); $this->captchaStringResolverMock = $this->createMock(\Magento\Captcha\Observer\CaptchaStringResolver::class); - $this->sessionMock = $this->createPartialMock(\Magento\Framework\Session\SessionManager::class, ['addError']); + $this->sessionMock = $this->createPartialMock( + \Magento\Framework\Session\SessionManager::class, + ['addErrorMessage'] + ); $this->dataPersistorMock = $this->getMockBuilder(\Magento\Framework\App\Request\DataPersistorInterface::class) ->getMockForAbstractClass(); @@ -116,7 +119,7 @@ public function testCheckContactUsFormWhenCaptchaIsRequiredAndValid() $this->helperMock->expects($this->any()) ->method('getCaptcha') ->with($formId)->willReturn($this->captchaMock); - $this->sessionMock->expects($this->never())->method('addError'); + $this->sessionMock->expects($this->never())->method('addErrorMessage'); $this->checkContactUsFormObserver->execute( new \Magento\Framework\Event\Observer(['controller_action' => $controller]) @@ -163,7 +166,7 @@ public function testCheckContactUsFormRedirectsCustomerWithWarningMessageWhenCap ->method('getCaptcha') ->with($formId) ->willReturn($this->captchaMock); - $this->messageManagerMock->expects($this->once())->method('addError')->with($warningMessage); + $this->messageManagerMock->expects($this->once())->method('addErrorMessage')->with($warningMessage); $this->actionFlagMock->expects($this->once()) ->method('set') ->with('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckForgotpasswordObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckForgotpasswordObserverTest.php index b05a3b2e34af..93b58191cc33 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckForgotpasswordObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckForgotpasswordObserverTest.php @@ -138,7 +138,7 @@ public function testCheckForgotpasswordRedirects() )->will( $this->returnValue($this->_captcha) ); - $this->_messageManager->expects($this->once())->method('addError')->with($warningMessage); + $this->_messageManager->expects($this->once())->method('addErrorMessage')->with($warningMessage); $this->_actionFlag->expects( $this->once() )->method( diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserCreateObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserCreateObserverTest.php index 8dc67437f487..a57faabda99e 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserCreateObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserCreateObserverTest.php @@ -151,7 +151,7 @@ public function testCheckUserCreateRedirectsError() )->will( $this->returnValue($this->_captcha) ); - $this->_messageManager->expects($this->once())->method('addError')->with($warningMessage); + $this->_messageManager->expects($this->once())->method('addErrorMessage')->with($warningMessage); $this->_actionFlag->expects( $this->once() )->method( diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserEditObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserEditObserverTest.php index 26fd8fd928c5..0f08e5c569df 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserEditObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserEditObserverTest.php @@ -146,7 +146,7 @@ public function testExecute() $message = __('The account is locked. Please wait and try again or contact %1.', $email); $this->messageManagerMock->expects($this->exactly(2)) - ->method('addError') + ->method('addErrorMessage') ->withConsecutive([$message], [__('Incorrect CAPTCHA')]); $this->actionFlagMock->expects($this->once()) diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginObserverTest.php index 19dc096b9ef6..0499ec3255c5 100644 --- a/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginObserverTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Observer/CheckUserLoginObserverTest.php @@ -145,7 +145,7 @@ public function testExecute() ->with($customerId); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('Incorrect CAPTCHA')); $this->actionFlagMock->expects($this->once()) diff --git a/app/code/Magento/Customer/Controller/Account/Confirmation.php b/app/code/Magento/Customer/Controller/Account/Confirmation.php index a3e2db020763..59def8640328 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirmation.php +++ b/app/code/Magento/Customer/Controller/Account/Confirmation.php @@ -1,21 +1,26 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Customer\Controller\Account; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Controller\AbstractAccount; +use Magento\Customer\Model\Session; use Magento\Customer\Model\Url; +use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\App\Action\Context; -use Magento\Customer\Model\Session; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\State\InvalidTransitionException; use Magento\Framework\View\Result\PageFactory; use Magento\Store\Model\StoreManagerInterface; -use Magento\Customer\Api\AccountManagementInterface; -use Magento\Framework\Exception\State\InvalidTransitionException; -class Confirmation extends \Magento\Customer\Controller\AbstractAccount +/** + * Class Confirmation. Send confirmation link to specified email + */ +class Confirmation extends AbstractAccount implements HttpGetActionInterface, HttpPostActionInterface { /** * @var \Magento\Store\Model\StoreManagerInterface @@ -91,11 +96,11 @@ public function execute() $email, $this->storeManager->getStore()->getWebsiteId() ); - $this->messageManager->addSuccess(__('Please check your email for confirmation key.')); + $this->messageManager->addSuccessMessage(__('Please check your email for confirmation key.')); } catch (InvalidTransitionException $e) { - $this->messageManager->addSuccess(__('This email does not require confirmation.')); + $this->messageManager->addSuccessMessage(__('This email does not require confirmation.')); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Wrong email.')); + $this->messageManager->addExceptionMessage($e, __('Wrong email.')); $resultRedirect->setPath('*/*/*', ['email' => $email, '_secure' => true]); return $resultRedirect; } diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php index 1287dbe5df70..34f1e5e2da46 100644 --- a/app/code/Magento/Customer/Model/Customer.php +++ b/app/code/Magento/Customer/Model/Customer.php @@ -394,7 +394,9 @@ public function getSharingConfig() public function authenticate($login, $password) { $this->loadByEmail($login); - if ($this->getConfirmation() && $this->isConfirmationRequired()) { + if ($this->getConfirmation() && + $this->accountConfirmation->isConfirmationRequired($this->getWebsiteId(), $this->getId(), $this->getEmail()) + ) { throw new EmailNotConfirmedException( __("This account isn't confirmed. Verify and try again.") ); @@ -415,8 +417,9 @@ public function authenticate($login, $password) /** * Load customer by email * - * @param string $customerEmail - * @return $this + * @param string $customerEmail + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException */ public function loadByEmail($customerEmail) { @@ -427,8 +430,9 @@ public function loadByEmail($customerEmail) /** * Change customer password * - * @param string $newPassword - * @return $this + * @param string $newPassword + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException */ public function changePassword($newPassword) { @@ -440,6 +444,7 @@ public function changePassword($newPassword) * Get full customer name * * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ public function getName() { @@ -462,8 +467,9 @@ public function getName() /** * Add address to address collection * - * @param Address $address - * @return $this + * @param Address $address + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException */ public function addAddress(Address $address) { @@ -487,6 +493,7 @@ public function getAddressById($addressId) * * @param int $addressId * @return Address + * @throws \Magento\Framework\Exception\LocalizedException */ public function getAddressItemById($addressId) { @@ -506,7 +513,8 @@ public function getAddressCollection() /** * Customer addresses collection * - * @return \Magento\Customer\Model\ResourceModel\Address\Collection + * @return ResourceModel\Address\Collection + * @throws \Magento\Framework\Exception\LocalizedException */ public function getAddressesCollection() { @@ -538,6 +546,7 @@ public function getAddresses() * Retrieve all customer attributes * * @return Attribute[] + * @throws \Magento\Framework\Exception\LocalizedException */ public function getAttributes() { @@ -591,7 +600,8 @@ public function hashPassword($password, $salt = true) * Validate password with salted hash * * @param string $password - * @return boolean + * @return bool + * @throws \Exception */ public function validatePassword($password) { @@ -805,6 +815,7 @@ public function isConfirmationRequired() */ public function getRandomConfirmationKey() { + // phpcs:ignore Magento2.Security.InsecureFunction return md5(uniqid()); } diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer.php b/app/code/Magento/Customer/Model/ResourceModel/Customer.php index 94196df6fe09..1477287f79f4 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Customer.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Customer.php @@ -6,6 +6,7 @@ namespace Magento\Customer\Model\ResourceModel; +use Magento\Customer\Model\AccountConfirmation; use Magento\Customer\Model\Customer\NotificationStorage; use Magento\Framework\App\ObjectManager; use Magento\Framework\Validator\Exception as ValidatorException; @@ -42,12 +43,19 @@ class Customer extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity */ protected $storeManager; + /** + * @var AccountConfirmation + */ + private $accountConfirmation; + /** * @var NotificationStorage */ private $notificationStorage; /** + * Customer constructor. + * * @param \Magento\Eav\Model\Entity\Context $context * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite @@ -56,6 +64,7 @@ class Customer extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param array $data + * @param AccountConfirmation $accountConfirmation */ public function __construct( \Magento\Eav\Model\Entity\Context $context, @@ -65,15 +74,19 @@ public function __construct( \Magento\Framework\Validator\Factory $validatorFactory, \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Store\Model\StoreManagerInterface $storeManager, - $data = [] + $data = [], + AccountConfirmation $accountConfirmation = null ) { parent::__construct($context, $entitySnapshot, $entityRelationComposite, $data); + $this->_scopeConfig = $scopeConfig; $this->_validatorFactory = $validatorFactory; $this->dateTime = $dateTime; - $this->storeManager = $storeManager; + $this->accountConfirmation = $accountConfirmation ?: ObjectManager::getInstance() + ->get(AccountConfirmation::class); $this->setType('customer'); $this->setConnection('customer_read', 'customer_write'); + $this->storeManager = $storeManager; } /** @@ -144,7 +157,13 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer) } // set confirmation key logic - if (!$customer->getId() && $customer->isConfirmationRequired()) { + if (!$customer->getId() && + $this->accountConfirmation->isConfirmationRequired( + $customer->getWebsiteId(), + $customer->getId(), + $customer->getEmail() + ) + ) { $customer->setConfirmation($customer->getRandomConfirmationKey()); } // remove customer confirmation key from database, if empty diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index 5900fed218ed..047327a0b6c2 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -10,6 +10,7 @@ use Magento\Customer\Api\GroupManagementInterface; use Magento\Customer\Model\Config\Share; use Magento\Customer\Model\ResourceModel\Customer as ResourceCustomer; +use Magento\Framework\App\ObjectManager; /** * Customer session model @@ -17,6 +18,7 @@ * @api * @method string getNoReferer() * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Session extends \Magento\Framework\Session\SessionManager @@ -107,6 +109,8 @@ class Session extends \Magento\Framework\Session\SessionManager protected $response; /** + * Session constructor. + * * @param \Magento\Framework\App\Request\Http $request * @param \Magento\Framework\Session\SidResolverInterface $sidResolver * @param \Magento\Framework\Session\Config\ConfigInterface $sessionConfig @@ -118,7 +122,7 @@ class Session extends \Magento\Framework\Session\SessionManager * @param \Magento\Framework\App\State $appState * @param Share $configShare * @param \Magento\Framework\Url\Helper\Data $coreUrl - * @param \Magento\Customer\Model\Url $customerUrl + * @param Url $customerUrl * @param ResourceCustomer $customerResource * @param CustomerFactory $customerFactory * @param \Magento\Framework\UrlFactory $urlFactory @@ -128,6 +132,7 @@ class Session extends \Magento\Framework\Session\SessionManager * @param CustomerRepositoryInterface $customerRepository * @param GroupManagementInterface $groupManagement * @param \Magento\Framework\App\Response\Http $response + * @param AccountConfirmation $accountConfirmation * @throws \Magento\Framework\Exception\SessionException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -152,7 +157,8 @@ public function __construct( \Magento\Framework\App\Http\Context $httpContext, CustomerRepositoryInterface $customerRepository, GroupManagementInterface $groupManagement, - \Magento\Framework\App\Response\Http $response + \Magento\Framework\App\Response\Http $response, + AccountConfirmation $accountConfirmation = null ) { $this->_coreUrl = $coreUrl; $this->_customerUrl = $customerUrl; @@ -177,6 +183,8 @@ public function __construct( ); $this->groupManagement = $groupManagement; $this->response = $response; + $this->accountConfirmation = $accountConfirmation ?: ObjectManager::getInstance() + ->get(AccountConfirmation::class); $this->_eventManager->dispatch('customer_session_init', ['customer_session' => $this]); } @@ -216,6 +224,8 @@ public function setCustomerData(CustomerData $customer) * Retrieve customer model object * * @return CustomerData + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getCustomerData() { @@ -266,8 +276,14 @@ public function setCustomer(Customer $customerModel) \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID ); $this->setCustomerId($customerModel->getId()); - if (!$customerModel->isConfirmationRequired() && $customerModel->getConfirmation()) { - $customerModel->setConfirmation(null)->save(); + $accountConfirmationRequired = $this->accountConfirmation->isConfirmationRequired( + $customerModel->getWebsiteId(), + $customerModel->getId(), + $customerModel->getEmail() + ); + if (!$accountConfirmationRequired && $customerModel->getConfirmation() && $customerModel->getId()) { + $customerModel->setConfirmation(null); + $this->_customerResource->save($customerModel); } /** @@ -354,10 +370,11 @@ public function setCustomerGroupId($id) } /** - * Get customer group id - * If customer is not logged in system, 'not logged in' group id will be returned + * Get customer group id. If customer is not logged in system, 'not logged in' group id will be returned. * * @return int + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getCustomerGroupId() { @@ -407,6 +424,8 @@ public function checkCustomerId($customerId) } /** + * Sets customer as logged in + * * @param Customer $customer * @return $this */ @@ -420,6 +439,8 @@ public function setCustomerAsLoggedIn($customer) } /** + * Sets customer data as logged in + * * @param CustomerData $customer * @return $this */ @@ -521,6 +542,8 @@ protected function _setAuthUrl($key, $url) * Logout without dispatching event * * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ protected function _logout() { @@ -567,6 +590,8 @@ public function regenerateId() } /** + * Creates URL factory + * * @return \Magento\Framework\UrlInterface */ protected function _createUrl() diff --git a/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php b/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php index 7efc61af800d..8565790990df 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/SessionTest.php @@ -66,7 +66,7 @@ protected function setUp() $this->urlFactoryMock = $this->createMock(\Magento\Framework\UrlFactory::class); $this->customerFactoryMock = $this->getMockBuilder(\Magento\Customer\Model\CustomerFactory::class) ->disableOriginalConstructor() - ->setMethods(['create']) + ->setMethods(['create', 'save']) ->getMock(); $this->customerRepositoryMock = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -192,15 +192,12 @@ protected function prepareLoginDataMock($customerId) $customerMock = $this->createPartialMock( \Magento\Customer\Model\Customer::class, - ['getId', 'isConfirmationRequired', 'getConfirmation', 'updateData', 'getGroupId'] + ['getId', 'getConfirmation', 'updateData', 'getGroupId'] ); - $customerMock->expects($this->once()) + $customerMock->expects($this->exactly(3)) ->method('getId') ->will($this->returnValue($customerId)); $customerMock->expects($this->once()) - ->method('isConfirmationRequired') - ->will($this->returnValue(true)); - $customerMock->expects($this->never()) ->method('getConfirmation') ->will($this->returnValue($customerId)); From bdd053a9c0781a1fbf4e397a78dd33bb06bb5bf6 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 12 Sep 2019 12:43:32 +0300 Subject: [PATCH 34/55] MC-11557: Check importing of configurable products with images present in filesystem --- .../Catalog/Test/Mftf/Data/CategoryData.xml | 7 + .../Test/Mftf/Data/FrontendLabelData.xml | 4 + .../Test/Mftf/Data/ImageContentData.xml | 5 + .../Test/Mftf/Data/ProductAttributeData.xml | 22 ++ .../ProductAttributeMediaGalleryEntryData.xml | 13 + .../Mftf/Data/ProductAttributeOptionData.xml | 17 ++ .../Catalog/Test/Mftf/Data/ProductData.xml | 36 +++ .../Test/AdminMassProductPriceUpdateTest.xml | 6 +- .../AdminOpenExportIndexPageActionGroup.xml | 15 ++ ...mportConfigurableProductWithImagesTest.xml | 222 ++++++++++++++++++ .../Mftf/Data/ConfigurableProductData.xml | 15 ++ .../AdminProductFormConfigurationsSection.xml | 2 + .../export_import_configurable_product.csv | 2 + .../export_import_configurable_product_2.csv | 2 + 14 files changed, 365 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminOpenExportIndexPageActionGroup.xml create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml create mode 100644 dev/tests/acceptance/tests/_data/export_import_configurable_product.csv create mode 100644 dev/tests/acceptance/tests/_data/export_import_configurable_product_2.csv diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml index 13951a0d197d..bc013eedb0f4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -117,4 +117,11 @@ <data key="is_active">true</data> <data key="include_in_menu">true</data> </entity> + <!-- Category from file "export_import_configurable_product.csv" --> + <entity name="CategoryExportImport" type="category"> + <data key="name">CategoryExportImport</data> + <data key="name_lwr">categoryExportImport</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml index a2bdaa7dbc62..e4ffdbde4368 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml @@ -20,4 +20,8 @@ <data key="store_id">0</data> <data key="label" unique="suffix">attributeThree</data> </entity> + <entity name="ProductAttributeFrontendLabelForExportImport" type="FrontendLabel"> + <data key="store_id">0</data> + <data key="label">attributeExportImport</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml index 1f4b1470098e..ac86deb671ed 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml @@ -18,4 +18,9 @@ <data key="type">image/png</data> <data key="name" unique="prefix">magento-logo.png</data> </entity> + <entity name="MagentoLogoImageContentExportImport" type="ImageContent"> + <data key="base64_encoded_data">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</data> + <data key="type">image/png</data> + <data key="name">magento-logo.png</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index 2deec6b8c1f8..0bdc3a261053 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -376,4 +376,26 @@ <data key="frontend_label">Size</data> <data key="attribute_code" unique="suffix">size_attr</data> </entity> + <!-- Product attribute from file "export_import_configurable_product.csv" --> + <entity name="productAttributeWithTwoOptionsForExportImport" type="ProductAttribute"> + <data key="attribute_code">attribute</data> + <data key="frontend_input">select</data> + <data key="scope">global</data> + <data key="is_required">false</data> + <data key="is_unique">false</data> + <data key="is_searchable">true</data> + <data key="is_visible">true</data> + <data key="is_visible_in_advanced_search">true</data> + <data key="is_visible_on_front">true</data> + <data key="is_filterable">true</data> + <data key="is_filterable_in_search">true</data> + <data key="used_in_product_listing">true</data> + <data key="is_used_for_promo_rules">true</data> + <data key="is_comparable">true</data> + <data key="is_used_in_grid">true</data> + <data key="is_visible_in_grid">true</data> + <data key="is_filterable_in_grid">true</data> + <data key="used_for_sort_by">true</data> + <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabelForExportImport</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml index 98c9a70e6aad..7d7f2cb7bf4b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml @@ -30,4 +30,17 @@ <data key="disabled">false</data> <requiredEntity type="ImageContent">MagentoLogoImageContent</requiredEntity> </entity> + <!-- From file "export_import_configurable_product.csv" --> + <entity name="ApiProductAttributeMediaGalleryForExportImport" type="ProductAttributeMediaGalleryEntry"> + <data key="media_type">image</data> + <data key="label">Magento Logo</data> + <data key="position">1</data> + <array key="types"> + <item>image</item> + <item>small_image</item> + <item>thumbnail</item> + </array> + <data key="disabled">false</data> + <requiredEntity type="ImageContent">MagentoLogoImageContentExportImport</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index fcb56cf298a9..8daa63f08d32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -86,4 +86,21 @@ <data key="label" unique="suffix">White</data> <data key="value" unique="suffix">white</data> </entity> + <!-- Product attribute options from file "export_import_configurable_product.csv" --> + <entity name="productAttributeOptionOneForExportImport" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label">option1</data> + <data key="is_default">false</data> + <data key="sort_order">0</data> + <requiredEntity type="StoreLabel">Option1Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option1Store1</requiredEntity> + </entity> + <entity name="productAttributeOptionTwoForExportImport" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label">option2</data> + <data key="is_default">true</data> + <data key="sort_order">1</data> + <requiredEntity type="StoreLabel">Option2Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option2Store1</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 517ab253b823..faad9094b8d7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -351,6 +351,15 @@ <data key="filename">adobe-base</data> <data key="file_extension">jpg</data> </entity> + <entity name="TestImage" type="image"> + <data key="title" unique="suffix">test_image</data> + <data key="price">1.00</data> + <data key="file_type">Upload File</data> + <data key="shareable">Yes</data> + <data key="file">test_image.jpg</data> + <data key="filename">test_image</data> + <data key="file_extension">jpg</data> + </entity> <entity name="ProductWithUnicode" type="product"> <data key="sku" unique="suffix">霁产品</data> <data key="type_id">simple</data> @@ -1165,6 +1174,33 @@ <requiredEntity type="product_extension_attribute">EavStock10</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> + <!-- Products from file "export_import_configurable_product.csv" --> + <entity name="ApiSimpleOneExportImport" type="product2"> + <data key="sku">api-simple-one-export-import</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name">Api Simple Product One Export Import</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-simple-one-export-import</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> + </entity> + <entity name="ApiSimpleTwoExportImport" type="product2"> + <data key="sku">api-simple-two-export-import</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name">Api Simple Product Two Export Import</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-simple-two-export-import</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> + </entity> <entity name="SimpleProductPrice10Qty1" type="product"> <data key="sku" unique="suffix">simple-product_</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index 4d581bae700d..f5ad5b8079d1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMassProductPriceUpdateTest"> <annotations> - <stories value="Mass product update "/> + <stories value="Mass product update"/> <features value="Catalog"/> <title value="Mass update simple product price"/> <description value="Login as admin and update mass product price"/> @@ -24,8 +24,8 @@ <createData entity="defaultSimpleProduct" stepKey="simpleProduct2"/> </before> <after> - <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminOpenExportIndexPageActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminOpenExportIndexPageActionGroup.xml new file mode 100644 index 000000000000..83762d347a93 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminOpenExportIndexPageActionGroup.xml @@ -0,0 +1,15 @@ +<?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="AdminOpenExportIndexPageActionGroup"> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml new file mode 100644 index 000000000000..7548c0991289 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml @@ -0,0 +1,222 @@ +<?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="AdminExportImportConfigurableProductWithImagesTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Export/Import products"/> + <title value="Check importing of configurable products with images present in filesystem"/> + <description value="Check importing of configurable products with images present in filesystem"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-11557"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- Create sample data: + 1. Create simple products --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="createFirstSimpleProduct"/> + <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> + + <!-- 2. Create Downloadable product --> + <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="createDownloadableProduct"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="createDownloadableProduct"/> + </createData> + + <!-- 3. Create Grouped product --> + <createData entity="ApiGroupedProduct" stepKey="createGroupedProduct"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createFirstSimpleProduct"/> + </createData> + + <!-- 4. Create configurable product with images --> + <createData entity="CategoryExportImport" stepKey="createExportImportCategory"/> + <createData entity="ApiConfigurableExportImportProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createExportImportCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryForExportImport" stepKey="createConfigurableProductWithImage"> + <requiredEntity createDataKey="createConfigProduct"/> + </createData> + <createData entity="productAttributeWithTwoOptionsForExportImport" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOptionOneForExportImport" stepKey="createConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOptionTwoForExportImport" stepKey="createConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOneExportImport" stepKey="createConfigFirstChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryForExportImport" stepKey="createSimpleProduct1Image"> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + <createData entity="ApiSimpleTwoExportImport" stepKey="createConfigSecondChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image1"> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductTwoOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + + <!-- 5. Create configurable product --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigurableProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttr"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption"> + <requiredEntity createDataKey="createConfigProductAttr"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttrSet"> + <requiredEntity createDataKey="createConfigProductAttr"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption"> + <requiredEntity createDataKey="createConfigProductAttr"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct"> + <requiredEntity createDataKey="createConfigProductAttr"/> + <requiredEntity createDataKey="getConfigAttributeOption"/> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigurableProduct"/> + <requiredEntity createDataKey="createConfigProductAttr"/> + <requiredEntity createDataKey="getConfigAttributeOption"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild"> + <requiredEntity createDataKey="createConfigurableProduct"/> + <requiredEntity createDataKey="createConfigChildProduct"/> + </createData> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> + </before> + <after> + <!-- Delete created data --> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFisrtSimpleProduct"/> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> + <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteConfigFirstChildProduct"/> + <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteConfigSecondChildProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createConfigurableProduct" stepKey="deleteConfigurableProduct"/> + <deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/> + <deleteData createDataKey="createConfigProductAttr" stepKey="deleteConfigProductAttr"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createExportImportCategory" stepKey="deleteExportImportCategory"/> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <!-- Admin logout--> + <actionGroup ref="logout" stepKey="adminLogout"/> + </after> + + <!-- Go to Catalog > Products and choose one of configurable products: Products page is open --> + <actionGroup ref="filterAndSelectProduct" stepKey="openCreatedConfigurableProduct"> + <argument name="productSku" value="$$createConfigProduct.sku$$"/> + </actionGroup> + + <!-- Go to System > Export --> + <actionGroup ref="AdminOpenExportIndexPageActionGroup" stepKey="goToExportPage"/> + + <!-- Set Export Settings: Entity Type > Products, SKU > ConfProd's sku and press "Continue" --> + <actionGroup ref="exportProductsFilterByAttribute" stepKey="exportProductBySku"> + <argument name="attribute" value="sku"/> + <argument name="attributeData" value="$$createConfigProduct.sku$$"/> + </actionGroup> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCronFirstTime"/> + <magentoCLI command="cron:run" stepKey="runCronSecondTime"/> + + <!-- Save exported file: file successfully downloaded --> + <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <argument name="rowIndex" value="0"/> + </actionGroup> + + <!-- Go to Catalog > Products. Find ConfProd and delete it --> + <actionGroup ref="deleteProductBySku" stepKey="deleteConfigurableProductBySku"> + <argument name="sku" value="$$createConfigProduct.sku$$"/> + </actionGroup> + + <!-- Go to System > Import. Set import settings: Entity Type > Product, Import Behavior > Add/Update, + Select File to Import > previously exported file and press "Check Data" --> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProduct"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="export_import_configurable_product.csv"/> + <argument name="importMessage" value="Created: 1, Updated: 0, Deleted: 0"/> + </actionGroup> + + <!-- Go to Catalog > Products: Configurable product exists --> + <actionGroup ref="filterAndSelectProduct" stepKey="openConfigurableProduct"> + <argument name="productSku" value="$$createConfigProduct.sku$$"/> + </actionGroup> + + <!-- Go to "Configurations" section: configurations exist and have images --> + <seeNumberOfElements selector="{{AdminProductFormConfigurationsSection.currentVariationsRows}}" userInput="2" stepKey="seeNumberOfRows"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigFirstChildProduct.name$$" stepKey="seeFirstProductNameInField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" userInput="$$createConfigSecondChildProduct.name$$" stepKey="seeSecondProductNameInField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="$$createConfigFirstChildProduct.sku$$" stepKey="seeFirstProductSkuInField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="$$createConfigSecondChildProduct.sku$$" stepKey="seeSecondProductSkuInField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="$$createConfigFirstChildProduct.price$$" stepKey="seeFirstProductPriceInField"/> + <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="$$createConfigSecondChildProduct.price$$" stepKey="seeSecondProductPriceInField"/> + <seeElement selector="{{AdminProductFormConfigurationsSection.imageSource(MagentoLogo.fileName)}}" stepKey="seeFirstProductImageInField"/> + <seeElement selector="{{AdminProductFormConfigurationsSection.imageSource(TestImage.fileName)}}" stepKey="seeSecondProductImageInField"/> + + <!-- Go to "Images and Videos" section: assert image --> + <scrollTo selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" stepKey="scrollToProductGalleryTab"/> + <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + + <!-- Go to any ConfProd's configuration page: Product page open successfully --> + <click selector="{{AdminProductFormConfigurationsSection.productLinkByName($$createConfigFirstChildProduct.name$$)}}" stepKey="clickOnFirstProductLink"/> + <switchToNextTab stepKey="switchToConfigChildProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <!-- Go to "Images and Videos" section: assert image --> + <scrollTo selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" stepKey="scrollToChildProductGalleryTab"/> + <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertChildProductImageAdminProductPage"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <closeTab stepKey="closeConfigChildProductPage"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml index de6714a9b959..318791ae9453 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml @@ -78,4 +78,19 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <!-- Configurable product from file "export_import_configurable_product.csv"--> + <entity name="ApiConfigurableExportImportProduct" type="product"> + <data key="sku">api-configurable-export-import-product</data> + <data key="type_id">configurable</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name">API Configurable Export Import Product</data> + <data key="urlKey">api-configurable-export-import-product</data> + <data key="price">123.00</data> + <data key="weight">2</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml index 1defecbc7c28..58101e8173e5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -39,6 +39,8 @@ <element name="variationLabel" type="text" selector="//div[@data-index='configurable-matrix']/label"/> <element name="stepsWizardTitle" type="text" selector="div.content:not([style='display: none;']) .steps-wizard-title"/> <element name="attributeEntityByName" type="text" selector="//div[@class='attribute-entity']//div[normalize-space(.)='{{attributeLabel}}']" parameterized="true"/> + <element name="imageSource" type="text" selector=".admin__control-fields[data-index='thumbnail_image_container'] img[src*='{{imageName}}']" parameterized="true"/> + <element name="productLinkByName" type="text" selector="//a[contains(text(), '{{productName}}')]" parameterized="true"/> </section> <section name="AdminConfigurableProductFormSection"> <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> diff --git a/dev/tests/acceptance/tests/_data/export_import_configurable_product.csv b/dev/tests/acceptance/tests/_data/export_import_configurable_product.csv new file mode 100644 index 000000000000..97e63d06abe7 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/export_import_configurable_product.csv @@ -0,0 +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_label,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,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,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,bundle_shipment_type,associated_skus,configurable_variations,configurable_variation_labels +api-configurable-export-import-product,,Default,configurable,Default Category/CategoryExportImport,base,API Configurable Export Import Product,,,2,1,Taxable Goods,"Catalog, Search",123,,,,api-configurable-export-import-product,,,,/m/a/magento-logo_1.png,Magento Logo,/m/a/magento-logo_1.png,Magento Logo,/m/a/magento-logo_1.png,Magento Logo,,,"7/26/19, 8:21 AM","7/26/19, 8:21 AM",,,Block after Info Column,,,,,,,,,,,Use config,,,0,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,0,,,,,,,,,,,,,,,,,,"sku=api-simple-one-export-import,attribute=option1|sku=api-simple-two-export-import,attribute=option2",attribute=attributeExportImport diff --git a/dev/tests/acceptance/tests/_data/export_import_configurable_product_2.csv b/dev/tests/acceptance/tests/_data/export_import_configurable_product_2.csv new file mode 100644 index 000000000000..fc27af79fc16 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/export_import_configurable_product_2.csv @@ -0,0 +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_label,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,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,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,bundle_shipment_type,associated_skus,configurable_variations,configurable_variation_labels +api-configurable-export-import-product,,Default,configurable,Default Category/CategoryExportImport,base,API Configurable Export Import Product,,,2,1,Taxable Goods,"Catalog, Search",123,,,,api-configurable-export-import-product,,,,/m/a/magento-logo.png,Magento Logo,/m/a/magento-logo.png,Magento Logo,/m/a/magento-logo.png,Magento Logo,,,"7/26/19, 8:21 AM","7/26/19, 8:21 AM",,,Block after Info Column,,,,,,,,,,,Use config,,,0,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,0,,,,,,,,,,,,,,,,,,"sku=api-simple-one-export-import,attribute=option1|sku=api-simple-two-export-import,attribute=option2",attribute=attributeExportImport From e4068ad05ece76026b137cf96bb3273b179eedd9 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 12 Sep 2019 12:46:34 +0300 Subject: [PATCH 35/55] MC-6443: Product custom URL Key is preserved when assigned to a Category (without custom URL Key) alongside with another Product without custom URL Key --- .../Mftf/ActionGroup/AdminCategoryActionGroup.xml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 96575ee475a2..dc89d2fa5c04 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -405,13 +405,10 @@ <argument name="productSku" type="string"/> </arguments> - <conditionalClick selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" dependentSelector="{{CatalogProductsSection.resetFilter}}" visible="false" stepKey="clickOnProductInCategory"/> - <waitForPageLoad time="30" stepKey="waitForProductsInCategoryOpened"/> - <click selector="{{CatalogProductsSection.resetFilter}}" stepKey="clickOnResetFilter"/> - <waitForPageLoad time="30" stepKey="waitForProductsToLoad"/> - <fillField selector="{{AdminCategoryContentSection.productTableColumnSku}}" userInput="{{productSku}}" stepKey="selectProduct"/> - <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> - <waitForPageLoad time="30" stepKey="waitForSearch"/> + <conditionalClick selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="false" stepKey="clickOnProductInCategory"/> + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickOnResetFilter"/> + <fillField selector="{{AdminCategoryContentSection.productTableColumnSku}}" userInput="{{productSku}}" stepKey="fillSkuFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectProductFromTableRow"/> </actionGroup> </actionGroups> From 012d1aa865ab75fb961e43220e4b9cad8b5796fc Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 12 Sep 2019 13:38:22 +0300 Subject: [PATCH 36/55] MC-6358: Import products with error entries --- .../AdminImportProductsActionGroup.xml | 29 +++++++-- .../Mftf/Section/AdminImportMainSection.xml | 2 + .../AdminImportValidationMessagesSection.xml | 17 +++++ .../AdminCheckDoubleImportOfProductsTest.xml | 4 +- ...mportProductsWithAddUpdateBehaviorTest.xml | 4 +- ...dminImportProductsWithErrorEntriesTest.xml | 65 +++++++++++++++++++ ...nImportProductsWithReplaceBehaviorTest.xml | 2 +- .../tests/_data/catalog_product_err_img.csv | 11 ++++ 8 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportValidationMessagesSection.xml create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml create mode 100644 dev/tests/acceptance/tests/_data/catalog_product_err_img.csv diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml index 2ac790b953ec..3574e6612162 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml @@ -14,21 +14,36 @@ </annotations> <arguments> <argument name="behavior" type="string"/> + <argument name="validationStrategy" type="string" defaultValue="Stop on Error"/> + <argument name="allowedErrorsCount" type="string" defaultValue="10"/> <argument name="importFile" type="string"/> - <argument name="importMessage" type="string"/> + <argument name="importNoticeMessage" type="string"/> + <argument name="importMessageType" type="string" defaultValue="success"/> + <argument name="importMessage" type="string" defaultValue="Import successfully done"/> </arguments> <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> - <waitForPageLoad stepKey="AdminImportMainSectionLoad"/> + <waitForPageLoad stepKey="adminImportMainSectionLoad"/> <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> - <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="{{behavior}}" stepKey="selectImportOption"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="{{behavior}}" stepKey="selectImportBehaviorOption"/> + <selectOption selector="{{AdminImportMainSection.validationStrategy}}" userInput="{{validationStrategy}}" stepKey="selectValidationStrategyOption"/> + <fillField selector="{{AdminImportMainSection.allowedErrorsCount}}" userInput="{{allowedErrorsCount}}" stepKey="fillAllowedErrorsCountField"/> <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> - <waitForPageLoad stepKey="AdminImportMainSectionLoad2"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage"/> - <waitForPageLoad stepKey="AdminMessagesSection"/> - <see selector="{{AdminMessagesSection.notice}}" userInput="{{importMessage}}" stepKey="seeImportMessage"/> + <waitForElementVisible selector="{{AdminImportValidationMessagesSection.notice}}" stepKey="waitForNoticeMessage"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="{{importNoticeMessage}}" stepKey="seeNoticeMessage"/> + <see selector="{{AdminImportValidationMessagesSection.messageByType(importMessageType)}}" userInput="{{importMessage}}" stepKey="seeImportMessage"/> + </actionGroup> + + <actionGroup name="AdminImportProductsWithCheckDataActionGroup" extends="AdminImportProductsActionGroup"> + <arguments> + <argument name="validationNoticeMessage" type="string"/> + <argument name="validationMessage" type="string" defaultValue="File is valid! To start import process press "Import" button"/> + </arguments> + <waitForElementVisible selector="{{AdminImportValidationMessagesSection.notice}}" after="clickCheckDataButton" stepKey="waitForValidationNoticeMessage"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="{{validationNoticeMessage}}" after="waitForValidationNoticeMessage" stepKey="seeValidationNoticeMessage"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="{{validationMessage}}" after="seeValidationNoticeMessage" stepKey="seeValidationMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml index 2ce6b1e35777..d44b93bf05c9 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml @@ -13,5 +13,7 @@ <element name="importBehavior" type="select" selector="#basic_behavior"/> <element name="selectFileToImport" type="input" selector="#import_file"/> <element name="importButton" type="button" selector="#import_validation_container button" timeout="30"/> + <element name="validationStrategy" type="select" selector="#basic_behaviorvalidation_strategy"/> + <element name="allowedErrorsCount" type="input" selector="#basic_behavior_allowed_error_count"/> </section> </sections> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportValidationMessagesSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportValidationMessagesSection.xml new file mode 100644 index 000000000000..370d9546fa2f --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportValidationMessagesSection.xml @@ -0,0 +1,17 @@ +<?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="AdminImportValidationMessagesSection"> + <element name="notice" type="text" selector="#import_validation_messages .message-notice"/> + <element name="success" type="text" selector="#import_validation_messages .message-success"/> + <element name="messageByType" type="text" selector="#import_validation_messages .message-{{messageType}}" parameterized="true" /> + <element name="importErrorList" type="text" selector="#import_validation_messages .import-error-list"/> + </section> +</sections> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml index 0f2dde99b901..909c6101fe53 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml @@ -60,14 +60,14 @@ <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsFirstTime"> <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="prepared-for-sample-data.csv"/> - <argument name="importMessage" value="Created: 100, Updated: 3, Deleted: 0"/> + <argument name="importNoticeMessage" value="Created: 100, Updated: 3, Deleted: 0"/> </actionGroup> <!-- Import products with add/update behavior again --> <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsSecondTime"> <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="prepared-for-sample-data.csv"/> - <argument name="importMessage" value="Created: 0, Updated: 300, Deleted: 0"/> + <argument name="importNoticeMessage" value="Created: 0, Updated: 300, Deleted: 0"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index ceb4e93e4e9a..796732d57229 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -72,7 +72,7 @@ <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProducts"> <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="catalog_import_products.csv"/> - <argument name="importMessage" value="Created: 2, Updated: 1, Deleted: 0"/> + <argument name="importNoticeMessage" value="Created: 2, Updated: 1, Deleted: 0"/> </actionGroup> <!-- Assert Simple Product1 on grid--> @@ -109,7 +109,7 @@ <actionGroup ref="StoreFrontProductValidationActionGroup" stepKey="storeFrontSimpleProduct1Validation"> <argument name="product" value="SimpleProductAfterImport1"/> </actionGroup> - + <!-- Assert SimpleProduct2 on store front--> <actionGroup ref="StoreFrontProductValidationActionGroup" stepKey="storeFrontSimpleProduct2Validation"> <argument name="product" value="SimpleProductAfterImport2"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml new file mode 100644 index 000000000000..3ccd3b16842d --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.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="AdminImportProductsWithErrorEntriesTest"> + <annotations> + <features value="ImportExport"/> + <stories value="Import Products"/> + <title value="Import products with error entries"/> + <description value="Verify import status during import products with error entries"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6358"/> + <useCaseId value="MAGETWO-65066"/> + <group value="importExport"/> + </annotations> + <before> + <!--Login to Admin Page--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Clear products grid filters--> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductsGridFilters"/> + <!--Delete all imported products--> + <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> + <!--Logout from Admin page--> + <actionGroup ref="logout" stepKey="logoutFromAdminPage"/> + </after> + + <!--Import products with "Skip error entries"--> + <actionGroup ref="AdminImportProductsWithCheckDataActionGroup" stepKey="importProductsWithSkipErrorEntries"> + <argument name="behavior" value="Add/Update"/> + <argument name="validationStrategy" value="Skip error entries"/> + <argument name="importFile" value="catalog_product_err_img.csv"/> + <argument name="importNoticeMessage" value="Created: 10, Updated: 0, Deleted: 0"/> + <argument name="validationNoticeMessage" value="Checked rows: 10, checked entities: 10, invalid rows: 0, total errors: 0"/> + </actionGroup> + <see selector="{{AdminImportValidationMessagesSection.importErrorList}}" userInput="row(s): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10" stepKey="seeTenImportError"/> + + <!--Import products with "Stop on Error" and "Allowed Errors Count" equals 5--> + <actionGroup ref="AdminImportProductsWithCheckDataActionGroup" stepKey="importProductsWithAllowedErrorsCountFive"> + <argument name="behavior" value="Add/Update"/> + <argument name="allowedErrorsCount" value="5"/> + <argument name="importFile" value="catalog_product_err_img.csv"/> + <argument name="importNoticeMessage" value="Following Error(s) has been occurred during importing process"/> + <argument name="importMessageType" value="error"/> + <argument name="importMessage" value="Maximum error count has been reached or system error is occurred!"/> + <argument name="validationNoticeMessage" value="Checked rows: 10, checked entities: 10, invalid rows: 0, total errors: 0"/> + </actionGroup> + <see selector="{{AdminImportValidationMessagesSection.importErrorList}}" userInput="row(s): 1, 2, 3, 4, 5, 6" stepKey="seeAboutFiveImportError"/> + + <!--Import products with "Stop on Error" and "Allowed Errors Count" equals 11--> + <actionGroup ref="AdminImportProductsWithCheckDataActionGroup" stepKey="importProductsWithAllowedErrorsCountEleven"> + <argument name="behavior" value="Add/Update"/> + <argument name="allowedErrorsCount" value="11"/> + <argument name="importFile" value="catalog_product_err_img.csv"/> + <argument name="importNoticeMessage" value="Created: 0, Updated: 10, Deleted: 0"/> + <argument name="validationNoticeMessage" value="Checked rows: 10, checked entities: 10, invalid rows: 0, total errors: 0"/> + </actionGroup> + <see selector="{{AdminImportValidationMessagesSection.importErrorList}}" userInput="row(s): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10" stepKey="seeAboutTenImportError"/> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml index d63a5546716b..dc4ede1978de 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml @@ -39,7 +39,7 @@ <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProducts"> <argument name="behavior" value="Replace"/> <argument name="importFile" value="catalog_import_products.csv"/> - <argument name="importMessage" value="Created: 3, Updated: 0, Deleted: 3"/> + <argument name="importNoticeMessage" value="Created: 3, Updated: 0, Deleted: 3"/> </actionGroup> </test> </tests> diff --git a/dev/tests/acceptance/tests/_data/catalog_product_err_img.csv b/dev/tests/acceptance/tests/_data/catalog_product_err_img.csv new file mode 100644 index 000000000000..97ac55e8e5a2 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/catalog_product_err_img.csv @@ -0,0 +1,11 @@ +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,additional_images +simple1,,Default,simple,,base,simple1,,,,1,Taxable Goods,"Catalog, Search",100,,,,simple1,test.jpg +simple2,,Default,simple,,base,simple2,,,,2,Taxable Goods,"Catalog, Search",101,,,,simple2,test.jpg +simple3,,Default,simple,,base,simple3,,,,3,Taxable Goods,"Catalog, Search",102,,,,simple3,test.jpg +simple4,,Default,simple,,base,simple4,,,,4,Taxable Goods,"Catalog, Search",103,,,,simple4,test.jpg +simple5,,Default,simple,,base,simple5,,,,5,Taxable Goods,"Catalog, Search",104,,,,simple5,test.jpg +simple6,,Default,simple,,base,simple6,,,,6,Taxable Goods,"Catalog, Search",105,,,,simple6,test.jpg +simple7,,Default,simple,,base,simple7,,,,7,Taxable Goods,"Catalog, Search",106,,,,simple7,test.jpg +simple8,,Default,simple,,base,simple8,,,,8,Taxable Goods,"Catalog, Search",107,,,,simple8,test.jpg +simple9,,Default,simple,,base,simple9,,,,9,Taxable Goods,"Catalog, Search",108,,,,simple9,test.jpg +simple10,,Default,simple,,base,simple10,,,,10,Taxable Goods,"Catalog, Search",109,,,,simple10,test.jpg From 248c682adbaf707fe89694214c7037cd6d8abf4a Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 12 Sep 2019 18:11:32 +0300 Subject: [PATCH 37/55] MC-6358: Import products with error entries --- .../Mftf/ActionGroup/AdminImportProductsActionGroup.xml | 2 +- .../ImportExport/Test/Mftf/Page/AdminImportIndexPage.xml | 1 + .../Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml index 3574e6612162..6bcca6c86c98 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml @@ -37,7 +37,7 @@ <see selector="{{AdminImportValidationMessagesSection.messageByType(importMessageType)}}" userInput="{{importMessage}}" stepKey="seeImportMessage"/> </actionGroup> - <actionGroup name="AdminImportProductsWithCheckDataActionGroup" extends="AdminImportProductsActionGroup"> + <actionGroup name="AdminImportProductsWithCheckValidationResultActionGroup" extends="AdminImportProductsActionGroup"> <arguments> <argument name="validationNoticeMessage" type="string"/> <argument name="validationMessage" type="string" defaultValue="File is valid! To start import process press "Import" button"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Page/AdminImportIndexPage.xml b/app/code/Magento/ImportExport/Test/Mftf/Page/AdminImportIndexPage.xml index 87807eb9b0e8..626293117959 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Page/AdminImportIndexPage.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Page/AdminImportIndexPage.xml @@ -11,5 +11,6 @@ <page name="AdminImportIndexPage" url="admin/import/" area="admin" module="Magento_ImportExport"> <section name="AdminImportHeaderSection"/> <section name="AdminImportMainSection"/> + <section name="AdminImportValidationMessagesSection"/> </page> </pages> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml index 3ccd3b16842d..94840a4ea614 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml @@ -31,7 +31,7 @@ </after> <!--Import products with "Skip error entries"--> - <actionGroup ref="AdminImportProductsWithCheckDataActionGroup" stepKey="importProductsWithSkipErrorEntries"> + <actionGroup ref="AdminImportProductsWithCheckValidationResultActionGroup" stepKey="importProductsWithSkipErrorEntries"> <argument name="behavior" value="Add/Update"/> <argument name="validationStrategy" value="Skip error entries"/> <argument name="importFile" value="catalog_product_err_img.csv"/> @@ -41,7 +41,7 @@ <see selector="{{AdminImportValidationMessagesSection.importErrorList}}" userInput="row(s): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10" stepKey="seeTenImportError"/> <!--Import products with "Stop on Error" and "Allowed Errors Count" equals 5--> - <actionGroup ref="AdminImportProductsWithCheckDataActionGroup" stepKey="importProductsWithAllowedErrorsCountFive"> + <actionGroup ref="AdminImportProductsWithCheckValidationResultActionGroup" stepKey="importProductsWithAllowedErrorsCountFive"> <argument name="behavior" value="Add/Update"/> <argument name="allowedErrorsCount" value="5"/> <argument name="importFile" value="catalog_product_err_img.csv"/> @@ -53,7 +53,7 @@ <see selector="{{AdminImportValidationMessagesSection.importErrorList}}" userInput="row(s): 1, 2, 3, 4, 5, 6" stepKey="seeAboutFiveImportError"/> <!--Import products with "Stop on Error" and "Allowed Errors Count" equals 11--> - <actionGroup ref="AdminImportProductsWithCheckDataActionGroup" stepKey="importProductsWithAllowedErrorsCountEleven"> + <actionGroup ref="AdminImportProductsWithCheckValidationResultActionGroup" stepKey="importProductsWithAllowedErrorsCountEleven"> <argument name="behavior" value="Add/Update"/> <argument name="allowedErrorsCount" value="11"/> <argument name="importFile" value="catalog_product_err_img.csv"/> From 8afbd8c97725b585f81a51764ac7e6cf43093d8d Mon Sep 17 00:00:00 2001 From: Yevhen Miroshnychenko <ymiroshnychenko@magento.com> Date: Thu, 12 Sep 2019 13:49:38 -0500 Subject: [PATCH 38/55] MC-19421: Reduce q-ty of Reports Created in /app/*/var/report --- pub/errors/processor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pub/errors/processor.php b/pub/errors/processor.php index 63d054319fb0..108ddf4e03cb 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -629,7 +629,7 @@ private function getReportDirNestingLevel(string $reportId): int */ private function getMaxReportDirNestingLevel(string $reportId): int { - return (integer)floor(mb_strlen($reportId) / self::NUMBER_SYMBOLS_IN_SUBDIR_NAME); + return (int)floor(strlen($reportId) / self::NUMBER_SYMBOLS_IN_SUBDIR_NAME); } /** From b8f70d2e7e58484f58d65beed8a73d9b9ccbfcee Mon Sep 17 00:00:00 2001 From: Yevhen Miroshnychenko <ymiroshnychenko@magento.com> Date: Thu, 12 Sep 2019 16:12:30 -0500 Subject: [PATCH 39/55] MC-19421: Reduce q-ty of Reports Created in /app/*/var/report --- pub/errors/processor.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pub/errors/processor.php b/pub/errors/processor.php index a0410ce60c34..889eed2cad10 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -507,10 +507,6 @@ public function saveReport(array $reportData): string } $this->_setReportData($reportData); - if (!file_exists($this->_reportDir)) { - @mkdir($this->_reportDir, 0777, true); - } - @file_put_contents($this->_reportFile, $this->serializer->serialize($reportData). PHP_EOL); if (isset($reportData['skin']) && self::DEFAULT_SKIN != $reportData['skin']) { From 7a199d40ee8086b604153ef2b4d44b9591d12866 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 13 Sep 2019 11:41:54 +0300 Subject: [PATCH 40/55] MC-11329: Storefront Navigation Menu UI, desktop --- .../ActionGroup/AdminCategoryActionGroup.xml | 25 ++ .../Catalog/Test/Mftf/Data/CategoryData.xml | 3 + ...rontCatalogNavigationMenuUIDesktopTest.xml | 335 ++++++++++++++++++ .../AdminChangeMagentoThemeActionGroup.xml | 23 ++ ...StorefrontCheckElementColorActionGroup.xml | 24 ++ .../Theme/Test/Mftf/Data/DesignData.xml | 6 + .../Mftf/Data/NavigationMenuColorData.xml | 16 + .../Mftf/Section/AdminDesignConfigSection.xml | 1 + .../StorefrontNavigationMenuSection.xml | 21 ++ 9 files changed, 454 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml create mode 100644 app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml create mode 100644 app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml create mode 100644 app/code/Magento/Theme/Test/Mftf/Data/NavigationMenuColorData.xml create mode 100644 app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 12bf5179a07d..419e3778f212 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -396,4 +396,29 @@ <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> </actionGroup> + + <actionGroup name="DeleteDefaultCategoryChildren"> + <annotations> + <description>Deletes all children categories of Default Root Category.</description> + </annotations> + + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategoryPage"/> + <executeInSelenium function="function ($webdriver) use ($I) { + $children = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., + \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a')); + while (!empty($children)) { + $I->click('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., + \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a'); + $I->waitForPageLoad(30); + $I->click('#delete'); + $I->waitForElementVisible('aside.confirm .modal-footer button.action-accept'); + $I->click('aside.confirm .modal-footer button.action-accept'); + $I->waitForPageLoad(30); + $I->waitForElementVisible('#messages div.message-success', 30); + $I->see('You deleted the category.', '#messages div.message-success'); + $children = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., + \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a')); + } + }" stepKey="deleteAllChildCategories"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml index 13951a0d197d..336cf2d41ef0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -117,4 +117,7 @@ <data key="is_active">true</data> <data key="include_in_menu">true</data> </entity> + <entity name="DefaultCategory" type="category"> + <data key="name">Default Category</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml new file mode 100644 index 000000000000..4df0e151aeab --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml @@ -0,0 +1,335 @@ +<?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="StorefrontCatalogNavigationMenuUIDesktopTest"> + <annotations> + <features value="Catalog"/> + <stories value="Storefront Catalog Navigation Menu UI"/> + <title value="Storefront Catalog Navigation Menu UI, desktop"/> + <description value="Verify UI of Navigation Menu functionality on Storefront"/> + <testCaseId value="MC-11329"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteDefaultCategoryChildren" stepKey="deleteRootCategoryChildren"/> + </before> + <after> + <actionGroup ref="DeleteDefaultCategoryChildren" stepKey="deleteRootCategoryChildren"/> + <actionGroup ref="AdminChangeMagentoThemeActionGroup" stepKey="changeThemeToDefault"> + <argument name="theme" value="{{MagentoLumaTheme.name}}"/> + </actionGroup> + <!-- Admin log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to Content > Themes. Change theme to Blank --> + <actionGroup ref="AdminChangeMagentoThemeActionGroup" stepKey="changeThemeToBlank"> + <argument name="theme" value="{{MagentoBlankTheme.name}}"/> + </actionGroup> + + <!-- Open storefront --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefrontPage"/> + + <!-- Assert no category - no menu --> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.navigationMenu}}" stepKey="dontSeeMenu"/> + + <!-- Assert single row - no hover state --> + <createData entity="ApiCategory" stepKey="createFirstCategoryBlank"> + <field key="name">Category A</field> + </createData> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitForBlankSingleRowAppear"/> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createFirstCategoryBlank.name$$)}}" stepKey="hoverFirstCategoryBlank"/> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.subItemLevelHover('level0')}}" stepKey="assertNoHoverState"/> + + <!-- Create categories --> + <createData entity="ApiCategory" stepKey="createSecondCategoryBlank"> + <field key="name">TEST</field> + </createData> + <createData entity="ApiCategory" stepKey="createThirdCategoryBlank"> + <field key="name">_test2</field> + </createData> + <createData entity="ApiCategory" stepKey="createFourthCategoryBlank"> + <field key="name">test 3</field> + </createData> + <createData entity="ApiCategory" stepKey="createFifthCategoryBlank"> + <field key="name">Category with several products</field> + </createData> + <createData entity="ApiCategory" stepKey="createSixthCategoryBlank"> + <field key="name">test 5</field> + </createData> + <createData entity="ApiCategory" stepKey="createSeventhCategoryBlank"> + <field key="name">test 8</field> + </createData> + <createData entity="ApiCategory" stepKey="createEighthCategoryBlank"> + <field key="name">This is a very very very very very looong title</field> + </createData> + <createData entity="ApiCategory" stepKey="createNinthCategoryBlank"> + <field key="name">test 6</field> + </createData> + <createData entity="ApiCategory" stepKey="createTenthCategoryBlank"> + <field key="name">test 7</field> + </createData> + <createData entity="ApiCategory" stepKey="createEleventhCategoryBlank"> + <field key="name">test 4</field> + </createData> + <createData entity="ApiCategory" stepKey="createTwelfthCategoryBlank"> + <field key="name">Category with image</field> + </createData> + <createData entity="ApiCategory" stepKey="createThirteenthCategoryBlank"> + <field key="name">test 0</field> + </createData> + <createData entity="ApiCategory" stepKey="createCategoryWithoutChildrenBlank"> + <field key="name">Category with description & custom title</field> + </createData> + <createData entity="ApiCategory" stepKey="createCategoryWithChildrenBlank"> + <field key="name">Category with children</field> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createFirstCategoryLevelOneBlank"> + <field key="name">level 1 test category very very very long name</field> + <requiredEntity createDataKey="createCategoryWithChildrenBlank"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createSecondCategoryLevelOneBlank"> + <field key="name">level 1 test category name</field> + <requiredEntity createDataKey="createCategoryWithChildrenBlank"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createThirdCategoryLevelOneBlank"> + <field key="name">level 1 with children</field> + <requiredEntity createDataKey="createCategoryWithChildrenBlank"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createCategoryLevelTwoBlank"> + <field key="name">level 2 with children</field> + <requiredEntity createDataKey="createThirdCategoryLevelOneBlank"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createCategoryLevelThreeBlank"> + <field key="name">level 3 test</field> + <requiredEntity createDataKey="createCategoryLevelTwoBlank"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createFirstCategoryLevelFourBlank"> + <field key="name">level 4</field> + <requiredEntity createDataKey="createCategoryLevelThreeBlank"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createSecondCategoryLevelFourBlank"> + <field key="name">level 4 test</field> + <requiredEntity createDataKey="createCategoryLevelThreeBlank"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createCategoryLevelFiveBlank"> + <field key="name">level 5</field> + <requiredEntity createDataKey="createSecondCategoryLevelFourBlank"/> + </createData> + + <!-- Several rows. Hover on category without children --> + <reloadPage stepKey="reloadPage"/> + <waitForPageLoad stepKey="waitForBlankSeveralRowsAppear"/> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryWithoutChildrenBlank.name$$)}}" stepKey="hoverCategoryWithoutChildren"/> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.itemByNameAndLevel($$createCategoryWithoutChildrenBlank.name$$, 'level0')}}" stepKey="dontSeeChildrenInCategory"/> + + <!-- Nested level 1. No hover state --> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryWithChildrenBlank.name$$)}}" stepKey="hoverCategoryWithChildrenTopLevel"/> + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkNoHoverState"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemByLevel('level0')}}"/> + <argument name="property" value="background-color"/> + <argument name="color" value="{{NavigationMenuColor.white}}"/> + </actionGroup> + + <!-- Nested level 1. Hover state on 1st item --> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createFirstCategoryLevelOneBlank.name$$)}}" stepKey="hoverCategoryLevelOneFirstItem"/> + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkHighlightedAfterHoverFirstItem"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemLevelHover('level0')}}"/> + <argument name="property" value="background-color"/> + <argument name="color" value="{{NavigationMenuColor.gray}}"/> + </actionGroup> + + <!-- Nested level 1 & 2. Hover state on the last item --> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createThirdCategoryLevelOneBlank.name$$)}}" stepKey="hoverCategoryLevelOneLastItem"/> + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkHighlightedAfterHoverLastItem"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemLevelHover('level0')}}"/> + <argument name="property" value="background-color"/> + <argument name="color" value="{{NavigationMenuColor.gray}}"/> + </actionGroup> + + <!-- Submenu appears rightward --> + <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level0')}}" stepKey="assertTopLevelMenuLeftDirection"/> + + <!-- Nested level 1 & 5 --> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryLevelTwoBlank.name$$)}}" stepKey="hoverCategoryLevelTwo"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenuLeftDirection('level1')}}" stepKey="seeLevelOneMenuLeftDirection"/> + + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryLevelThreeBlank.name$$)}}" stepKey="hoverCategoryLevelThree"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenuLeftDirection('level2')}}" stepKey="seeLevelTwoMenuRightDirection"/> + + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSecondCategoryLevelFourBlank.name$$)}}" stepKey="hoverCategoryLevelFour"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level3')}}" stepKey="seeLevelThreeMenuRightDirection"/> + + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkSubcategoryHighlighted"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemLevelHover('level3')}}"/> + <argument name="property" value="background-color"/> + <argument name="color" value="{{NavigationMenuColor.gray}}"/> + </actionGroup> + + <!-- Delete all creation for Blank theme --> + <deleteData createDataKey="createFirstCategoryBlank" stepKey="deleteFirstCategoryBlank"/> + <deleteData createDataKey="createSecondCategoryBlank" stepKey="deleteSecondCategoryBlank"/> + <deleteData createDataKey="createThirdCategoryBlank" stepKey="deleteThirdCategoryBlank"/> + <deleteData createDataKey="createFourthCategoryBlank" stepKey="deleteFourthCategoryBlank"/> + <deleteData createDataKey="createFifthCategoryBlank" stepKey="deleteFifthCategoryBlank"/> + <deleteData createDataKey="createSixthCategoryBlank" stepKey="deleteSixthCategoryBlank"/> + <deleteData createDataKey="createSeventhCategoryBlank" stepKey="deleteSeventhCategoryBlank"/> + <deleteData createDataKey="createEighthCategoryBlank" stepKey="deleteEighthCategoryBlank"/> + <deleteData createDataKey="createNinthCategoryBlank" stepKey="deleteNinthCategoryBlank"/> + <deleteData createDataKey="createTenthCategoryBlank" stepKey="deleteTenthCategoryBlank"/> + <deleteData createDataKey="createEleventhCategoryBlank" stepKey="deleteEleventhCategoryBlank"/> + <deleteData createDataKey="createTwelfthCategoryBlank" stepKey="deleteTwelfthCategoryBlank"/> + <deleteData createDataKey="createThirteenthCategoryBlank" stepKey="deleteThirteenthCategoryBlank"/> + <deleteData createDataKey="createCategoryWithChildrenBlank" stepKey="deleteCategoryWithChildrenBlank"/> + <deleteData createDataKey="createCategoryWithoutChildrenBlank" stepKey="deleteCategoryWithoutChildrenBlank"/> + + <!-- Go to Content > Themes. Change theme to Luma --> + <actionGroup ref="AdminChangeMagentoThemeActionGroup" stepKey="changeThemeToLuma"> + <argument name="theme" value="{{MagentoLumaTheme.name}}"/> + </actionGroup> + + <!-- Open storefront --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefront"/> + + <!-- Assert no category - no menu --> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.navigationMenu}}" stepKey="dontSeeMenuOnStorefront"/> + + <!-- Create categories --> + <createData entity="ApiCategory" stepKey="createFirstCategoryLuma"/> + <createData entity="ApiCategory" stepKey="createSecondCategoryLuma"/> + <createData entity="ApiCategory" stepKey="createThirdCategoryLuma"/> + <createData entity="ApiCategory" stepKey="createFourthCategoryLuma"/> + + <!-- Single row. No hover state --> + <reloadPage stepKey="reload"/> + <waitForPageLoad stepKey="waitForLumaSingleRowAppear"/> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.itemByNameAndLevel($$createFirstCategoryLuma.name$$, 'level0')}}" stepKey="noHoverStateInFirstCategory"/> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.itemByNameAndLevel($$createSecondCategoryLuma.name$$, 'level0')}}" stepKey="noHoverStateInSecondCategory"/> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.itemByNameAndLevel($$createThirdCategoryLuma.name$$, 'level0')}}" stepKey="noHoverStateThirdCategory"/> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.itemByNameAndLevel($$createFourthCategoryLuma.name$$, 'level0')}}" stepKey="noHoverStateInFourthCategory"/> + + <!-- Create categories for testing Luma theme --> + <createData entity="ApiCategory" stepKey="createFifthCategoryLuma"/> + <createData entity="ApiCategory" stepKey="createCategoryWithChildrenLuma"/> + <createData entity="SubCategoryWithParent" stepKey="createFirstCategoryLevelOneLuma"> + <requiredEntity createDataKey="createCategoryWithChildrenLuma"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createSecondCategoryLevelOneLuma"> + <requiredEntity createDataKey="createCategoryWithChildrenLuma"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createThirdCategoryLevelOneLuma"> + <requiredEntity createDataKey="createCategoryWithChildrenLuma"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createFirstCategoryLevelTwoLuma"> + <requiredEntity createDataKey="createThirdCategoryLevelOneLuma"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createSecondCategoryLevelTwoLuma"> + <requiredEntity createDataKey="createThirdCategoryLevelOneLuma"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createCategoryLevelThreeLuma"> + <requiredEntity createDataKey="createSecondCategoryLevelTwoLuma"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="createCategoryLevelFourLuma"> + <requiredEntity createDataKey="createCategoryLevelThreeLuma"/> + </createData> + <createData entity="ApiCategory" stepKey="createSixthCategoryLuma"/> + <createData entity="ApiCategory" stepKey="createSeventhCategoryLuma"/> + <createData entity="ApiCategory" stepKey="createEighthCategoryLuma"/> + + <!-- Several rows. Hover on Category without children --> + <reloadPage stepKey="refresh"/> + <waitForPageLoad stepKey="waitForLumaSeveralRowsAppear"/> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createFifthCategoryLuma.name$$)}}" stepKey="hoverOnCategoryWithoutChildren"/> + <dontSeeElement selector="{{StorefrontNavigationMenuSection.itemByNameAndLevel($$createFifthCategoryLuma.name$$, 'level0')}}" stepKey="dontSeeSubcategoriesInCategory"/> + + <!-- Nested level 1. No hover state --> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryWithChildrenLuma.name$$)}}" stepKey="hoverOnCategoryWithChildren"/> + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkNoHighlightedInSubmenuAfterHover"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemByLevel('level0')}}"/> + <argument name="property" value="background-color"/> + <argument name="color" value="{{NavigationMenuColor.white}}"/> + </actionGroup> + + <!-- Nested level 1. Hover state on first item --> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createFirstCategoryLevelOneLuma.name$$)}}" stepKey="hoverOnFirstItemLevelOne"/> + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkHighlightedAfterHoverOnFirstItem"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemLevelHover('level0')}}"/> + <argument name="property" value="background-color"/> + <argument name="color" value="{{NavigationMenuColor.gray}}"/> + </actionGroup> + + <!-- Nested levels 1 & 2. Hover state on last item --> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createThirdCategoryLevelOneLuma.name$$)}}" stepKey="hoverOnLastItemLevelOne"/> + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkHighlightedAfterHoverOnLastItem"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemLevelHover('level0')}}"/> + <argument name="property" value="background-color"/> + <argument name="color" value="{{NavigationMenuColor.gray}}"/> + </actionGroup> + + <!-- Submenu appears rightward --> + <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level0')}}" stepKey="seeTopLevelRightDirection"/> + + <!-- Nested levels 1 & 5 --> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createSecondCategoryLevelTwoLuma.name$$)}}" stepKey="hoverThirdCategoryLevelTwo"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level1')}}" stepKey="seeFirstLevelRightDirection"/> + + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryLevelThreeLuma.name$$)}}" stepKey="hoverOnCategoryLevelThree"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level2')}}" stepKey="seeSecondLevelRightDirection"/> + + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryLevelFourLuma.name$$)}}" stepKey="hoverOnCategoryLevelFour"/> + <seeElement selector="{{StorefrontNavigationMenuSection.submenuRightDirection('level3')}}" stepKey="seeThirdLevelRightDirection"/> + + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkSubcategoryHighlightedAfterHover"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemLevelHover('level3')}}"/> + <argument name="property" value="background-color"/> + <argument name="color" value="{{NavigationMenuColor.gray}}"/> + </actionGroup> + + <!-- Selected 1st level category --> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryWithChildrenLuma.name$$)}}" stepKey="openTopLevelCategory"/> + <waitForPageLoad stepKey="waitForCategoryPageLoaded"/> + + <!-- Assert category active state --> + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkCategoryActiveState"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.itemActiveState}}"/> + <argument name="property" value="border-color"/> + <argument name="color" value="{{NavigationMenuColor.orange}}"/> + </actionGroup> + + <!-- Selected subcategory. Assert active state --> + <actionGroup ref="StorefrontGoToSubCategoryPageActionGroup" stepKey="openSubcategory"> + <argument name="categoryName" value="$$createCategoryWithChildrenLuma.name$$"/> + <argument name="subCategoryName" value="$$createThirdCategoryLevelOneLuma.name$$"/> + </actionGroup> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategoryWithChildrenLuma.name$$)}}" stepKey="hoverOnCategory"/> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createThirdCategoryLevelOneLuma.name$$)}}" stepKey="hoverOnSubcategory"/> + + <!-- Assert subcategory active state --> + <actionGroup ref="StorefrontCheckElementColorActionGroup" stepKey="checkSubitemActiveState"> + <argument name="selector" value="{{StorefrontNavigationMenuSection.subItemActiveState}}"/> + <argument name="property" value="border-color"/> + <argument name="color" value="{{NavigationMenuColor.orange}}"/> + </actionGroup> + + <!-- Delete created category --> + <deleteData createDataKey="createFirstCategoryLuma" stepKey="deleteFirstCategoryLuma"/> + <deleteData createDataKey="createSecondCategoryLuma" stepKey="deleteSecondCategoryLuma"/> + <deleteData createDataKey="createThirdCategoryLuma" stepKey="deleteThirdCategoryLuma"/> + <deleteData createDataKey="createFourthCategoryLuma" stepKey="deleteFourthCategoryLuma"/> + <deleteData createDataKey="createFifthCategoryLuma" stepKey="deleteFifthCategoryLuma"/> + <deleteData createDataKey="createSixthCategoryLuma" stepKey="deleteSixthCategoryLuma"/> + <deleteData createDataKey="createSeventhCategoryLuma" stepKey="deleteSeventhCategoryLuma"/> + <deleteData createDataKey="createEighthCategoryLuma" stepKey="deleteEighthCategoryLuma"/> + <deleteData createDataKey="createCategoryWithChildrenLuma" stepKey="deleteCategoryWithChildrenLuma"/> + </test> +</tests> diff --git a/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml b/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml new file mode 100644 index 000000000000..0b093a1e0e8d --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml @@ -0,0 +1,23 @@ +<?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="AdminChangeMagentoThemeActionGroup"> + <arguments> + <argument name="theme" type="string"/> + </arguments> + <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage"/> + <click selector="{{AdminDesignConfigSection.scopeRow('3')}}" stepKey="editStoreView"/> + <waitForPageLoad stepKey="waitForOpen"/> + <selectOption selector="{{AdminDesignConfigSection.appliedTheme}}" userInput="{{theme}}" stepKey="selectTheme"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml b/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.xml new file mode 100644 index 000000000000..66e98d5e4152 --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/ActionGroup/StorefrontCheckElementColorActionGroup.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="StorefrontCheckElementColorActionGroup"> + <annotations> + <description>Checks element color on storefront.</description> + </annotations> + <arguments> + <argument name="selector" type="string"/> + <argument name="property" type="string"/> + <argument name="color" type="string"/> + </arguments> + + <executeJS function="return window.getComputedStyle(document.querySelector('{{selector}}')).getPropertyValue('{{property}}')" stepKey="getElementColor"/> + <assertEquals expected="{{color}}" expectedType="string" actualType="variable" actual="getElementColor" message="pass" stepKey="assertElementColor"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml b/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml index ec28e8ed7a99..1a3c10745f5a 100644 --- a/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml +++ b/app/code/Magento/Theme/Test/Mftf/Data/DesignData.xml @@ -11,4 +11,10 @@ <entity name="Layout" type="page_layout"> <data key="1column">1 column</data> </entity> + <entity name="MagentoBlankTheme" type="theme"> + <data key="name">Magento Blank</data> + </entity> + <entity name="MagentoLumaTheme" type="theme"> + <data key="name">Magento Luma</data> + </entity> </entities> diff --git a/app/code/Magento/Theme/Test/Mftf/Data/NavigationMenuColorData.xml b/app/code/Magento/Theme/Test/Mftf/Data/NavigationMenuColorData.xml new file mode 100644 index 000000000000..7af07753d7c9 --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/Data/NavigationMenuColorData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="NavigationMenuColor" type="navigation_menu_color"> + <data key="gray">rgb(232, 232, 232)</data> + <data key="white">rgb(255, 255, 255)</data> + <data key="orange">rgb(255, 85, 1)</data> + </entity> +</entities> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index c2652f33f760..8004d6d6c344 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -31,5 +31,6 @@ <element name="storesArrow" type="button" selector="#ZmF2aWNvbi9zdG9yZXM- > .jstree-icon" /> <element name="checkIfStoresArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbi9zdG9yZXM-' and contains(@class,'jstree-closed')]" /> <element name="storeLink" type="button" selector="#ZmF2aWNvbi9zdG9yZXMvMQ-- > a"/> + <element name="appliedTheme" type="select" selector="select[name='theme_theme_id']"/> </section> </sections> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml new file mode 100644 index 000000000000..fd4b0ce45363 --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml @@ -0,0 +1,21 @@ +<?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="StorefrontNavigationMenuSection"> + <element name="navigationMenu" type="block" selector=".section-items.nav-sections-items li"/> + <element name="subItemLevelHover" type="text" selector=".{{level}} .submenu a:hover" parameterized="true"/> + <element name="itemByNameAndLevel" type="text" selector="//a[span[contains(., '{{categoryName}}')]]/following-sibling::ul[contains(@class,'{{categoryLevel}}')]" parameterized="true"/> + <element name="subItemByLevel" type="text" selector="li.{{itemLevel}}.parent ul.{{itemLevel}}" parameterized="true"/> + <element name="itemActiveState" type="text" selector=".navigation .level0.active>.level-top"/> + <element name="subItemActiveState" type="text" selector=".navigation .level0 .submenu .active>a"/> + <element name="submenuLeftDirection" type="text" selector="ul.{{itemLevel}}.submenu-reverse" parameterized="true"/> + <element name="submenuRightDirection" type="text" selector="ul.{{itemLevel}}:not(.submenu-reverse)" parameterized="true"/> + </section> +</sections> From d1c4f9c0da86ee059d2e14c94cc776cb2346d096 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 13 Sep 2019 13:34:22 +0300 Subject: [PATCH 41/55] MC-11386: Verify Category Product and Product Category partial reindex --- .../Catalog/Test/Mftf/Data/CategoryData.xml | 3 + .../Test/Mftf/Data/CustomAttributeData.xml | 4 + ...ctAndProductCategoryPartialReindexTest.xml | 226 ++++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml index 13951a0d197d..05a6bae9ab0f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -117,4 +117,7 @@ <data key="is_active">true</data> <data key="include_in_menu">true</data> </entity> + <entity name="SubCategoryNonAnchor" extends="SubCategoryWithParent"> + <requiredEntity type="custom_attribute">CustomAttributeCategoryIsAnchor</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml index 1684bd0c8a2c..69d8c3c87ae5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml @@ -51,4 +51,8 @@ <data key="attribute_code">short_description</data> <data key="value">Short Fixedtest 555</data> </entity> + <entity name="CustomAttributeCategoryIsAnchor" type="custom_attribute"> + <data key="attribute_code">is_anchor</data> + <data key="value">0</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml new file mode 100644 index 000000000000..6184a220f047 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml @@ -0,0 +1,226 @@ +<?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="VerifyCategoryProductAndProductCategoryPartialReindexTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Categories Indexer"/> + <title value="Verify Category Product and Product Category partial reindex"/> + <description value="Verify that Merchant Developer can use console commands to perform partial reindex for Category Products, Product Categories, and Catalog Search"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-11386"/> + <group value="catalog"/> + <group value="indexer"/> + </annotations> + <before> + <!-- Change "Category Products" and "Product Categories" indexers to "Update by Schedule" mode --> + <magentoCLI command="indexer:set-mode" arguments="schedule catalog_category_product catalog_product_category" stepKey="setIndexerMode"/> + + <!-- Create categories K, L, M, N with different nesting in the tree and Anchor = Yes/No--> + <!-- Category K is an anchor category --> + <createData entity="_defaultCategory" stepKey="categoryK"/> + <!-- Category L is a non-anchor subcategory of category K --> + <createData entity="SubCategoryNonAnchor" stepKey="categoryL"> + <requiredEntity createDataKey="categoryK"/> + </createData> + <!-- Category M is a subcategory of category L --> + <createData entity="SubCategoryWithParent" stepKey="categoryM"> + <requiredEntity createDataKey="categoryL"/> + </createData> + <!-- Category N is a subcategory of category K --> + <createData entity="SubCategoryWithParent" stepKey="categoryN"> + <requiredEntity createDataKey="categoryK"/> + </createData> + + <!-- Create different Products with different settings, assign to categories: --> + <!-- Product A in 0 categories, i.e. not assigned to any category --> + <createData entity="SimpleProduct2" stepKey="productA"/> + <!-- Product B in 1 category M --> + <createData entity="SimpleProduct3" stepKey="productB"> + <requiredEntity createDataKey="categoryM"/> + </createData> + <!-- Product C in 2 categories M and N --> + <createData entity="SimpleProduct2" stepKey="productC"/> + + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignCategoryNAndMToProductC"> + <argument name="productId" value="$$productC.id$$"/> + <argument name="categoryName" value="$$categoryN.name$$, $$categoryM.name$$"/> + </actionGroup> + + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <!-- Change "Category Products" and "Product Categories" indexers to "Update on Save" mode --> + <magentoCLI command="indexer:set-mode" arguments="realtime" stepKey="setRealtimeMode"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + + <!-- Delete data --> + <deleteData createDataKey="productA" stepKey="deleteProductA"/> + <deleteData createDataKey="productB" stepKey="deleteProductB"/> + <deleteData createDataKey="productC" stepKey="deleteProductC"/> + <deleteData createDataKey="categoryN" stepKey="deleteCategoryN"/> + <deleteData createDataKey="categoryM" stepKey="deleteCategoryM"/> + <deleteData createDataKey="categoryL" stepKey="deleteCategoryL"/> + <deleteData createDataKey="categoryK" stepKey="deleteCategoryK"/> + + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Open categories K, L, M, N on Storefront --> + <!-- Category K contains only Products B & C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$)}}" stepKey="onCategoryK"/> + <see userInput="$$productB.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductBOnCategoryK"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductCOnCategoryK"/> + + <!-- Category L contains no Products --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$)}}" stepKey="onCategoryL"/> + <see userInput="We can't find products matching the selection." selector="{{StorefrontCategoryMainSection.emptyProductMessage}}" stepKey="seeMessage"/> + <dontSeeElement selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProducts"/> + + <!-- Category M contains only Products B & C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$/$$categoryM.custom_attributes[url_key]$$)}}" stepKey="onCategoryM"/> + <see userInput="$$productB.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductBOnCategoryM"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductCOnCategoryM"/> + + <!-- Category N contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="onCategoryN"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductCOnCategoryN"/> + + <!-- Open Products A, B, C to edit. Assign/unassign categories to/from them. Save changes --> + <!-- Assign category K to Product A --> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignCategoryK"> + <argument name="productId" value="$$productA.id$$"/> + <argument name="categoryName" value="$$categoryK.name$$"/> + </actionGroup> + + <!-- Unassign category M from Product B --> + <amOnPage url="{{AdminProductEditPage.url($$productB.id$$)}}" stepKey="amOnEditCategoryPageB"/> + <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryM"> + <argument name="categoryName" value="$$categoryM.name$$"/> + </actionGroup> + + <!-- Assign category L to Product C --> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignCategoryNAndM"> + <argument name="productId" value="$$productC.id$$"/> + <argument name="categoryName" value="$$categoryL.name$$"/> + </actionGroup> + + <!-- "One or more indexers are invalid. Make sure your Magento cron job is running." global warning message appears --> + <click selector="{{AdminSystemMessagesSection.systemMessagesDropdown}}" stepKey="openMessageSection"/> + <see userInput="One or more indexers are invalid. Make sure your Magento cron job is running." selector="{{AdminMessagesSection.warningMessage}}" stepKey="seeWarningMessage"/> + + <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are not applied yet --> + <!-- Category K contains only Products B & C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryK"/> + <see userInput="$$productB.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductBCategoryK"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductCCategoryK"/> + + <!-- Category L contains no Products --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryL"/> + <see userInput="We can't find products matching the selection." selector="{{StorefrontCategoryMainSection.emptyProductMessage}}" stepKey="seeEmptyMessage"/> + <dontSeeElement selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProduct"/> + + <!-- Category M contains only Products B & C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$/$$categoryM.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryM"/> + <see userInput="$$productB.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductBCategoryM"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductCCategoryM"/> + + <!-- Category N contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryN"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductInCategoryN"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron"/> + <magentoCLI command="cron:run" stepKey="runCronAgain"/> + + <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are applied --> + <!-- Category K contains only Products A, C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$)}}" stepKey="storefrontCategoryK"/> + <see userInput="$$productA.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductAOnCategoryK"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeCategoryKWithProductC"/> + + <!-- Category L contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$)}}" stepKey="storefrontCategoryL"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeCategoryLWithProductC"/> + + <!-- Category M contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$/$$categoryM.custom_attributes[url_key]$$)}}" stepKey="storefrontCategoryM"/> + <waitForPageLoad stepKey="waitForStorefrontCategoryM"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeCategoryMAndProductC"/> + + <!-- Category N contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="storefrontCategoryN"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductCAndCategoryN"/> + + <!-- Open categories K, L, N to edit. Assign/unassign Products to/from them. Save changes --> + + <!-- Remove Product A assignment for category K --> + <amOnPage url="{{AdminProductEditPage.url($$productA.id$$)}}" stepKey="amOnEditProductPageA"/> + <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryK"> + <argument name="categoryName" value="$$categoryK.name$$"/> + </actionGroup> + + <!-- Remove Product C assignment for category L --> + <amOnPage url="{{AdminProductEditPage.url($$productC.id$$)}}" stepKey="amOnEditProductPageC"/> + <actionGroup ref="AdminUnassignCategoryOnProductAndSaveActionGroup" stepKey="unassignCategoryL"> + <argument name="categoryName" value="$$categoryL.name$$"/> + </actionGroup> + + <!-- Add Product B assignment for category N --> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignCategoryN"> + <argument name="productId" value="$$productB.id$$"/> + <argument name="categoryName" value="$$categoryN.name$$"/> + </actionGroup> + + <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are not applied yet --> + <!-- Category K contains only Products A, C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$)}}" stepKey="onStorefrontCategoryK"/> + <see userInput="$$productA.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductAWithCategoryK"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductC"/> + + <!-- Category L contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$)}}" stepKey="onStorefrontCategoryL"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeCategoryLAndProductC"/> + + <!-- Category M contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$/$$categoryM.custom_attributes[url_key]$$)}}" stepKey="onStorefrontCategoryM"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeCategoryMWithProductC"/> + + <!-- Category N contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="onStorefrontCategoryN"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="productCOnCategoryN"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="firstCronRun"/> + <magentoCLI command="cron:run" stepKey="secondCronRun"/> + + <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are applied --> + + <!-- Category K contains only Products B & C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$)}}" stepKey="onFrontendCategoryK"/> + <see userInput="$$productB.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="productBOnCategoryK"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="productCOnCategoryK"/> + + <!-- Category L contains no Products --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$)}}" stepKey="onFrontendCategoryL"/> + <see userInput="We can't find products matching the selection." selector="{{StorefrontCategoryMainSection.emptyProductMessage}}" stepKey="noProductsMessage"/> + <dontSeeElement selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontSeeProductsOnCategoryL"/> + + <!-- Category M contains only Product C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryL.custom_attributes[url_key]$$/$$categoryM.custom_attributes[url_key]$$)}}" stepKey="onFrontendCategoryM"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeCategoryMPageAndProductC"/> + + <!-- Category N contains only Products B and C --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="onFrontendCategoryN"/> + <see userInput="$$productB.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductBAndCategoryN"/> + <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductCCategoryN"/> + </test> +</tests> From 8cbc3743e5b0358bec65f3bc568ae23c39f338c7 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p-bystritsky@users.noreply.github.com> Date: Fri, 13 Sep 2019 13:34:26 +0300 Subject: [PATCH 42/55] magento/magento2#24340: Static test fix. --- app/code/Magento/Captcha/Observer/CheckUserEditObserver.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Captcha/Observer/CheckUserEditObserver.php b/app/code/Magento/Captcha/Observer/CheckUserEditObserver.php index 70ccfeae6b7e..872bbec4ffa5 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserEditObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserEditObserver.php @@ -17,9 +17,6 @@ */ class CheckUserEditObserver implements ObserverInterface { - /** - * Form ID - */ const FORM_ID = 'user_edit'; /** From 922d4b084d0f1595833c6d0e64625e2372f5344b Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 13 Sep 2019 16:14:51 +0300 Subject: [PATCH 43/55] MC-11557: Check importing of configurable products with images present in filesystem --- .../Catalog/Test/Mftf/Data/CategoryData.xml | 5 +- .../Test/Mftf/Data/ImageContentData.xml | 4 +- .../Test/Mftf/Data/ProductAttributeData.xml | 19 +---- .../ProductAttributeMediaGalleryEntryData.xml | 10 +-- .../Mftf/Data/ProductAttributeOptionData.xml | 14 +--- .../Catalog/Test/Mftf/Data/ProductData.xml | 6 +- .../AdminOpenExportIndexPageActionGroup.xml | 15 ---- ...mportConfigurableProductWithImagesTest.xml | 74 +++++++++---------- .../AdminProductFormConfigurationsSection.xml | 4 +- .../export_import_configurable_product_2.csv | 2 - 10 files changed, 43 insertions(+), 110 deletions(-) delete mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminOpenExportIndexPageActionGroup.xml delete mode 100644 dev/tests/acceptance/tests/_data/export_import_configurable_product_2.csv diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml index bc013eedb0f4..04b818076a47 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -118,10 +118,7 @@ <data key="include_in_menu">true</data> </entity> <!-- Category from file "export_import_configurable_product.csv" --> - <entity name="CategoryExportImport" type="category"> + <entity name="CategoryExportImport" extends="SimpleSubCategory" type="category"> <data key="name">CategoryExportImport</data> - <data key="name_lwr">categoryExportImport</data> - <data key="is_active">true</data> - <data key="include_in_menu">true</data> </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml index ac86deb671ed..6e40499d0efe 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ImageContentData.xml @@ -18,9 +18,7 @@ <data key="type">image/png</data> <data key="name" unique="prefix">magento-logo.png</data> </entity> - <entity name="MagentoLogoImageContentExportImport" type="ImageContent"> - <data key="base64_encoded_data">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</data> - <data key="type">image/png</data> + <entity name="MagentoLogoImageContentExportImport" extends="MagentoLogoImageContent" type="ImageContent"> <data key="name">magento-logo.png</data> </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index 0bdc3a261053..1986821f899c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -377,25 +377,8 @@ <data key="attribute_code" unique="suffix">size_attr</data> </entity> <!-- Product attribute from file "export_import_configurable_product.csv" --> - <entity name="productAttributeWithTwoOptionsForExportImport" type="ProductAttribute"> + <entity name="ProductAttributeWithTwoOptionsForExportImport" extends="productAttributeDropdownTwoOptions" type="ProductAttribute"> <data key="attribute_code">attribute</data> - <data key="frontend_input">select</data> - <data key="scope">global</data> - <data key="is_required">false</data> - <data key="is_unique">false</data> - <data key="is_searchable">true</data> - <data key="is_visible">true</data> - <data key="is_visible_in_advanced_search">true</data> - <data key="is_visible_on_front">true</data> - <data key="is_filterable">true</data> - <data key="is_filterable_in_search">true</data> - <data key="used_in_product_listing">true</data> - <data key="is_used_for_promo_rules">true</data> - <data key="is_comparable">true</data> - <data key="is_used_in_grid">true</data> - <data key="is_visible_in_grid">true</data> - <data key="is_filterable_in_grid">true</data> - <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabelForExportImport</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml index 7d7f2cb7bf4b..75b4ef773a93 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeMediaGalleryEntryData.xml @@ -31,16 +31,8 @@ <requiredEntity type="ImageContent">MagentoLogoImageContent</requiredEntity> </entity> <!-- From file "export_import_configurable_product.csv" --> - <entity name="ApiProductAttributeMediaGalleryForExportImport" type="ProductAttributeMediaGalleryEntry"> - <data key="media_type">image</data> + <entity name="ApiProductAttributeMediaGalleryForExportImport" extends="ApiProductAttributeMediaGalleryEntryTestImage" type="ProductAttributeMediaGalleryEntry"> <data key="label">Magento Logo</data> - <data key="position">1</data> - <array key="types"> - <item>image</item> - <item>small_image</item> - <item>thumbnail</item> - </array> - <data key="disabled">false</data> <requiredEntity type="ImageContent">MagentoLogoImageContentExportImport</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index 8daa63f08d32..bb0e85bcbb40 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -87,20 +87,10 @@ <data key="value" unique="suffix">white</data> </entity> <!-- Product attribute options from file "export_import_configurable_product.csv" --> - <entity name="productAttributeOptionOneForExportImport" type="ProductAttributeOption"> - <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <entity name="ProductAttributeOptionOneForExportImport" extends="productAttributeOption1" type="ProductAttributeOption"> <data key="label">option1</data> - <data key="is_default">false</data> - <data key="sort_order">0</data> - <requiredEntity type="StoreLabel">Option1Store0</requiredEntity> - <requiredEntity type="StoreLabel">Option1Store1</requiredEntity> </entity> - <entity name="productAttributeOptionTwoForExportImport" type="ProductAttributeOption"> - <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <entity name="ProductAttributeOptionTwoForExportImport" extends="productAttributeOption2" type="ProductAttributeOption"> <data key="label">option2</data> - <data key="is_default">true</data> - <data key="sort_order">1</data> - <requiredEntity type="StoreLabel">Option2Store0</requiredEntity> - <requiredEntity type="StoreLabel">Option2Store1</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index faad9094b8d7..25e3e39b20d9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -351,14 +351,10 @@ <data key="filename">adobe-base</data> <data key="file_extension">jpg</data> </entity> - <entity name="TestImage" type="image"> + <entity name="TestImage" extends="TestImageAdobe" type="image"> <data key="title" unique="suffix">test_image</data> - <data key="price">1.00</data> - <data key="file_type">Upload File</data> - <data key="shareable">Yes</data> <data key="file">test_image.jpg</data> <data key="filename">test_image</data> - <data key="file_extension">jpg</data> </entity> <entity name="ProductWithUnicode" type="product"> <data key="sku" unique="suffix">霁产品</data> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminOpenExportIndexPageActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminOpenExportIndexPageActionGroup.xml deleted file mode 100644 index 83762d347a93..000000000000 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminOpenExportIndexPageActionGroup.xml +++ /dev/null @@ -1,15 +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="AdminOpenExportIndexPageActionGroup"> - <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> - <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml index 7548c0991289..0124ff5a16dd 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml @@ -16,7 +16,7 @@ <description value="Check importing of configurable products with images present in filesystem"/> <severity value="CRITICAL"/> <testCaseId value="MC-11557"/> - <group value="ConfigurableProduct"/> + <group value="configurableproduct"/> </annotations> <before> <!-- Create sample data: @@ -27,10 +27,10 @@ <!-- 2. Create Downloadable product --> <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink"> + <createData entity="ApiDownloadableLink" stepKey="addFirstDownloadableLink"> <requiredEntity createDataKey="createDownloadableProduct"/> </createData> - <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <createData entity="ApiDownloadableLink" stepKey="addSecondDownloadableLink"> <requiredEntity createDataKey="createDownloadableProduct"/> </createData> @@ -43,54 +43,54 @@ <!-- 4. Create configurable product with images --> <createData entity="CategoryExportImport" stepKey="createExportImportCategory"/> - <createData entity="ApiConfigurableExportImportProduct" stepKey="createConfigProduct"> + <createData entity="ApiConfigurableExportImportProduct" stepKey="createExportImportConfigurableProduct"> <requiredEntity createDataKey="createExportImportCategory"/> </createData> <createData entity="ApiProductAttributeMediaGalleryForExportImport" stepKey="createConfigurableProductWithImage"> - <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createExportImportConfigurableProduct"/> </createData> - <createData entity="productAttributeWithTwoOptionsForExportImport" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOptionOneForExportImport" stepKey="createConfigProductAttributeFirstOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <createData entity="ProductAttributeWithTwoOptionsForExportImport" stepKey="createExportImportConfigurableProductAttribute"/> + <createData entity="ProductAttributeOptionOneForExportImport" stepKey="createExportImportConfigurableProductAttributeFirstOption"> + <requiredEntity createDataKey="createExportImportConfigurableProductAttribute"/> </createData> - <createData entity="productAttributeOptionTwoForExportImport" stepKey="createConfigProductAttributeSecondOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <createData entity="ProductAttributeOptionTwoForExportImport" stepKey="createExportImportConfigurableProductAttributeSecondOption"> + <requiredEntity createDataKey="createExportImportConfigurableProductAttribute"/> </createData> <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createExportImportConfigurableProductAttribute"/> </createData> <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createExportImportConfigurableProductAttribute"/> </getData> <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createExportImportConfigurableProductAttribute"/> </getData> <createData entity="ApiSimpleOneExportImport" stepKey="createConfigFirstChildProduct"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createExportImportConfigurableProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeFirstOption"/> </createData> - <createData entity="ApiProductAttributeMediaGalleryForExportImport" stepKey="createSimpleProduct1Image"> + <createData entity="ApiProductAttributeMediaGalleryForExportImport" stepKey="addImageForFirstSimpleProduct"> <requiredEntity createDataKey="createConfigFirstChildProduct"/> </createData> <createData entity="ApiSimpleTwoExportImport" stepKey="createConfigSecondChildProduct"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createExportImportConfigurableProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeSecondOption"/> </createData> - <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image1"> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="addImageForSecondSimpleProduct"> <requiredEntity createDataKey="createConfigSecondChildProduct"/> </createData> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductTwoOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <createData entity="ConfigurableProductTwoOptions" stepKey="createExportImportConfigurableProductTwoOption"> + <requiredEntity createDataKey="createExportImportConfigurableProduct"/> + <requiredEntity createDataKey="createExportImportConfigurableProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeFirstOption"/> <requiredEntity createDataKey="getConfigAttributeSecondOption"/> </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> + <createData entity="ConfigurableProductAddChild" stepKey="addFirstExportImportConfigurableProductChild"> + <requiredEntity createDataKey="createExportImportConfigurableProduct"/> <requiredEntity createDataKey="createConfigFirstChildProduct"/> </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> + <createData entity="ConfigurableProductAddChild" stepKey="addSecondExportImportConfigurableProductChild"> + <requiredEntity createDataKey="createExportImportConfigurableProduct"/> <requiredEntity createDataKey="createConfigSecondChildProduct"/> </createData> @@ -118,7 +118,7 @@ <requiredEntity createDataKey="createConfigProductAttr"/> <requiredEntity createDataKey="getConfigAttributeOption"/> </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild"> + <createData entity="ConfigurableProductAddChild" stepKey="addConfigurableProductChild"> <requiredEntity createDataKey="createConfigurableProduct"/> <requiredEntity createDataKey="createConfigChildProduct"/> </createData> @@ -133,10 +133,10 @@ <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createExportImportConfigurableProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteConfigFirstChildProduct"/> <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteConfigSecondChildProduct"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createExportImportConfigurableProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createConfigurableProduct" stepKey="deleteConfigurableProduct"/> <deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/> <deleteData createDataKey="createConfigProductAttr" stepKey="deleteConfigProductAttr"/> @@ -144,24 +144,18 @@ <deleteData createDataKey="createExportImportCategory" stepKey="deleteExportImportCategory"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPage"/> <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> <!-- Admin logout--> <actionGroup ref="logout" stepKey="adminLogout"/> </after> - <!-- Go to Catalog > Products and choose one of configurable products: Products page is open --> - <actionGroup ref="filterAndSelectProduct" stepKey="openCreatedConfigurableProduct"> - <argument name="productSku" value="$$createConfigProduct.sku$$"/> - </actionGroup> - <!-- Go to System > Export --> - <actionGroup ref="AdminOpenExportIndexPageActionGroup" stepKey="goToExportPage"/> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> <!-- Set Export Settings: Entity Type > Products, SKU > ConfProd's sku and press "Continue" --> <actionGroup ref="exportProductsFilterByAttribute" stepKey="exportProductBySku"> <argument name="attribute" value="sku"/> - <argument name="attributeData" value="$$createConfigProduct.sku$$"/> + <argument name="attributeData" value="$$createExportImportConfigurableProduct.sku$$"/> </actionGroup> <!-- Run cron twice --> @@ -175,7 +169,7 @@ <!-- Go to Catalog > Products. Find ConfProd and delete it --> <actionGroup ref="deleteProductBySku" stepKey="deleteConfigurableProductBySku"> - <argument name="sku" value="$$createConfigProduct.sku$$"/> + <argument name="sku" value="$$createExportImportConfigurableProduct.sku$$"/> </actionGroup> <!-- Go to System > Import. Set import settings: Entity Type > Product, Import Behavior > Add/Update, @@ -188,7 +182,7 @@ <!-- Go to Catalog > Products: Configurable product exists --> <actionGroup ref="filterAndSelectProduct" stepKey="openConfigurableProduct"> - <argument name="productSku" value="$$createConfigProduct.sku$$"/> + <argument name="productSku" value="$$createExportImportConfigurableProduct.sku$$"/> </actionGroup> <!-- Go to "Configurations" section: configurations exist and have images --> @@ -199,8 +193,8 @@ <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="$$createConfigSecondChildProduct.sku$$" stepKey="seeSecondProductSkuInField"/> <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="$$createConfigFirstChildProduct.price$$" stepKey="seeFirstProductPriceInField"/> <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="$$createConfigSecondChildProduct.price$$" stepKey="seeSecondProductPriceInField"/> - <seeElement selector="{{AdminProductFormConfigurationsSection.imageSource(MagentoLogo.fileName)}}" stepKey="seeFirstProductImageInField"/> - <seeElement selector="{{AdminProductFormConfigurationsSection.imageSource(TestImage.fileName)}}" stepKey="seeSecondProductImageInField"/> + <seeElement selector="{{AdminProductFormConfigurationsSection.variationImageSource(MagentoLogo.fileName)}}" stepKey="seeFirstProductImageInField"/> + <seeElement selector="{{AdminProductFormConfigurationsSection.variationImageSource(TestImage.fileName)}}" stepKey="seeSecondProductImageInField"/> <!-- Go to "Images and Videos" section: assert image --> <scrollTo selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" stepKey="scrollToProductGalleryTab"/> @@ -209,7 +203,7 @@ </actionGroup> <!-- Go to any ConfProd's configuration page: Product page open successfully --> - <click selector="{{AdminProductFormConfigurationsSection.productLinkByName($$createConfigFirstChildProduct.name$$)}}" stepKey="clickOnFirstProductLink"/> + <click selector="{{AdminProductFormConfigurationsSection.variationProductLinkByName($$createConfigFirstChildProduct.name$$)}}" stepKey="clickOnFirstProductLink"/> <switchToNextTab stepKey="switchToConfigChildProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> <!-- Go to "Images and Videos" section: assert image --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml index 58101e8173e5..64b145d44e0e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -39,8 +39,8 @@ <element name="variationLabel" type="text" selector="//div[@data-index='configurable-matrix']/label"/> <element name="stepsWizardTitle" type="text" selector="div.content:not([style='display: none;']) .steps-wizard-title"/> <element name="attributeEntityByName" type="text" selector="//div[@class='attribute-entity']//div[normalize-space(.)='{{attributeLabel}}']" parameterized="true"/> - <element name="imageSource" type="text" selector=".admin__control-fields[data-index='thumbnail_image_container'] img[src*='{{imageName}}']" parameterized="true"/> - <element name="productLinkByName" type="text" selector="//a[contains(text(), '{{productName}}')]" parameterized="true"/> + <element name="variationImageSource" type="text" selector="[data-index='configurable-matrix'] [data-index='thumbnail_image_container'] img[src*='{{imageName}}']" parameterized="true"/> + <element name="variationProductLinkByName" type="text" selector="//div[@data-index='configurable-matrix']//*[@data-index='name_container']//a[contains(text(), '{{productName}}')]" parameterized="true"/> </section> <section name="AdminConfigurableProductFormSection"> <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> diff --git a/dev/tests/acceptance/tests/_data/export_import_configurable_product_2.csv b/dev/tests/acceptance/tests/_data/export_import_configurable_product_2.csv deleted file mode 100644 index fc27af79fc16..000000000000 --- a/dev/tests/acceptance/tests/_data/export_import_configurable_product_2.csv +++ /dev/null @@ -1,2 +0,0 @@ -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_label,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,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,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,bundle_shipment_type,associated_skus,configurable_variations,configurable_variation_labels -api-configurable-export-import-product,,Default,configurable,Default Category/CategoryExportImport,base,API Configurable Export Import Product,,,2,1,Taxable Goods,"Catalog, Search",123,,,,api-configurable-export-import-product,,,,/m/a/magento-logo.png,Magento Logo,/m/a/magento-logo.png,Magento Logo,/m/a/magento-logo.png,Magento Logo,,,"7/26/19, 8:21 AM","7/26/19, 8:21 AM",,,Block after Info Column,,,,,,,,,,,Use config,,,0,0,1,0,0,1,1,1,0,1,1,,1,0,1,1,0,1,0,0,0,,,,,,,,,,,,,,,,,,"sku=api-simple-one-export-import,attribute=option1|sku=api-simple-two-export-import,attribute=option2",attribute=attributeExportImport From 2625477ab87e7523e895fecf47d63c5b0491e27c Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 13 Sep 2019 16:46:53 +0300 Subject: [PATCH 44/55] MC-11557: Check importing of configurable products with images present in filesystem --- .../Test/Mftf/Data/ConfigurableProductData.xml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml index 318791ae9453..3f21c98068d8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml @@ -79,18 +79,9 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <!-- Configurable product from file "export_import_configurable_product.csv"--> - <entity name="ApiConfigurableExportImportProduct" type="product"> + <entity name="ApiConfigurableExportImportProduct" extends="ApiConfigurableProduct" type="product"> <data key="sku">api-configurable-export-import-product</data> - <data key="type_id">configurable</data> - <data key="attribute_set_id">4</data> - <data key="visibility">4</data> <data key="name">API Configurable Export Import Product</data> <data key="urlKey">api-configurable-export-import-product</data> - <data key="price">123.00</data> - <data key="weight">2</data> - <data key="status">1</data> - <data key="quantity">100</data> - <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> - <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> </entities> From d807552e8fd96bacb41e8a56d70ea07b01bcd4c9 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 13 Sep 2019 16:55:14 +0300 Subject: [PATCH 45/55] MC-11329: Storefront Navigation Menu UI, desktop --- .../Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml | 7 ++++--- .../ActionGroup/AdminChangeMagentoThemeActionGroup.xml | 7 ++++--- .../Theme/Test/Mftf/Section/AdminDesignConfigSection.xml | 1 + .../Test/Mftf/Section/StorefrontNavigationMenuSection.xml | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml index 4df0e151aeab..ae54b72a5a70 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml @@ -16,6 +16,7 @@ <testCaseId value="MC-11329"/> <severity value="CRITICAL"/> <group value="catalog"/> + <group value="theme"/> </annotations> <before> <!-- Login as admin --> @@ -24,7 +25,7 @@ </before> <after> <actionGroup ref="DeleteDefaultCategoryChildren" stepKey="deleteRootCategoryChildren"/> - <actionGroup ref="AdminChangeMagentoThemeActionGroup" stepKey="changeThemeToDefault"> + <actionGroup ref="AdminChangeStorefrontThemeActionGroup" stepKey="changeThemeToDefault"> <argument name="theme" value="{{MagentoLumaTheme.name}}"/> </actionGroup> <!-- Admin log out --> @@ -32,7 +33,7 @@ </after> <!-- Go to Content > Themes. Change theme to Blank --> - <actionGroup ref="AdminChangeMagentoThemeActionGroup" stepKey="changeThemeToBlank"> + <actionGroup ref="AdminChangeStorefrontThemeActionGroup" stepKey="changeThemeToBlank"> <argument name="theme" value="{{MagentoBlankTheme.name}}"/> </actionGroup> @@ -194,7 +195,7 @@ <deleteData createDataKey="createCategoryWithoutChildrenBlank" stepKey="deleteCategoryWithoutChildrenBlank"/> <!-- Go to Content > Themes. Change theme to Luma --> - <actionGroup ref="AdminChangeMagentoThemeActionGroup" stepKey="changeThemeToLuma"> + <actionGroup ref="AdminChangeStorefrontThemeActionGroup" stepKey="changeThemeToLuma"> <argument name="theme" value="{{MagentoLumaTheme.name}}"/> </actionGroup> diff --git a/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml b/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml index 0b093a1e0e8d..0620b9b73ba9 100644 --- a/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml +++ b/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml @@ -8,13 +8,14 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminChangeMagentoThemeActionGroup"> + <actionGroup name="AdminChangeStorefrontThemeActionGroup"> <arguments> <argument name="theme" type="string"/> + <argument name="scopeColumn" type="string" defaultValue="Store View"/> + <argument name="scopeName" type="string" defaultValue="{{_defaultStore.name}}"/> </arguments> <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage"/> - <click selector="{{AdminDesignConfigSection.scopeRow('3')}}" stepKey="editStoreView"/> - <waitForPageLoad stepKey="waitForOpen"/> + <click selector="{{AdminDesignConfigSection.scopeEditLinkByName(scopeColumn, scopeName)}}" stepKey="editScopeConfig"/> <selectOption selector="{{AdminDesignConfigSection.appliedTheme}}" userInput="{{theme}}" stepKey="selectTheme"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index 8004d6d6c344..069068163cca 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -32,5 +32,6 @@ <element name="checkIfStoresArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbi9zdG9yZXM-' and contains(@class,'jstree-closed')]" /> <element name="storeLink" type="button" selector="#ZmF2aWNvbi9zdG9yZXMvMQ-- > a"/> <element name="appliedTheme" type="select" selector="select[name='theme_theme_id']"/> + <element name="scopeEditLinkByName" type="button" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[normalize-space(.)= '{{scope}}']/preceding-sibling::th)+1][contains(.,'{{scopeName}}')]/..//a[contains(@class, 'action-menu-item')]" timeout="30" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml index fd4b0ce45363..5741b50f877f 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/StorefrontNavigationMenuSection.xml @@ -11,7 +11,7 @@ <section name="StorefrontNavigationMenuSection"> <element name="navigationMenu" type="block" selector=".section-items.nav-sections-items li"/> <element name="subItemLevelHover" type="text" selector=".{{level}} .submenu a:hover" parameterized="true"/> - <element name="itemByNameAndLevel" type="text" selector="//a[span[contains(., '{{categoryName}}')]]/following-sibling::ul[contains(@class,'{{categoryLevel}}')]" parameterized="true"/> + <element name="itemByNameAndLevel" type="text" selector="//a[span[contains(., '{{itemName}}')]]/following-sibling::ul[contains(@class,'{{itemLevel}}')]" parameterized="true"/> <element name="subItemByLevel" type="text" selector="li.{{itemLevel}}.parent ul.{{itemLevel}}" parameterized="true"/> <element name="itemActiveState" type="text" selector=".navigation .level0.active>.level-top"/> <element name="subItemActiveState" type="text" selector=".navigation .level0 .submenu .active>a"/> From 36e0e27341125fc6a9832a8e4be6e3ae9c28df43 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 13 Sep 2019 17:14:43 +0300 Subject: [PATCH 46/55] MC-11329: Storefront Navigation Menu UI, desktop --- ...eActionGroup.xml => AdminChangeStorefrontThemeActionGroup.xml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/code/Magento/Theme/Test/Mftf/ActionGroup/{AdminChangeMagentoThemeActionGroup.xml => AdminChangeStorefrontThemeActionGroup.xml} (100%) diff --git a/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml b/app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeStorefrontThemeActionGroup.xml similarity index 100% rename from app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeMagentoThemeActionGroup.xml rename to app/code/Magento/Theme/Test/Mftf/ActionGroup/AdminChangeStorefrontThemeActionGroup.xml From d3498b82ed3f6362508396cd81936f2ee67240d3 Mon Sep 17 00:00:00 2001 From: Yevhen Miroshnychenko <ymiroshnychenko@magento.com> Date: Fri, 13 Sep 2019 16:14:09 -0500 Subject: [PATCH 47/55] MC-19421: Reduce q-ty of Reports Created in /app/*/var/report --- .../Magento/Framework/Error/ProcessorTest.php | 8 +- .../Framework/App/ExceptionHandler.php | 12 +-- lib/internal/Magento/Framework/App/Http.php | 5 +- .../App/Test/Unit/ExceptionHandlerTest.php | 6 +- .../Framework/App/Test/Unit/HttpTest.php | 85 ++++++++++++------- pub/errors/processor.php | 2 +- 6 files changed, 68 insertions(+), 50 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Error/ProcessorTest.php b/dev/tests/integration/testsuite/Magento/Framework/Error/ProcessorTest.php index 51441e487953..0ed7bd4440be 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Error/ProcessorTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Error/ProcessorTest.php @@ -19,7 +19,7 @@ class ProcessorTest extends \PHPUnit\Framework\TestCase /** * @inheritdoc */ - public function setUp() + protected function setUp() { $this->processor = $this->createProcessor(); } @@ -28,7 +28,7 @@ public function setUp() * {@inheritdoc} * @throws \Exception */ - public function tearDown() + protected function tearDown() { $reportDir = $this->processor->_reportDir; $this->removeDirRecursively($reportDir); @@ -45,7 +45,7 @@ public function testSaveAndLoadReport( int $logReportDirNestingLevelChanged, string $exceptionMessage ) { - $_ENV['MAGE_LOG_REPORT_DIR_NESTING_LEVEL'] = $logReportDirNestingLevel; + $_ENV['MAGE_ERROR_REPORT_DIR_NESTING_LEVEL'] = $logReportDirNestingLevel; $reportData = [ 0 => $exceptionMessage, 1 => 'exceptionTrace', @@ -60,7 +60,7 @@ public function testSaveAndLoadReport( $this->fail("Failed to generate report id"); } $this->assertEquals($expectedReportData, $processor->reportData); - $_ENV['MAGE_LOG_REPORT_DIR_NESTING_LEVEL'] = $logReportDirNestingLevelChanged; + $_ENV['MAGE_ERROR_REPORT_DIR_NESTING_LEVEL'] = $logReportDirNestingLevelChanged; $processor = $this->createProcessor(); $processor->loadReport($reportId); $this->assertEquals($expectedReportData, $processor->reportData, "File contents of report don't match"); diff --git a/lib/internal/Magento/Framework/App/ExceptionHandler.php b/lib/internal/Magento/Framework/App/ExceptionHandler.php index 38e85326ef5e..2bec055808ac 100644 --- a/lib/internal/Magento/Framework/App/ExceptionHandler.php +++ b/lib/internal/Magento/Framework/App/ExceptionHandler.php @@ -25,7 +25,7 @@ class ExceptionHandler implements ExceptionHandlerInterface /** * @var Filesystem */ - protected $_filesystem; + private $filesystem; /** * @var LoggerInterface @@ -48,7 +48,7 @@ public function __construct( LoggerInterface $logger ) { $this->encryptor = $encryptor; - $this->_filesystem = $filesystem; + $this->filesystem = $filesystem; $this->logger = $logger; } @@ -166,7 +166,7 @@ private function handleBootstrapErrors( $bootstrapCode = $bootstrap->getErrorCode(); if (Bootstrap::ERR_MAINTENANCE == $bootstrapCode) { // phpcs:ignore Magento2.Security.IncludeFile - require $this->_filesystem + require $this->filesystem ->getDirectoryRead(DirectoryList::PUB) ->getAbsolutePath('errors/503.php'); return true; @@ -214,7 +214,7 @@ private function handleInitException(\Exception $exception): bool if ($exception instanceof InitException) { $this->logger->critical($exception); // phpcs:ignore Magento2.Security.IncludeFile - require $this->_filesystem + require $this->filesystem ->getDirectoryRead(DirectoryList::PUB) ->getAbsolutePath('errors/404.php'); return true; @@ -250,7 +250,7 @@ private function handleGenericReport(Bootstrap $bootstrap, \Exception $exception $reportData['report_id'] = $this->encryptor->getHash(implode('', $reportData)); $this->logger->critical($exception, ['report_id' => $reportData['report_id']]); // phpcs:ignore Magento2.Security.IncludeFile - require $this->_filesystem + require $this->filesystem ->getDirectoryRead(DirectoryList::PUB) ->getAbsolutePath('errors/report.php'); return true; @@ -268,7 +268,7 @@ private function handleGenericReport(Bootstrap $bootstrap, \Exception $exception private function redirectToSetup(Bootstrap $bootstrap, \Exception $exception, ResponseHttp $response) { $setupInfo = new SetupInfo($bootstrap->getParams()); - $projectRoot = $this->_filesystem->getDirectoryRead(DirectoryList::ROOT)->getAbsolutePath(); + $projectRoot = $this->filesystem->getDirectoryRead(DirectoryList::ROOT)->getAbsolutePath(); if ($setupInfo->isAvailable()) { $response->setRedirect($setupInfo->getUrl()); $response->sendHeaders(); diff --git a/lib/internal/Magento/Framework/App/Http.php b/lib/internal/Magento/Framework/App/Http.php index e69a858ee5a5..d54dda9e9f16 100644 --- a/lib/internal/Magento/Framework/App/Http.php +++ b/lib/internal/Magento/Framework/App/Http.php @@ -9,7 +9,6 @@ use Magento\Framework\App\Response\Http as ResponseHttp; use Magento\Framework\App\Response\HttpInterface; use Magento\Framework\Controller\ResultInterface; -use Magento\Framework\Event; use Magento\Framework\ObjectManager\ConfigLoaderInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\ObjectManagerInterface; @@ -70,7 +69,7 @@ class Http implements \Magento\Framework\AppInterface /** * @param ObjectManagerInterface $objectManager - * @param Event\Manager $eventManager + * @param Manager $eventManager * @param AreaList $areaList * @param RequestHttp $request * @param ResponseHttp $response @@ -81,7 +80,7 @@ class Http implements \Magento\Framework\AppInterface */ public function __construct( ObjectManagerInterface $objectManager, - Event\Manager $eventManager, + Manager $eventManager, AreaList $areaList, RequestHttp $request, ResponseHttp $response, diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ExceptionHandlerTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ExceptionHandlerTest.php index 7ade77ee40df..bce2fd811314 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/ExceptionHandlerTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/ExceptionHandlerTest.php @@ -49,17 +49,17 @@ class ExceptionHandlerTest extends \PHPUnit\Framework\TestCase /** * @var LoggerInterface|MockObject */ - protected $loggerMock; + private $loggerMock; /** * @var ResponseHttp|MockObject */ - protected $responseMock; + private $responseMock; /** * @var RequestHttp|MockObject */ - protected $requestMock; + private $requestMock; protected function setUp() { diff --git a/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php index 6b5f11b21b9b..5d8e5960e579 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/HttpTest.php @@ -5,81 +5,100 @@ */ namespace Magento\Framework\App\Test\Unit; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as HelperObjectManager; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Framework\App\Request\Http as RequestHttp; +use Magento\Framework\App\Response\Http as ResponseHttp; +use Magento\Framework\App\Http as AppHttp; +use Magento\Framework\App\FrontControllerInterface; +use Magento\Framework\Event\Manager; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\App\AreaList; +use Magento\Framework\App\ObjectManager\ConfigLoader; +use Magento\Framework\App\ExceptionHandlerInterface; +use Magento\Framework\Stdlib\Cookie\CookieReaderInterface; +use Magento\Framework\App\Route\ConfigInterface\Proxy; +use Magento\Framework\App\Request\PathInfoProcessorInterface; +use Magento\Framework\Stdlib\StringUtils; + /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class HttpTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var HelperObjectManager */ - protected $objectManager; + private $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ResponseHttp|MockObject */ - protected $responseMock; + private $responseMock; /** - * @var \Magento\Framework\App\Http + * @var AppHttp */ - protected $http; + private $http; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var FrontControllerInterface|MockObject */ - protected $frontControllerMock; + private $frontControllerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ - protected $eventManagerMock; + private $eventManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var RequestHttp|MockObject */ - protected $requestMock; + private $requestMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ObjectManagerInterface|MockObject */ - protected $objectManagerMock; + private $objectManagerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var AreaList|MockObject */ - protected $areaListMock; + private $areaListMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ConfigLoader|MockObject */ - protected $configLoaderMock; + private $configLoaderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ExceptionHandlerInterface|MockObject */ - protected $exceptionHandlerMock; + private $exceptionHandlerMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $cookieReaderMock = $this->getMockBuilder(\Magento\Framework\Stdlib\Cookie\CookieReaderInterface::class) + $this->objectManager = new HelperObjectManager($this); + $cookieReaderMock = $this->getMockBuilder(CookieReaderInterface::class) ->disableOriginalConstructor() ->getMock(); - $routeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Route\ConfigInterface\Proxy::class) + $routeConfigMock = $this->getMockBuilder(Proxy::class) ->disableOriginalConstructor() ->getMock(); - $pathInfoProcessorMock = $this->getMockBuilder(\Magento\Framework\App\Request\PathInfoProcessorInterface::class) + $pathInfoProcessorMock = $this->getMockBuilder(PathInfoProcessorInterface::class) ->disableOriginalConstructor() ->getMock(); - $converterMock = $this->getMockBuilder(\Magento\Framework\Stdlib\StringUtils::class) + $converterMock = $this->getMockBuilder(StringUtils::class) ->disableOriginalConstructor() ->setMethods(['cleanString']) ->getMock(); - $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) + $objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $this->requestMock = $this->getMockBuilder(RequestHttp::class) ->setConstructorArgs( [ 'cookieReader' => $cookieReaderMock, @@ -91,7 +110,7 @@ protected function setUp() ) ->setMethods(['getFrontName', 'isHead']) ->getMock(); - $this->areaListMock = $this->getMockBuilder(\Magento\Framework\App\AreaList::class) + $this->areaListMock = $this->getMockBuilder(AreaList::class) ->disableOriginalConstructor() ->setMethods(['getCodeByFrontName']) ->getMock(); @@ -99,20 +118,20 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['load']) ->getMock(); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); + $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class); + $this->responseMock = $this->createMock(ResponseHttp::class); $this->frontControllerMock = $this->getMockBuilder(\Magento\Framework\App\FrontControllerInterface::class) ->disableOriginalConstructor() ->setMethods(['dispatch']) ->getMock(); - $this->eventManagerMock = $this->getMockBuilder(\Magento\Framework\Event\Manager::class) + $this->eventManagerMock = $this->getMockBuilder(Manager::class) ->disableOriginalConstructor() ->setMethods(['dispatch']) ->getMock(); - $this->exceptionHandlerMock = $this->createMock(\Magento\Framework\App\ExceptionHandlerInterface::class); + $this->exceptionHandlerMock = $this->createMock(ExceptionHandlerInterface::class); $this->http = $this->objectManager->getObject( - \Magento\Framework\App\Http::class, + AppHttp::class, [ 'objectManager' => $this->objectManagerMock, 'eventManager' => $this->eventManagerMock, diff --git a/pub/errors/processor.php b/pub/errors/processor.php index 889eed2cad10..7cab4add51a9 100644 --- a/pub/errors/processor.php +++ b/pub/errors/processor.php @@ -612,7 +612,7 @@ private function getReportPath(int $reportDirNestingLevel, string $reportId): st */ private function getReportDirNestingLevel(string $reportId): int { - $envName = 'MAGE_LOG_REPORT_DIR_NESTING_LEVEL'; + $envName = 'MAGE_ERROR_REPORT_DIR_NESTING_LEVEL'; $value = $_ENV[$envName] ?? getenv($envName); if(false === $value && property_exists($this->_config, 'dir_nesting_level')) { $value = $this->_config->dir_nesting_level; From ad82991a825b5c33682451b1e860cfa84ea9529e Mon Sep 17 00:00:00 2001 From: Yevhen Miroshnychenko <ymiroshnychenko@magento.com> Date: Fri, 13 Sep 2019 16:22:34 -0500 Subject: [PATCH 48/55] MC-19421: Reduce q-ty of Reports Created in /app/*/var/report --- pub/errors/local.xml.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pub/errors/local.xml.sample b/pub/errors/local.xml.sample index bdd980d4f8a2..c5c35559bd6d 100644 --- a/pub/errors/local.xml.sample +++ b/pub/errors/local.xml.sample @@ -40,7 +40,7 @@ ... dir_nesting_level=32 -> <magento_root>/var/report/44/ff/b1/08/7a/44/e6/1b/01/8b/3c/de/e7/22/84/d0/17/f2/2e/52/75/5c/24/e5/c8/5c/ba/c1/64/7a/e7/a7/44ffb1087a44e61b018b3cdee72284d017f22e52755c24e5c85cbac1647ae7a7 - If you use an environment variable MAGE_LOG_REPORT_DIR_NESTING_LEVEL, this property will be ignored. + If you use an environment variable MAGE_ERROR_REPORT_DIR_NESTING_LEVEL, this property will be ignored. Environment variable has a higher priority. --> <dir_nesting_level>0</dir_nesting_level> From b0e70c798b4118609f52b4abfa8684d9ddd46f48 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Sat, 14 Sep 2019 07:00:35 +0000 Subject: [PATCH 49/55] MC-19862: [Function Test] MC-148 Magento\FunctionalTestingFramework.functional.ApplyCatalogPriceRuleByProductAttributeTest --- .../Controller/Adminhtml/Promo/Catalog/Save.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 4f58293d5335..996fc4e8ef3d 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -12,6 +12,7 @@ use Magento\Framework\Registry; use Magento\Framework\Stdlib\DateTime\Filter\Date; use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** * Save action for catalog rule @@ -25,19 +26,27 @@ class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog imple */ protected $dataPersistor; + /** + * @var TimezoneInterface + */ + private $localeDate; + /** * @param Context $context * @param Registry $coreRegistry * @param Date $dateFilter * @param DataPersistorInterface $dataPersistor + * @param TimezoneInterface $localeDate */ public function __construct( Context $context, Registry $coreRegistry, Date $dateFilter, - DataPersistorInterface $dataPersistor + DataPersistorInterface $dataPersistor, + TimezoneInterface $localeDate ) { $this->dataPersistor = $dataPersistor; + $this->localeDate = $localeDate; parent::__construct($context, $coreRegistry, $dateFilter); } @@ -66,6 +75,9 @@ public function execute() ); $data = $this->getRequest()->getPostValue(); + if (!$this->getRequest()->getParam('from_date')) { + $data['from_date'] = $this->localeDate->formatDate(); + } $filterValues = ['from_date' => $this->_dateFilter]; if ($this->getRequest()->getParam('to_date')) { $filterValues['to_date'] = $this->_dateFilter; From 6f11d16b9d4444e9ebab83ad56c0876d12e2069f Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Sat, 14 Sep 2019 10:44:56 -0300 Subject: [PATCH 50/55] magento-engcom/magento2ce#3248: Code style fixes --- app/code/Magento/Customer/Model/Customer.php | 4 ++-- lib/web/mage/adminhtml/browser.js | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php index 34f1e5e2da46..1f8f7d90f6d0 100644 --- a/app/code/Magento/Customer/Model/Customer.php +++ b/app/code/Magento/Customer/Model/Customer.php @@ -513,7 +513,7 @@ public function getAddressCollection() /** * Customer addresses collection * - * @return ResourceModel\Address\Collection + * @return \Magento\Customer\Model\ResourceModel\Address\Collection * @throws \Magento\Framework\Exception\LocalizedException */ public function getAddressesCollection() @@ -600,7 +600,7 @@ public function hashPassword($password, $salt = true) * Validate password with salted hash * * @param string $password - * @return bool + * @return boolean * @throws \Exception */ public function validatePassword($password) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 20bcdd36baca..137ad5541a87 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -52,10 +52,11 @@ define([ content = '<div class="popup-window" id="' + windowId + '"></div>', self = this; - if (this.modalLoaded === true - && options - && self.targetElementId - && self.targetElementId === options.targetElementId) { + if (this.modalLoaded === true && + options && + self.targetElementId && + self.targetElementId === options.targetElementId + ) { if (typeof options.closed !== 'undefined') { this.modal.modal('option', 'closed', options.closed); } From 5bb1a40efe2509234d21370c8b91c4899fa74246 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Sat, 14 Sep 2019 22:12:35 +0000 Subject: [PATCH 51/55] MC-19862: [Function Test] MC-148 Magento\FunctionalTestingFramework.functional.ApplyCatalogPriceRuleByProductAttributeTest --- .../CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php | 4 +--- .../Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 996fc4e8ef3d..6d499b93e411 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -55,16 +55,15 @@ public function __construct( * * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { if ($this->getRequest()->getPostValue()) { - /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ $ruleRepository = $this->_objectManager->get( \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class ); - /** @var \Magento\CatalogRule\Model\Rule $model */ $model = $this->_objectManager->create(\Magento\CatalogRule\Model\Rule::class); @@ -74,7 +73,6 @@ public function execute() ['request' => $this->getRequest()] ); $data = $this->getRequest()->getPostValue(); - if (!$this->getRequest()->getParam('from_date')) { $data['from_date'] = $this->localeDate->formatDate(); } diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php index e589c8595ce2..944710773123 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php @@ -101,7 +101,9 @@ public function execute(Rule $rule, $batchCount, $useAdditionalTable = false) $scopeTz = new \DateTimeZone( $this->localeDate->getConfigTimezone(ScopeInterface::SCOPE_WEBSITE, $websiteId) ); - $fromTime = (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp(); + $fromTime = $rule->getFromDate() + ? (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp() + : 0; $toTime = $rule->getToDate() ? (new \DateTime($rule->getToDate(), $scopeTz))->getTimestamp() + IndexBuilder::SECONDS_IN_DAY - 1 : 0; From f54e506d7b4ec31cbda52db89d001a7f98640b38 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 16 Sep 2019 08:15:09 +0300 Subject: [PATCH 52/55] MC-11557: Check importing of configurable products with images present in filesystem --- .../Catalog/Test/Mftf/Data/ProductData.xml | 22 ++----------------- ...mportConfigurableProductWithImagesTest.xml | 2 +- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 25e3e39b20d9..e122615eb8aa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -1171,31 +1171,13 @@ <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> <!-- Products from file "export_import_configurable_product.csv" --> - <entity name="ApiSimpleOneExportImport" type="product2"> + <entity name="ApiSimpleOneExportImport" extends="ApiSimpleOne" type="product2"> <data key="sku">api-simple-one-export-import</data> - <data key="type_id">simple</data> - <data key="attribute_set_id">4</data> - <data key="visibility">4</data> <data key="name">Api Simple Product One Export Import</data> - <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-one-export-import</data> - <data key="status">1</data> - <data key="quantity">100</data> - <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> - <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> - <entity name="ApiSimpleTwoExportImport" type="product2"> + <entity name="ApiSimpleTwoExportImport" extends="ApiSimpleTwo" type="product2"> <data key="sku">api-simple-two-export-import</data> - <data key="type_id">simple</data> - <data key="attribute_set_id">4</data> - <data key="visibility">4</data> <data key="name">Api Simple Product Two Export Import</data> - <data key="price">123.00</data> - <data key="urlKey" unique="suffix">api-simple-two-export-import</data> - <data key="status">1</data> - <data key="quantity">100</data> - <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> - <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> <entity name="SimpleProductPrice10Qty1" type="product"> <data key="sku" unique="suffix">simple-product_</data> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml index 0124ff5a16dd..7c6c36b07ee2 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml @@ -16,7 +16,7 @@ <description value="Check importing of configurable products with images present in filesystem"/> <severity value="CRITICAL"/> <testCaseId value="MC-11557"/> - <group value="configurableproduct"/> + <group value="configurable_product"/> </annotations> <before> <!-- Create sample data: From 9d8128eeb4fe55f3fdcaa1d7b16b75064bbdc11d Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 16 Sep 2019 10:26:45 +0300 Subject: [PATCH 53/55] MC-13104: Cannot upload incorrect file --- .../Catalog/Test/Mftf/Data/ProductData.xml | 5 ++ ...hIncorrectNameThroughCustomOptionsTest.xml | 71 +++++++++++++++++++ ...ithout <script><:script> tags...\")'>.png" | 0 3 files changed, 76 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontVerifyCannotLoadFileWithIncorrectNameThroughCustomOptionsTest.xml create mode 100644 "dev/tests/acceptance/tests/_data/<img src=x onerror='alert(\"XSS without <script><:script> tags...\")'>.png" diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 517ab253b823..9ba1adf31316 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -512,6 +512,11 @@ <requiredEntity type="product_option">ProductOptionArea</requiredEntity> <requiredEntity type="product_option">ProductOptionFile</requiredEntity> </entity> + <entity name="ProductFileOptionWithScriptTag" type="product"> + <var key="sku" entityType="product" entityKey="sku"/> + <data key="file"><img src=x onerror='alert("XSS without <script><:script> tags...")'>.png</data> + <requiredEntity type="product_option">ProductOptionFile</requiredEntity> + </entity> <entity name="ApiVirtualProductWithDescription" type="product"> <data key="sku" unique="suffix">api-virtual-product</data> <data key="type_id">virtual</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontVerifyCannotLoadFileWithIncorrectNameThroughCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontVerifyCannotLoadFileWithIncorrectNameThroughCustomOptionsTest.xml new file mode 100644 index 000000000000..2ea8e8820b1b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontVerifyCannotLoadFileWithIncorrectNameThroughCustomOptionsTest.xml @@ -0,0 +1,71 @@ +<?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="StorefrontVerifyCannotLoadFileWithIncorrectNameThroughCustomOptionsTest"> + <annotations> + <features value="Catalog"/> + <stories value="Custom options"/> + <title value="Verify cannot load file with incorrect name through Custom options"/> + <description value="Verify cannot load file with incorrect name through Custom options"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13104"/> + <group value="catalog"/> + </annotations> + <before> + <!-- Create customer --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!-- Create category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!-- Create simple product --> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Add file upload custom option to the product --> + <updateData createDataKey="createProduct" entity="ProductFileOptionWithScriptTag" stepKey="updateProductWithOption"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + </before> + <after> + <!-- Delete product --> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete customer --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + </after> + + <!-- Login to storefront --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open product page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductPage"> + <argument name="productUrlKey" value="$$createProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <!-- Upload file --> + <actionGroup ref="StorefrontAttachOptionFileActionGroup" stepKey="selectAndAttachFile"> + <argument name="optionTitle" value="ProductOptionFile"/> + <argument name="file" value="ProductFileOptionWithScriptTag.file"/> + </actionGroup> + + <!-- Add product to cart --> + <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCartButton"/> + <waitForPageLoad stepKey="waitForProductAddToCart"/> + + <!-- Assert alert message --> + <waitForElementVisible selector="{{StorefrontProductPageSection.alertMessage}}" stepKey="waitForElementVisible"/> + <see selector="{{StorefrontProductPageSection.alertMessage}}" userInput="The file is empty. Select another file and try again." stepKey="seeAlertMessage"/> + + <!-- Assert cart is empty --> + <actionGroup ref="assertMiniCartEmpty" stepKey="assertMiniCartEmpty"/> + </test> +</tests> diff --git "a/dev/tests/acceptance/tests/_data/<img src=x onerror='alert(\"XSS without <script><:script> tags...\")'>.png" "b/dev/tests/acceptance/tests/_data/<img src=x onerror='alert(\"XSS without <script><:script> tags...\")'>.png" new file mode 100644 index 000000000000..e69de29bb2d1 From 0c36ccde066b4a5e6d643211a9a2e721e8f9d9b7 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 16 Sep 2019 10:50:42 +0300 Subject: [PATCH 54/55] MC-11386: Verify Category Product and Product Category partial reindex --- app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml | 2 +- app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml index 05a6bae9ab0f..b451096f2c3a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -118,6 +118,6 @@ <data key="include_in_menu">true</data> </entity> <entity name="SubCategoryNonAnchor" extends="SubCategoryWithParent"> - <requiredEntity type="custom_attribute">CustomAttributeCategoryIsAnchor</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributeCategoryNonAnchor</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml index 69d8c3c87ae5..7bd392f0aa74 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml @@ -51,7 +51,7 @@ <data key="attribute_code">short_description</data> <data key="value">Short Fixedtest 555</data> </entity> - <entity name="CustomAttributeCategoryIsAnchor" type="custom_attribute"> + <entity name="CustomAttributeCategoryNonAnchor" type="custom_attribute"> <data key="attribute_code">is_anchor</data> <data key="value">0</data> </entity> From afaa6546785f3f2fe8a552a06e5c9bc32a01d276 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 16 Sep 2019 16:07:27 +0300 Subject: [PATCH 55/55] MC-11557: Check importing of configurable products with images present in filesystem --- .../Test/AdminExportImportConfigurableProductWithImagesTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml index 7c6c36b07ee2..88f6e6c9f903 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml @@ -177,7 +177,7 @@ <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProduct"> <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="export_import_configurable_product.csv"/> - <argument name="importMessage" value="Created: 1, Updated: 0, Deleted: 0"/> + <argument name="importNoticeMessage" value="Created: 1, Updated: 0, Deleted: 0"/> </actionGroup> <!-- Go to Catalog > Products: Configurable product exists -->